diff options
Diffstat (limited to 'libs/json')
-rw-r--r-- | libs/json/Makefile | 2 | ||||
-rw-r--r-- | libs/json/luasrc/json.lua | 600 |
2 files changed, 0 insertions, 602 deletions
diff --git a/libs/json/Makefile b/libs/json/Makefile deleted file mode 100644 index f7fac7740..000000000 --- a/libs/json/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -include ../../build/config.mk -include ../../build/module.mk diff --git a/libs/json/luasrc/json.lua b/libs/json/luasrc/json.lua deleted file mode 100644 index 8dbaf9183..000000000 --- a/libs/json/luasrc/json.lua +++ /dev/null @@ -1,600 +0,0 @@ ---[[ -LuCI - Lua Configuration Interface - -Copyright 2008 Steven Barth <steven@midlink.org> -Copyright 2008 Jo-Philipp Wich <xm@leipzig.freifunk.net> - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -$Id$ - -Decoder: - Info: - null will be decoded to luci.json.null if first parameter of Decoder() is true - - Example: - decoder = luci.json.Decoder() - luci.ltn12.pump.all(luci.ltn12.source.string("decodableJSON"), decoder:sink()) - luci.util.dumptable(decoder:get()) - - Known issues: - does not support unicode conversion \uXXYY with XX != 00 will be ignored - - -Encoder: - Info: - Accepts numbers, strings, nil, booleans as they are - Accepts luci.json.null as replacement for nil - Accepts full associative and full numerically indexed tables - Mixed tables will loose their associative values during conversion - Iterator functions will be encoded as an array of their return values - Non-iterator functions will probably corrupt the encoder - - Example: - encoder = luci.json.Encoder(encodableData) - luci.ltn12.pump.all(encoder:source(), luci.ltn12.sink.file(io.open("someFile", w))) -]]-- - -local nixio = require "nixio" -local util = require "luci.util" -local table = require "table" -local string = require "string" -local coroutine = require "coroutine" - -local assert = assert -local tonumber = tonumber -local tostring = tostring -local error = error -local type = type -local pairs = pairs -local ipairs = ipairs -local next = next -local pcall = pcall - -local band = nixio.bit.band -local bor = nixio.bit.bor -local rshift = nixio.bit.rshift -local char = string.char - -local getmetatable = getmetatable - ---- LuCI JSON-Library --- @cstyle instance -module "luci.json" - - ---- Directly decode a JSON string --- @param json JSON-String --- @return Lua object -function decode(json, ...) - local a = ActiveDecoder(function() return nil end, ...) - a.chunk = json - local s, obj = pcall(a.get, a) - return s and obj or nil -end - - ---- Direcly encode a Lua object into a JSON string. --- @param obj Lua Object --- @return JSON string -function encode(obj, ...) - local out = {} - local e = Encoder(obj, 1, ...):source() - local chnk, err - repeat - chnk, err = e() - out[#out+1] = chnk - until not chnk - return not err and table.concat(out) or nil -end - - ---- Null replacement function --- @return null -function null() - return null -end - ---- Create a new JSON-Encoder. --- @class function --- @name Encoder --- @param data Lua-Object to be encoded. --- @param buffersize Blocksize of returned data source. --- @param fastescape Use non-standard escaping (don't escape control chars) --- @return JSON-Encoder -Encoder = util.class() - -function Encoder.__init__(self, data, buffersize, fastescape) - self.data = data - self.buffersize = buffersize or 512 - self.buffer = "" - self.fastescape = fastescape - - getmetatable(self).__call = Encoder.source -end - ---- Create an LTN12 source providing the encoded JSON-Data. --- @return LTN12 source -function Encoder.source(self) - local source = coroutine.create(self.dispatch) - return function() - local res, data = coroutine.resume(source, self, self.data, true) - if res then - return data - else - return nil, data - end - end -end - -function Encoder.dispatch(self, data, start) - local parser = self.parsers[type(data)] - - parser(self, data) - - if start then - if #self.buffer > 0 then - coroutine.yield(self.buffer) - end - - coroutine.yield() - end -end - -function Encoder.put(self, chunk) - if self.buffersize < 2 then - coroutine.yield(chunk) - else - if #self.buffer + #chunk > self.buffersize then - local written = 0 - local fbuffer = self.buffersize - #self.buffer - - coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer)) - written = fbuffer - - while #chunk - written > self.buffersize do - fbuffer = written + self.buffersize - coroutine.yield(chunk:sub(written + 1, fbuffer)) - written = fbuffer - end - - self.buffer = chunk:sub(written + 1) - else - self.buffer = self.buffer .. chunk - end - end -end - -function Encoder.parse_nil(self) - self:put("null") -end - -function Encoder.parse_bool(self, obj) - self:put(obj and "true" or "false") -end - -function Encoder.parse_number(self, obj) - self:put(tostring(obj)) -end - -function Encoder.parse_string(self, obj) - if self.fastescape then - self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"') - else - self:put('"' .. - obj:gsub('[%c\\"]', - function(char) - return '\\u00%02x' % char:byte() - end - ) - .. '"') - end -end - -function Encoder.parse_iter(self, obj) - if obj == null then - return self:put("null") - end - - if type(obj) == "table" and (#obj == 0 and next(obj)) then - self:put("{") - local first = true - - for key, entry in pairs(obj) do - first = first or self:put(",") - first = first and false - self:parse_string(tostring(key)) - self:put(":") - self:dispatch(entry) - end - - self:put("}") - else - self:put("[") - local first = true - - if type(obj) == "table" then - for i=1, #obj do - first = first or self:put(",") - first = first and nil - self:dispatch(obj[i]) - end - else - for entry in obj do - first = first or self:put(",") - first = first and nil - self:dispatch(entry) - end - end - - self:put("]") - end -end - -Encoder.parsers = { - ['nil'] = Encoder.parse_nil, - ['table'] = Encoder.parse_iter, - ['number'] = Encoder.parse_number, - ['string'] = Encoder.parse_string, - ['boolean'] = Encoder.parse_bool, - ['function'] = Encoder.parse_iter -} - - ---- Create a new JSON-Decoder. --- @class function --- @name Decoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return JSON-Decoder -Decoder = util.class() - -function Decoder.__init__(self, customnull) - self.cnull = customnull - getmetatable(self).__call = Decoder.sink -end - ---- Create an LTN12 sink from the decoder object which accepts the JSON-Data. --- @return LTN12 sink -function Decoder.sink(self) - local sink = coroutine.create(self.dispatch) - return function(...) - return coroutine.resume(sink, self, ...) - end -end - - ---- Get the decoded data packets after the rawdata has been sent to the sink. --- @return Decoded data -function Decoder.get(self) - return self.data -end - -function Decoder.dispatch(self, chunk, src_err, strict) - local robject, object - local oset = false - - while chunk do - while chunk and #chunk < 1 do - chunk = self:fetch() - end - - assert(not strict or chunk, "Unexpected EOS") - if not chunk then break end - - local char = chunk:sub(1, 1) - local parser = self.parsers[char] - or (char:match("%s") and self.parse_space) - or (char:match("[0-9-]") and self.parse_number) - or error("Unexpected char '%s'" % char) - - chunk, robject = parser(self, chunk) - - if parser ~= self.parse_space then - assert(not oset, "Scope violation: Too many objects") - object = robject - oset = true - - if strict then - return chunk, object - end - end - end - - assert(not src_err, src_err) - assert(oset, "Unexpected EOS") - - self.data = object -end - - -function Decoder.fetch(self) - local tself, chunk, src_err = coroutine.yield() - assert(chunk or not src_err, src_err) - return chunk -end - - -function Decoder.fetch_atleast(self, chunk, bytes) - while #chunk < bytes do - local nchunk = self:fetch() - assert(nchunk, "Unexpected EOS") - chunk = chunk .. nchunk - end - - return chunk -end - - -function Decoder.fetch_until(self, chunk, pattern) - local start = chunk:find(pattern) - - while not start do - local nchunk = self:fetch() - assert(nchunk, "Unexpected EOS") - chunk = chunk .. nchunk - start = chunk:find(pattern) - end - - return chunk, start -end - - -function Decoder.parse_space(self, chunk) - local start = chunk:find("[^%s]") - - while not start do - chunk = self:fetch() - if not chunk then - return nil - end - start = chunk:find("[^%s]") - end - - return chunk:sub(start) -end - - -function Decoder.parse_literal(self, chunk, literal, value) - chunk = self:fetch_atleast(chunk, #literal) - assert(chunk:sub(1, #literal) == literal, "Invalid character sequence") - return chunk:sub(#literal + 1), value -end - - -function Decoder.parse_null(self, chunk) - return self:parse_literal(chunk, "null", self.cnull and null) -end - - -function Decoder.parse_true(self, chunk) - return self:parse_literal(chunk, "true", true) -end - - -function Decoder.parse_false(self, chunk) - return self:parse_literal(chunk, "false", false) -end - - -function Decoder.parse_number(self, chunk) - local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]") - local number = tonumber(chunk:sub(1, start - 1)) - assert(number, "Invalid number specification") - return chunk:sub(start), number -end - - -function Decoder.parse_string(self, chunk) - local str = "" - local object = nil - assert(chunk:sub(1, 1) == '"', 'Expected "') - chunk = chunk:sub(2) - - while true do - local spos = chunk:find('[\\"]') - if spos then - str = str .. chunk:sub(1, spos - 1) - - local char = chunk:sub(spos, spos) - if char == '"' then -- String end - chunk = chunk:sub(spos + 1) - break - elseif char == "\\" then -- Escape sequence - chunk, object = self:parse_escape(chunk:sub(spos)) - str = str .. object - end - else - str = str .. chunk - chunk = self:fetch() - assert(chunk, "Unexpected EOS while parsing a string") - end - end - - return chunk, str -end - - -function Decoder.utf8_encode(self, s1, s2) - local n = s1 * 256 + s2 - - if n >= 0 and n <= 0x7F then - return char(n) - elseif n >= 0 and n <= 0x7FF then - return char( - bor(band(rshift(n, 6), 0x1F), 0xC0), - bor(band(n, 0x3F), 0x80) - ) - elseif n >= 0 and n <= 0xFFFF then - return char( - bor(band(rshift(n, 12), 0x0F), 0xE0), - bor(band(rshift(n, 6), 0x3F), 0x80), - bor(band(n, 0x3F), 0x80) - ) - elseif n >= 0 and n <= 0x10FFFF then - return char( - bor(band(rshift(n, 18), 0x07), 0xF0), - bor(band(rshift(n, 12), 0x3F), 0x80), - bor(band(rshift(n, 6), 0x3F), 0x80), - bor(band(n, 0x3F), 0x80) - ) - else - return "?" - end -end - - -function Decoder.parse_escape(self, chunk) - local str = "" - chunk = self:fetch_atleast(chunk:sub(2), 1) - local char = chunk:sub(1, 1) - chunk = chunk:sub(2) - - if char == '"' then - return chunk, '"' - elseif char == "\\" then - return chunk, "\\" - elseif char == "u" then - chunk = self:fetch_atleast(chunk, 4) - local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4) - s1, s2 = tonumber(s1, 16), tonumber(s2, 16) - assert(s1 and s2, "Invalid Unicode character") - - return chunk:sub(5), self:utf8_encode(s1, s2) - elseif char == "/" then - return chunk, "/" - elseif char == "b" then - return chunk, "\b" - elseif char == "f" then - return chunk, "\f" - elseif char == "n" then - return chunk, "\n" - elseif char == "r" then - return chunk, "\r" - elseif char == "t" then - return chunk, "\t" - else - error("Unexpected escaping sequence '\\%s'" % char) - end -end - - -function Decoder.parse_array(self, chunk) - chunk = chunk:sub(2) - local array = {} - local nextp = 1 - - local chunk, object = self:parse_delimiter(chunk, "%]") - - if object then - return chunk, array - end - - repeat - chunk, object = self:dispatch(chunk, nil, true) - table.insert(array, nextp, object) - nextp = nextp + 1 - - chunk, object = self:parse_delimiter(chunk, ",%]") - assert(object, "Delimiter expected") - until object == "]" - - return chunk, array -end - - -function Decoder.parse_object(self, chunk) - chunk = chunk:sub(2) - local array = {} - local name - - local chunk, object = self:parse_delimiter(chunk, "}") - - if object then - return chunk, array - end - - repeat - chunk = self:parse_space(chunk) - assert(chunk, "Unexpected EOS") - - chunk, name = self:parse_string(chunk) - - chunk, object = self:parse_delimiter(chunk, ":") - assert(object, "Separator expected") - - chunk, object = self:dispatch(chunk, nil, true) - array[name] = object - - chunk, object = self:parse_delimiter(chunk, ",}") - assert(object, "Delimiter expected") - until object == "}" - - return chunk, array -end - - -function Decoder.parse_delimiter(self, chunk, delimiter) - while true do - chunk = self:fetch_atleast(chunk, 1) - local char = chunk:sub(1, 1) - if char:match("%s") then - chunk = self:parse_space(chunk) - assert(chunk, "Unexpected EOS") - elseif char:match("[%s]" % delimiter) then - return chunk:sub(2), char - else - return chunk, nil - end - end -end - - -Decoder.parsers = { - ['"'] = Decoder.parse_string, - ['t'] = Decoder.parse_true, - ['f'] = Decoder.parse_false, - ['n'] = Decoder.parse_null, - ['['] = Decoder.parse_array, - ['{'] = Decoder.parse_object -} - - ---- Create a new Active JSON-Decoder. --- @class function --- @name ActiveDecoder --- @param customnull Use luci.json.null instead of nil for decoding null --- @return Active JSON-Decoder -ActiveDecoder = util.class(Decoder) - -function ActiveDecoder.__init__(self, source, customnull) - Decoder.__init__(self, customnull) - self.source = source - self.chunk = nil - getmetatable(self).__call = self.get -end - - ---- Fetches one JSON-object from given source --- @return Decoded object -function ActiveDecoder.get(self) - local chunk, src_err, object - if not self.chunk then - chunk, src_err = self.source() - else - chunk = self.chunk - end - - self.chunk, object = self:dispatch(chunk, src_err, true) - return object -end - - -function ActiveDecoder.fetch(self) - local chunk, src_err = self.source() - assert(chunk or not src_err, src_err) - return chunk -end |