1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
3 -- Licensed to the public under the Apache License 2.0.
5 local nixio = require "nixio"
6 local util = require "luci.util"
7 local table = require "table"
8 local string = require "string"
9 local coroutine = require "coroutine"
12 local tonumber = tonumber
13 local tostring = tostring
21 local band = nixio.bit.band
22 local bor = nixio.bit.bor
23 local rshift = nixio.bit.rshift
24 local char = string.char
26 local getmetatable = getmetatable
31 function decode(json, ...)
32 local a = ActiveDecoder(function() return nil end, ...)
34 local s, obj = pcall(a.get, a)
35 return s and obj or nil
39 function encode(obj, ...)
41 local e = Encoder(obj, 1, ...):source()
47 return not err and table.concat(out) or nil
55 Encoder = util.class()
57 function Encoder.__init__(self, data, buffersize, fastescape)
59 self.buffersize = buffersize or 512
61 self.fastescape = fastescape
63 getmetatable(self).__call = Encoder.source
66 function Encoder.source(self)
67 local source = coroutine.create(self.dispatch)
69 local res, data = coroutine.resume(source, self, self.data, true)
78 function Encoder.dispatch(self, data, start)
79 local parser = self.parsers[type(data)]
84 if #self.buffer > 0 then
85 coroutine.yield(self.buffer)
92 function Encoder.put(self, chunk)
93 if self.buffersize < 2 then
94 coroutine.yield(chunk)
96 if #self.buffer + #chunk > self.buffersize then
98 local fbuffer = self.buffersize - #self.buffer
100 coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
103 while #chunk - written > self.buffersize do
104 fbuffer = written + self.buffersize
105 coroutine.yield(chunk:sub(written + 1, fbuffer))
109 self.buffer = chunk:sub(written + 1)
111 self.buffer = self.buffer .. chunk
116 function Encoder.parse_nil(self)
120 function Encoder.parse_bool(self, obj)
121 self:put(obj and "true" or "false")
124 function Encoder.parse_number(self, obj)
125 self:put(tostring(obj))
128 function Encoder.parse_string(self, obj)
129 if self.fastescape then
130 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
135 return '\\u00%02x' % char:byte()
142 function Encoder.parse_iter(self, obj)
144 return self:put("null")
147 if type(obj) == "table" and (#obj == 0 and next(obj)) then
151 for key, entry in pairs(obj) do
152 first = first or self:put(",")
153 first = first and false
154 self:parse_string(tostring(key))
164 if type(obj) == "table" then
166 first = first or self:put(",")
167 first = first and nil
168 self:dispatch(obj[i])
172 first = first or self:put(",")
173 first = first and nil
183 ['nil'] = Encoder.parse_nil,
184 ['table'] = Encoder.parse_iter,
185 ['number'] = Encoder.parse_number,
186 ['string'] = Encoder.parse_string,
187 ['boolean'] = Encoder.parse_bool,
188 ['function'] = Encoder.parse_iter
192 Decoder = util.class()
194 function Decoder.__init__(self, customnull)
195 self.cnull = customnull
196 getmetatable(self).__call = Decoder.sink
199 function Decoder.sink(self)
200 local sink = coroutine.create(self.dispatch)
202 return coroutine.resume(sink, self, ...)
207 function Decoder.get(self)
211 function Decoder.dispatch(self, chunk, src_err, strict)
212 local robject, object
216 while chunk and #chunk < 1 do
220 assert(not strict or chunk, "Unexpected EOS")
221 if not chunk then break end
223 local char = chunk:sub(1, 1)
224 local parser = self.parsers[char]
225 or (char:match("%s") and self.parse_space)
226 or (char:match("[0-9-]") and self.parse_number)
227 or error("Unexpected char '%s'" % char)
229 chunk, robject = parser(self, chunk)
231 if parser ~= self.parse_space then
232 assert(not oset, "Scope violation: Too many objects")
242 assert(not src_err, src_err)
243 assert(oset, "Unexpected EOS")
249 function Decoder.fetch(self)
250 local tself, chunk, src_err = coroutine.yield()
251 assert(chunk or not src_err, src_err)
256 function Decoder.fetch_atleast(self, chunk, bytes)
257 while #chunk < bytes do
258 local nchunk = self:fetch()
259 assert(nchunk, "Unexpected EOS")
260 chunk = chunk .. nchunk
267 function Decoder.fetch_until(self, chunk, pattern)
268 local start = chunk:find(pattern)
271 local nchunk = self:fetch()
272 assert(nchunk, "Unexpected EOS")
273 chunk = chunk .. nchunk
274 start = chunk:find(pattern)
281 function Decoder.parse_space(self, chunk)
282 local start = chunk:find("[^%s]")
289 start = chunk:find("[^%s]")
292 return chunk:sub(start)
296 function Decoder.parse_literal(self, chunk, literal, value)
297 chunk = self:fetch_atleast(chunk, #literal)
298 assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
299 return chunk:sub(#literal + 1), value
303 function Decoder.parse_null(self, chunk)
304 return self:parse_literal(chunk, "null", self.cnull and null)
308 function Decoder.parse_true(self, chunk)
309 return self:parse_literal(chunk, "true", true)
313 function Decoder.parse_false(self, chunk)
314 return self:parse_literal(chunk, "false", false)
318 function Decoder.parse_number(self, chunk)
319 local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
320 local number = tonumber(chunk:sub(1, start - 1))
321 assert(number, "Invalid number specification")
322 return chunk:sub(start), number
326 function Decoder.parse_string(self, chunk)
329 assert(chunk:sub(1, 1) == '"', 'Expected "')
333 local spos = chunk:find('[\\"]')
335 str = str .. chunk:sub(1, spos - 1)
337 local char = chunk:sub(spos, spos)
338 if char == '"' then -- String end
339 chunk = chunk:sub(spos + 1)
341 elseif char == "\\" then -- Escape sequence
342 chunk, object = self:parse_escape(chunk:sub(spos))
348 assert(chunk, "Unexpected EOS while parsing a string")
356 function Decoder.utf8_encode(self, s1, s2)
357 local n = s1 * 256 + s2
359 if n >= 0 and n <= 0x7F then
361 elseif n >= 0 and n <= 0x7FF then
363 bor(band(rshift(n, 6), 0x1F), 0xC0),
364 bor(band(n, 0x3F), 0x80)
366 elseif n >= 0 and n <= 0xFFFF then
368 bor(band(rshift(n, 12), 0x0F), 0xE0),
369 bor(band(rshift(n, 6), 0x3F), 0x80),
370 bor(band(n, 0x3F), 0x80)
372 elseif n >= 0 and n <= 0x10FFFF then
374 bor(band(rshift(n, 18), 0x07), 0xF0),
375 bor(band(rshift(n, 12), 0x3F), 0x80),
376 bor(band(rshift(n, 6), 0x3F), 0x80),
377 bor(band(n, 0x3F), 0x80)
385 function Decoder.parse_escape(self, chunk)
387 chunk = self:fetch_atleast(chunk:sub(2), 1)
388 local char = chunk:sub(1, 1)
393 elseif char == "\\" then
395 elseif char == "u" then
396 chunk = self:fetch_atleast(chunk, 4)
397 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
398 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
399 assert(s1 and s2, "Invalid Unicode character")
401 return chunk:sub(5), self:utf8_encode(s1, s2)
402 elseif char == "/" then
404 elseif char == "b" then
406 elseif char == "f" then
408 elseif char == "n" then
410 elseif char == "r" then
412 elseif char == "t" then
415 error("Unexpected escaping sequence '\\%s'" % char)
420 function Decoder.parse_array(self, chunk)
425 local chunk, object = self:parse_delimiter(chunk, "%]")
432 chunk, object = self:dispatch(chunk, nil, true)
433 table.insert(array, nextp, object)
436 chunk, object = self:parse_delimiter(chunk, ",%]")
437 assert(object, "Delimiter expected")
444 function Decoder.parse_object(self, chunk)
449 local chunk, object = self:parse_delimiter(chunk, "}")
456 chunk = self:parse_space(chunk)
457 assert(chunk, "Unexpected EOS")
459 chunk, name = self:parse_string(chunk)
461 chunk, object = self:parse_delimiter(chunk, ":")
462 assert(object, "Separator expected")
464 chunk, object = self:dispatch(chunk, nil, true)
467 chunk, object = self:parse_delimiter(chunk, ",}")
468 assert(object, "Delimiter expected")
475 function Decoder.parse_delimiter(self, chunk, delimiter)
477 chunk = self:fetch_atleast(chunk, 1)
478 local char = chunk:sub(1, 1)
479 if char:match("%s") then
480 chunk = self:parse_space(chunk)
481 assert(chunk, "Unexpected EOS")
482 elseif char:match("[%s]" % delimiter) then
483 return chunk:sub(2), char
492 ['"'] = Decoder.parse_string,
493 ['t'] = Decoder.parse_true,
494 ['f'] = Decoder.parse_false,
495 ['n'] = Decoder.parse_null,
496 ['['] = Decoder.parse_array,
497 ['{'] = Decoder.parse_object
501 ActiveDecoder = util.class(Decoder)
503 function ActiveDecoder.__init__(self, source, customnull)
504 Decoder.__init__(self, customnull)
507 getmetatable(self).__call = self.get
511 function ActiveDecoder.get(self)
512 local chunk, src_err, object
513 if not self.chunk then
514 chunk, src_err = self.source()
519 self.chunk, object = self:dispatch(chunk, src_err, true)
524 function ActiveDecoder.fetch(self)
525 local chunk, src_err = self.source()
526 assert(chunk or not src_err, src_err)