Mercurial > prosody-modules
view mod_couchdb/couchdb/json.lib.lua @ 5383:df11a2cbc7b7
mod_http_oauth2: Implement RFC 7628 Proof Key for Code Exchange
Likely to become mandatory in OAuth 2.1.
Backwards compatible since the default 'plain' verifier would compare
nil with nil if the relevant parameters are left out.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 29 Apr 2023 13:09:46 +0200 |
parents | 316d7c8e1fb0 |
children |
line wrap: on
line source
local type = type; local t_insert, t_concat, t_remove = table.insert, table.concat, table.remove; local s_char = string.char; local tostring, tonumber = tostring, tonumber; local pairs, ipairs = pairs, ipairs; local next = next; local error = error; local newproxy, getmetatable = newproxy, getmetatable; local print = print; --module("json") local _M = {}; local null = newproxy and newproxy(true) or {}; if getmetatable and getmetatable(null) then getmetatable(null).__tostring = function() return "null"; end; end _M.null = null; local escapes = { ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"}; local unescapes = { ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", b = "\b", f = "\f", n = "\n", r = "\r", t = "\t"}; for i=0,31 do local ch = s_char(i); if not escapes[ch] then escapes[ch] = ("\\u%.4X"):format(i); end end local valid_types = { number = true, string = true, table = true, boolean = true }; local special_keys = { __array = true; __hash = true; }; local simplesave, tablesave, arraysave, stringsave; function stringsave(o, buffer) -- FIXME do proper utf-8 and binary data detection t_insert(buffer, "\""..(o:gsub(".", escapes)).."\""); end function arraysave(o, buffer) t_insert(buffer, "["); if next(o) then for i,v in ipairs(o) do simplesave(v, buffer); t_insert(buffer, ","); end t_remove(buffer); end t_insert(buffer, "]"); end function tablesave(o, buffer) local __array = {}; local __hash = {}; local hash = {}; for i,v in ipairs(o) do __array[i] = v; end for k,v in pairs(o) do local ktype, vtype = type(k), type(v); if valid_types[vtype] or v == null then if ktype == "string" and not special_keys[k] then hash[k] = v; elseif (valid_types[ktype] or k == null) and __array[k] == nil then __hash[k] = v; end end end if next(__hash) ~= nil or next(hash) ~= nil or next(__array) == nil then t_insert(buffer, "{"); local mark = #buffer; for k,v in pairs(hash) do stringsave(k, buffer); t_insert(buffer, ":"); simplesave(v, buffer); t_insert(buffer, ","); end if next(__hash) ~= nil then t_insert(buffer, "\"__hash\":["); for k,v in pairs(__hash) do simplesave(k, buffer); t_insert(buffer, ","); simplesave(v, buffer); t_insert(buffer, ","); end t_remove(buffer); t_insert(buffer, "]"); t_insert(buffer, ","); end if next(__array) then t_insert(buffer, "\"__array\":"); arraysave(__array, buffer); t_insert(buffer, ","); end if mark ~= #buffer then t_remove(buffer); end t_insert(buffer, "}"); else arraysave(__array, buffer); end end function simplesave(o, buffer) local t = type(o); if t == "number" then t_insert(buffer, tostring(o)); elseif t == "string" then stringsave(o, buffer); elseif t == "table" then tablesave(o, buffer); elseif t == "boolean" then t_insert(buffer, (o and "true" or "false")); else t_insert(buffer, "null"); end end function _M.encode(obj) local t = {}; simplesave(obj, t); return t_concat(t); end ----------------------------------- function _M.decode(json) local pos = 1; local current = {}; local stack = {}; local ch, peek; local function next() ch = json:sub(pos, pos); pos = pos+1; peek = json:sub(pos, pos); return ch; end local function skipwhitespace() while ch and (ch == "\r" or ch == "\n" or ch == "\t" or ch == " ") do next(); end end local function skiplinecomment() repeat next(); until not(ch) or ch == "\r" or ch == "\n"; skipwhitespace(); end local function skipstarcomment() next(); next(); -- skip '/', '*' while peek and ch ~= "*" and peek ~= "/" do next(); end if not peek then error("eof in star comment") end next(); next(); -- skip '*', '/' skipwhitespace(); end local function skipstuff() while true do skipwhitespace(); if ch == "/" and peek == "*" then skipstarcomment(); elseif ch == "/" and peek == "*" then skiplinecomment(); else return; end end end local readvalue; local function readarray() local t = {}; next(); -- skip '[' skipstuff(); if ch == "]" then next(); return t; end t_insert(t, readvalue()); while true do skipstuff(); if ch == "]" then next(); return t; end if not ch then error("eof while reading array"); elseif ch == "," then next(); elseif ch then error("unexpected character in array, comma expected"); end if not ch then error("eof while reading array"); end t_insert(t, readvalue()); end end local function checkandskip(c) local x = ch or "eof"; if x ~= c then error("unexpected "..x..", '"..c.."' expected"); end next(); end local function readliteral(lit, val) for c in lit:gmatch(".") do checkandskip(c); end return val; end local function readstring() local s = ""; checkandskip("\""); while ch do while ch and ch ~= "\\" and ch ~= "\"" do s = s..ch; next(); end if ch == "\\" then next(); if unescapes[ch] then s = s..unescapes[ch]; next(); elseif ch == "u" then local seq = ""; for i=1,4 do next(); if not ch then error("unexpected eof in string"); end if not ch:match("[0-9a-fA-F]") then error("invalid unicode escape sequence in string"); end seq = seq..ch; end s = s..s.char(tonumber(seq, 16)); -- FIXME do proper utf-8 next(); else error("invalid escape sequence in string"); end end if ch == "\"" then next(); return s; end end error("eof while reading string"); end local function readnumber() local s = ""; if ch == "-" then s = s..ch; next(); if not ch:match("[0-9]") then error("number format error"); end end if ch == "0" then s = s..ch; next(); if ch:match("[0-9]") then error("number format error"); end else while ch and ch:match("[0-9]") do s = s..ch; next(); end end if ch == "." then s = s..ch; next(); if not ch:match("[0-9]") then error("number format error"); end while ch and ch:match("[0-9]") do s = s..ch; next(); end if ch == "e" or ch == "E" then s = s..ch; next(); if ch == "+" or ch == "-" then s = s..ch; next(); if not ch:match("[0-9]") then error("number format error"); end while ch and ch:match("[0-9]") do s = s..ch; next(); end end end end return tonumber(s); end local function readmember(t) local k = readstring(); checkandskip(":"); t[k] = readvalue(); end local function fixobject(obj) local __array = obj.__array; if __array then obj.__array = nil; for i,v in ipairs(__array) do t_insert(obj, v); end end local __hash = obj.__hash; if __hash then obj.__hash = nil; local k; for i,v in ipairs(__hash) do if k ~= nil then obj[k] = v; k = nil; else k = v; end end end return obj; end local function readobject() local t = {}; next(); -- skip '{' skipstuff(); if ch == "}" then next(); return t; end if not ch then error("eof while reading object"); end readmember(t); while true do skipstuff(); if ch == "}" then next(); return fixobject(t); end if not ch then error("eof while reading object"); elseif ch == "," then next(); elseif ch then error("unexpected character in object, comma expected"); end if not ch then error("eof while reading object"); end readmember(t); end end function readvalue() skipstuff(); while ch do if ch == "{" then return readobject(); elseif ch == "[" then return readarray(); elseif ch == "\"" then return readstring(); elseif ch:match("[%-0-9%.]") then return readnumber(); elseif ch == "n" then return readliteral("null", null); elseif ch == "t" then return readliteral("true", true); elseif ch == "f" then return readliteral("false", false); end end error("eof while reading value"); end next(); return readvalue(); end function _M.test(object) local encoded = encode(object); local decoded = decode(encoded); local recoded = encode(decoded); if encoded ~= recoded then print("FAILED"); print("encoded:", encoded); print("recoded:", recoded); else print(encoded); end return encoded ~= recoded; end return _M;