Update my email addresses in the license headers
[project/luci.git] / libs / luci-lib-json / luasrc / json.lua
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.
4
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"
10
11 local assert    = assert
12 local tonumber  = tonumber
13 local tostring  = tostring
14 local error     = error
15 local type          = type
16 local pairs         = pairs
17 local ipairs    = ipairs
18 local next      = next
19 local pcall             = pcall
20
21 local band      = nixio.bit.band
22 local bor       = nixio.bit.bor
23 local rshift    = nixio.bit.rshift
24 local char      = string.char
25
26 local getmetatable = getmetatable
27
28 --- LuCI JSON-Library
29 -- @cstyle      instance
30 module "luci.json"
31
32
33 --- Directly decode a JSON string
34 -- @param json JSON-String
35 -- @return Lua object
36 function decode(json, ...)
37         local a = ActiveDecoder(function() return nil end, ...)
38         a.chunk = json
39         local s, obj = pcall(a.get, a)
40         return s and obj or nil
41 end
42
43
44 --- Direcly encode a Lua object into a JSON string.
45 -- @param obj Lua Object
46 -- @return JSON string
47 function encode(obj, ...)
48         local out = {}
49         local e = Encoder(obj, 1, ...):source()
50         local chnk, err
51         repeat
52                 chnk, err = e()
53                 out[#out+1] = chnk
54         until not chnk
55         return not err and table.concat(out) or nil
56 end
57
58
59 --- Null replacement function
60 -- @return null
61 function null()
62         return null
63 end
64
65 --- Create a new JSON-Encoder.
66 -- @class       function
67 -- @name        Encoder
68 -- @param data                  Lua-Object to be encoded.
69 -- @param buffersize    Blocksize of returned data source.
70 -- @param fastescape    Use non-standard escaping (don't escape control chars)
71 -- @return JSON-Encoder
72 Encoder = util.class()
73
74 function Encoder.__init__(self, data, buffersize, fastescape)
75         self.data = data
76         self.buffersize = buffersize or 512
77         self.buffer = ""
78         self.fastescape = fastescape
79
80         getmetatable(self).__call = Encoder.source
81 end
82
83 --- Create an LTN12 source providing the encoded JSON-Data.
84 -- @return LTN12 source
85 function Encoder.source(self)
86         local source = coroutine.create(self.dispatch)
87         return function()
88                 local res, data = coroutine.resume(source, self, self.data, true)
89                 if res then
90                         return data
91                 else
92                         return nil, data
93                 end
94         end
95 end
96
97 function Encoder.dispatch(self, data, start)
98         local parser = self.parsers[type(data)]
99
100         parser(self, data)
101
102         if start then
103                 if #self.buffer > 0 then
104                         coroutine.yield(self.buffer)
105                 end
106
107                 coroutine.yield()
108         end
109 end
110
111 function Encoder.put(self, chunk)
112         if self.buffersize < 2 then
113                 coroutine.yield(chunk)
114         else
115                 if #self.buffer + #chunk > self.buffersize then
116                         local written = 0
117                         local fbuffer = self.buffersize - #self.buffer
118
119                         coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
120                         written = fbuffer
121
122                         while #chunk - written > self.buffersize do
123                                 fbuffer = written + self.buffersize
124                                 coroutine.yield(chunk:sub(written + 1, fbuffer))
125                                 written = fbuffer
126                         end
127
128                         self.buffer = chunk:sub(written + 1)
129                 else
130                         self.buffer = self.buffer .. chunk
131                 end
132         end
133 end
134
135 function Encoder.parse_nil(self)
136         self:put("null")
137 end
138
139 function Encoder.parse_bool(self, obj)
140         self:put(obj and "true" or "false")
141 end
142
143 function Encoder.parse_number(self, obj)
144         self:put(tostring(obj))
145 end
146
147 function Encoder.parse_string(self, obj)
148         if self.fastescape then
149                 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
150         else
151                 self:put('"' ..
152                         obj:gsub('[%c\\"]',
153                                 function(char)
154                                         return '\\u00%02x' % char:byte()
155                                 end
156                         )
157                 .. '"')
158         end
159 end
160
161 function Encoder.parse_iter(self, obj)
162         if obj == null then
163                 return self:put("null")
164         end
165
166         if type(obj) == "table" and (#obj == 0 and next(obj)) then
167                 self:put("{")
168                 local first = true
169
170                 for key, entry in pairs(obj) do
171                         first = first or self:put(",")
172                         first = first and false
173                         self:parse_string(tostring(key))
174                         self:put(":")
175                         self:dispatch(entry)
176                 end
177
178                 self:put("}")
179         else
180                 self:put("[")
181                 local first = true
182
183                 if type(obj) == "table" then
184                         for i=1, #obj do
185                                 first = first or self:put(",")
186                                 first = first and nil
187                                 self:dispatch(obj[i])
188                         end
189                 else
190                         for entry in obj do
191                                 first = first or self:put(",")
192                                 first = first and nil
193                                 self:dispatch(entry)
194                         end
195                 end
196
197                 self:put("]")
198         end
199 end
200
201 Encoder.parsers = {
202         ['nil']      = Encoder.parse_nil,
203         ['table']    = Encoder.parse_iter,
204         ['number']   = Encoder.parse_number,
205         ['string']   = Encoder.parse_string,
206         ['boolean']  = Encoder.parse_bool,
207         ['function'] = Encoder.parse_iter
208 }
209
210
211 --- Create a new JSON-Decoder.
212 -- @class       function
213 -- @name        Decoder
214 -- @param customnull Use luci.json.null instead of nil for decoding null
215 -- @return JSON-Decoder
216 Decoder = util.class()
217
218 function Decoder.__init__(self, customnull)
219         self.cnull = customnull
220         getmetatable(self).__call = Decoder.sink
221 end
222
223 --- Create an LTN12 sink from the decoder object which accepts the JSON-Data.
224 -- @return LTN12 sink
225 function Decoder.sink(self)
226         local sink = coroutine.create(self.dispatch)
227         return function(...)
228                 return coroutine.resume(sink, self, ...)
229         end
230 end
231
232
233 --- Get the decoded data packets after the rawdata has been sent to the sink.
234 -- @return Decoded data
235 function Decoder.get(self)
236         return self.data
237 end
238
239 function Decoder.dispatch(self, chunk, src_err, strict)
240         local robject, object
241         local oset = false
242
243         while chunk do
244                 while chunk and #chunk < 1 do
245                         chunk = self:fetch()
246                 end
247
248                 assert(not strict or chunk, "Unexpected EOS")
249                 if not chunk then break end
250
251                 local char   = chunk:sub(1, 1)
252                 local parser = self.parsers[char]
253                  or (char:match("%s")     and self.parse_space)
254                  or (char:match("[0-9-]") and self.parse_number)
255                  or error("Unexpected char '%s'" % char)
256
257                 chunk, robject = parser(self, chunk)
258
259                 if parser ~= self.parse_space then
260                         assert(not oset, "Scope violation: Too many objects")
261                         object = robject
262                         oset = true
263
264                         if strict then
265                                 return chunk, object
266                         end
267                 end
268         end
269
270         assert(not src_err, src_err)
271         assert(oset, "Unexpected EOS")
272
273         self.data = object
274 end
275
276
277 function Decoder.fetch(self)
278         local tself, chunk, src_err = coroutine.yield()
279         assert(chunk or not src_err, src_err)
280         return chunk
281 end
282
283
284 function Decoder.fetch_atleast(self, chunk, bytes)
285         while #chunk < bytes do
286                 local nchunk = self:fetch()
287                 assert(nchunk, "Unexpected EOS")
288                 chunk = chunk .. nchunk
289         end
290
291         return chunk
292 end
293
294
295 function Decoder.fetch_until(self, chunk, pattern)
296         local start = chunk:find(pattern)
297
298         while not start do
299                 local nchunk = self:fetch()
300                 assert(nchunk, "Unexpected EOS")
301                 chunk = chunk .. nchunk
302                 start = chunk:find(pattern)
303         end
304
305         return chunk, start
306 end
307
308
309 function Decoder.parse_space(self, chunk)
310         local start = chunk:find("[^%s]")
311
312         while not start do
313                 chunk = self:fetch()
314                 if not chunk then
315                         return nil
316                 end
317                 start = chunk:find("[^%s]")
318         end
319
320         return chunk:sub(start)
321 end
322
323
324 function Decoder.parse_literal(self, chunk, literal, value)
325         chunk = self:fetch_atleast(chunk, #literal)
326         assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
327         return chunk:sub(#literal + 1), value
328 end
329
330
331 function Decoder.parse_null(self, chunk)
332         return self:parse_literal(chunk, "null", self.cnull and null)
333 end
334
335
336 function Decoder.parse_true(self, chunk)
337         return self:parse_literal(chunk, "true", true)
338 end
339
340
341 function Decoder.parse_false(self, chunk)
342         return self:parse_literal(chunk, "false", false)
343 end
344
345
346 function Decoder.parse_number(self, chunk)
347         local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
348         local number = tonumber(chunk:sub(1, start - 1))
349         assert(number, "Invalid number specification")
350         return chunk:sub(start), number
351 end
352
353
354 function Decoder.parse_string(self, chunk)
355         local str = ""
356         local object = nil
357         assert(chunk:sub(1, 1) == '"', 'Expected "')
358         chunk = chunk:sub(2)
359
360         while true do
361                 local spos = chunk:find('[\\"]')
362                 if spos then
363                         str = str .. chunk:sub(1, spos - 1)
364
365                         local char = chunk:sub(spos, spos)
366                         if char == '"' then                             -- String end
367                                 chunk = chunk:sub(spos + 1)
368                                 break
369                         elseif char == "\\" then                -- Escape sequence
370                                 chunk, object = self:parse_escape(chunk:sub(spos))
371                                 str = str .. object
372                         end
373                 else
374                         str = str .. chunk
375                         chunk = self:fetch()
376                         assert(chunk, "Unexpected EOS while parsing a string")
377                 end
378         end
379
380         return chunk, str
381 end
382
383
384 function Decoder.utf8_encode(self, s1, s2)
385         local n = s1 * 256 + s2
386
387         if n >= 0 and n <= 0x7F then
388                 return char(n)
389         elseif n >= 0 and n <= 0x7FF then
390                 return char(
391                         bor(band(rshift(n,  6), 0x1F), 0xC0),
392                         bor(band(n,             0x3F), 0x80)
393                 )
394         elseif n >= 0 and n <= 0xFFFF then
395                 return char(
396                         bor(band(rshift(n, 12), 0x0F), 0xE0),
397                         bor(band(rshift(n,  6), 0x3F), 0x80),
398                         bor(band(n,             0x3F), 0x80)
399                 )
400         elseif n >= 0 and n <= 0x10FFFF then
401                 return char(
402                         bor(band(rshift(n, 18), 0x07), 0xF0),
403                         bor(band(rshift(n, 12), 0x3F), 0x80),
404                         bor(band(rshift(n,  6), 0x3F), 0x80),
405                         bor(band(n,             0x3F), 0x80)
406                 )
407         else
408                 return "?"
409         end
410 end
411
412
413 function Decoder.parse_escape(self, chunk)
414         local str = ""
415         chunk = self:fetch_atleast(chunk:sub(2), 1)
416         local char = chunk:sub(1, 1)
417         chunk = chunk:sub(2)
418
419         if char == '"' then
420                 return chunk, '"'
421         elseif char == "\\" then
422                 return chunk, "\\"
423         elseif char == "u" then
424                 chunk = self:fetch_atleast(chunk, 4)
425                 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
426                 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
427                 assert(s1 and s2, "Invalid Unicode character")
428
429                 return chunk:sub(5), self:utf8_encode(s1, s2)
430         elseif char == "/" then
431                 return chunk, "/"
432         elseif char == "b" then
433                 return chunk, "\b"
434         elseif char == "f" then
435                 return chunk, "\f"
436         elseif char == "n" then
437                 return chunk, "\n"
438         elseif char == "r" then
439                 return chunk, "\r"
440         elseif char == "t" then
441                 return chunk, "\t"
442         else
443                 error("Unexpected escaping sequence '\\%s'" % char)
444         end
445 end
446
447
448 function Decoder.parse_array(self, chunk)
449         chunk = chunk:sub(2)
450         local array = {}
451         local nextp = 1
452
453         local chunk, object = self:parse_delimiter(chunk, "%]")
454
455         if object then
456                 return chunk, array
457         end
458
459         repeat
460                 chunk, object = self:dispatch(chunk, nil, true)
461                 table.insert(array, nextp, object)
462                 nextp = nextp + 1
463
464                 chunk, object = self:parse_delimiter(chunk, ",%]")
465                 assert(object, "Delimiter expected")
466         until object == "]"
467
468         return chunk, array
469 end
470
471
472 function Decoder.parse_object(self, chunk)
473         chunk = chunk:sub(2)
474         local array = {}
475         local name
476
477         local chunk, object = self:parse_delimiter(chunk, "}")
478
479         if object then
480                 return chunk, array
481         end
482
483         repeat
484                 chunk = self:parse_space(chunk)
485                 assert(chunk, "Unexpected EOS")
486
487                 chunk, name   = self:parse_string(chunk)
488
489                 chunk, object = self:parse_delimiter(chunk, ":")
490                 assert(object, "Separator expected")
491
492                 chunk, object = self:dispatch(chunk, nil, true)
493                 array[name] = object
494
495                 chunk, object = self:parse_delimiter(chunk, ",}")
496                 assert(object, "Delimiter expected")
497         until object == "}"
498
499         return chunk, array
500 end
501
502
503 function Decoder.parse_delimiter(self, chunk, delimiter)
504         while true do
505                 chunk = self:fetch_atleast(chunk, 1)
506                 local char = chunk:sub(1, 1)
507                 if char:match("%s") then
508                         chunk = self:parse_space(chunk)
509                         assert(chunk, "Unexpected EOS")
510                 elseif char:match("[%s]" % delimiter) then
511                         return chunk:sub(2), char
512                 else
513                         return chunk, nil
514                 end
515         end
516 end
517
518
519 Decoder.parsers = {
520         ['"'] = Decoder.parse_string,
521         ['t'] = Decoder.parse_true,
522         ['f'] = Decoder.parse_false,
523         ['n'] = Decoder.parse_null,
524         ['['] = Decoder.parse_array,
525         ['{'] = Decoder.parse_object
526 }
527
528
529 --- Create a new Active JSON-Decoder.
530 -- @class       function
531 -- @name        ActiveDecoder
532 -- @param   customnull  Use luci.json.null instead of nil for decoding null
533 -- @return  Active JSON-Decoder
534 ActiveDecoder = util.class(Decoder)
535
536 function ActiveDecoder.__init__(self, source, customnull)
537         Decoder.__init__(self, customnull)
538         self.source = source
539         self.chunk = nil
540         getmetatable(self).__call = self.get
541 end
542
543
544 --- Fetches one JSON-object from given source
545 -- @return Decoded object
546 function ActiveDecoder.get(self)
547         local chunk, src_err, object
548         if not self.chunk then
549                 chunk, src_err = self.source()
550         else
551                 chunk = self.chunk
552         end
553
554         self.chunk, object = self:dispatch(chunk, src_err, true)
555         return object
556 end
557
558
559 function ActiveDecoder.fetch(self)
560         local chunk, src_err = self.source()
561         assert(chunk or not src_err, src_err)
562         return chunk
563 end