Merge pull request #389 from hnyman/timezone2015d
[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 module "luci.json"
29
30
31 function decode(json, ...)
32         local a = ActiveDecoder(function() return nil end, ...)
33         a.chunk = json
34         local s, obj = pcall(a.get, a)
35         return s and obj or nil
36 end
37
38
39 function encode(obj, ...)
40         local out = {}
41         local e = Encoder(obj, 1, ...):source()
42         local chnk, err
43         repeat
44                 chnk, err = e()
45                 out[#out+1] = chnk
46         until not chnk
47         return not err and table.concat(out) or nil
48 end
49
50
51 function null()
52         return null
53 end
54
55 Encoder = util.class()
56
57 function Encoder.__init__(self, data, buffersize, fastescape)
58         self.data = data
59         self.buffersize = buffersize or 512
60         self.buffer = ""
61         self.fastescape = fastescape
62
63         getmetatable(self).__call = Encoder.source
64 end
65
66 function Encoder.source(self)
67         local source = coroutine.create(self.dispatch)
68         return function()
69                 local res, data = coroutine.resume(source, self, self.data, true)
70                 if res then
71                         return data
72                 else
73                         return nil, data
74                 end
75         end
76 end
77
78 function Encoder.dispatch(self, data, start)
79         local parser = self.parsers[type(data)]
80
81         parser(self, data)
82
83         if start then
84                 if #self.buffer > 0 then
85                         coroutine.yield(self.buffer)
86                 end
87
88                 coroutine.yield()
89         end
90 end
91
92 function Encoder.put(self, chunk)
93         if self.buffersize < 2 then
94                 coroutine.yield(chunk)
95         else
96                 if #self.buffer + #chunk > self.buffersize then
97                         local written = 0
98                         local fbuffer = self.buffersize - #self.buffer
99
100                         coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
101                         written = fbuffer
102
103                         while #chunk - written > self.buffersize do
104                                 fbuffer = written + self.buffersize
105                                 coroutine.yield(chunk:sub(written + 1, fbuffer))
106                                 written = fbuffer
107                         end
108
109                         self.buffer = chunk:sub(written + 1)
110                 else
111                         self.buffer = self.buffer .. chunk
112                 end
113         end
114 end
115
116 function Encoder.parse_nil(self)
117         self:put("null")
118 end
119
120 function Encoder.parse_bool(self, obj)
121         self:put(obj and "true" or "false")
122 end
123
124 function Encoder.parse_number(self, obj)
125         self:put(tostring(obj))
126 end
127
128 function Encoder.parse_string(self, obj)
129         if self.fastescape then
130                 self:put('"' .. obj:gsub('\\', '\\\\'):gsub('"', '\\"') .. '"')
131         else
132                 self:put('"' ..
133                         obj:gsub('[%c\\"]',
134                                 function(char)
135                                         return '\\u00%02x' % char:byte()
136                                 end
137                         )
138                 .. '"')
139         end
140 end
141
142 function Encoder.parse_iter(self, obj)
143         if obj == null then
144                 return self:put("null")
145         end
146
147         if type(obj) == "table" and (#obj == 0 and next(obj)) then
148                 self:put("{")
149                 local first = true
150
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))
155                         self:put(":")
156                         self:dispatch(entry)
157                 end
158
159                 self:put("}")
160         else
161                 self:put("[")
162                 local first = true
163
164                 if type(obj) == "table" then
165                         for i=1, #obj do
166                                 first = first or self:put(",")
167                                 first = first and nil
168                                 self:dispatch(obj[i])
169                         end
170                 else
171                         for entry in obj do
172                                 first = first or self:put(",")
173                                 first = first and nil
174                                 self:dispatch(entry)
175                         end
176                 end
177
178                 self:put("]")
179         end
180 end
181
182 Encoder.parsers = {
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
189 }
190
191
192 Decoder = util.class()
193
194 function Decoder.__init__(self, customnull)
195         self.cnull = customnull
196         getmetatable(self).__call = Decoder.sink
197 end
198
199 function Decoder.sink(self)
200         local sink = coroutine.create(self.dispatch)
201         return function(...)
202                 return coroutine.resume(sink, self, ...)
203         end
204 end
205
206
207 function Decoder.get(self)
208         return self.data
209 end
210
211 function Decoder.dispatch(self, chunk, src_err, strict)
212         local robject, object
213         local oset = false
214
215         while chunk do
216                 while chunk and #chunk < 1 do
217                         chunk = self:fetch()
218                 end
219
220                 assert(not strict or chunk, "Unexpected EOS")
221                 if not chunk then break end
222
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)
228
229                 chunk, robject = parser(self, chunk)
230
231                 if parser ~= self.parse_space then
232                         assert(not oset, "Scope violation: Too many objects")
233                         object = robject
234                         oset = true
235
236                         if strict then
237                                 return chunk, object
238                         end
239                 end
240         end
241
242         assert(not src_err, src_err)
243         assert(oset, "Unexpected EOS")
244
245         self.data = object
246 end
247
248
249 function Decoder.fetch(self)
250         local tself, chunk, src_err = coroutine.yield()
251         assert(chunk or not src_err, src_err)
252         return chunk
253 end
254
255
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
261         end
262
263         return chunk
264 end
265
266
267 function Decoder.fetch_until(self, chunk, pattern)
268         local start = chunk:find(pattern)
269
270         while not start do
271                 local nchunk = self:fetch()
272                 assert(nchunk, "Unexpected EOS")
273                 chunk = chunk .. nchunk
274                 start = chunk:find(pattern)
275         end
276
277         return chunk, start
278 end
279
280
281 function Decoder.parse_space(self, chunk)
282         local start = chunk:find("[^%s]")
283
284         while not start do
285                 chunk = self:fetch()
286                 if not chunk then
287                         return nil
288                 end
289                 start = chunk:find("[^%s]")
290         end
291
292         return chunk:sub(start)
293 end
294
295
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
300 end
301
302
303 function Decoder.parse_null(self, chunk)
304         return self:parse_literal(chunk, "null", self.cnull and null)
305 end
306
307
308 function Decoder.parse_true(self, chunk)
309         return self:parse_literal(chunk, "true", true)
310 end
311
312
313 function Decoder.parse_false(self, chunk)
314         return self:parse_literal(chunk, "false", false)
315 end
316
317
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
323 end
324
325
326 function Decoder.parse_string(self, chunk)
327         local str = ""
328         local object = nil
329         assert(chunk:sub(1, 1) == '"', 'Expected "')
330         chunk = chunk:sub(2)
331
332         while true do
333                 local spos = chunk:find('[\\"]')
334                 if spos then
335                         str = str .. chunk:sub(1, spos - 1)
336
337                         local char = chunk:sub(spos, spos)
338                         if char == '"' then                             -- String end
339                                 chunk = chunk:sub(spos + 1)
340                                 break
341                         elseif char == "\\" then                -- Escape sequence
342                                 chunk, object = self:parse_escape(chunk:sub(spos))
343                                 str = str .. object
344                         end
345                 else
346                         str = str .. chunk
347                         chunk = self:fetch()
348                         assert(chunk, "Unexpected EOS while parsing a string")
349                 end
350         end
351
352         return chunk, str
353 end
354
355
356 function Decoder.utf8_encode(self, s1, s2)
357         local n = s1 * 256 + s2
358
359         if n >= 0 and n <= 0x7F then
360                 return char(n)
361         elseif n >= 0 and n <= 0x7FF then
362                 return char(
363                         bor(band(rshift(n,  6), 0x1F), 0xC0),
364                         bor(band(n,             0x3F), 0x80)
365                 )
366         elseif n >= 0 and n <= 0xFFFF then
367                 return char(
368                         bor(band(rshift(n, 12), 0x0F), 0xE0),
369                         bor(band(rshift(n,  6), 0x3F), 0x80),
370                         bor(band(n,             0x3F), 0x80)
371                 )
372         elseif n >= 0 and n <= 0x10FFFF then
373                 return char(
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)
378                 )
379         else
380                 return "?"
381         end
382 end
383
384
385 function Decoder.parse_escape(self, chunk)
386         local str = ""
387         chunk = self:fetch_atleast(chunk:sub(2), 1)
388         local char = chunk:sub(1, 1)
389         chunk = chunk:sub(2)
390
391         if char == '"' then
392                 return chunk, '"'
393         elseif char == "\\" then
394                 return chunk, "\\"
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")
400
401                 return chunk:sub(5), self:utf8_encode(s1, s2)
402         elseif char == "/" then
403                 return chunk, "/"
404         elseif char == "b" then
405                 return chunk, "\b"
406         elseif char == "f" then
407                 return chunk, "\f"
408         elseif char == "n" then
409                 return chunk, "\n"
410         elseif char == "r" then
411                 return chunk, "\r"
412         elseif char == "t" then
413                 return chunk, "\t"
414         else
415                 error("Unexpected escaping sequence '\\%s'" % char)
416         end
417 end
418
419
420 function Decoder.parse_array(self, chunk)
421         chunk = chunk:sub(2)
422         local array = {}
423         local nextp = 1
424
425         local chunk, object = self:parse_delimiter(chunk, "%]")
426
427         if object then
428                 return chunk, array
429         end
430
431         repeat
432                 chunk, object = self:dispatch(chunk, nil, true)
433                 table.insert(array, nextp, object)
434                 nextp = nextp + 1
435
436                 chunk, object = self:parse_delimiter(chunk, ",%]")
437                 assert(object, "Delimiter expected")
438         until object == "]"
439
440         return chunk, array
441 end
442
443
444 function Decoder.parse_object(self, chunk)
445         chunk = chunk:sub(2)
446         local array = {}
447         local name
448
449         local chunk, object = self:parse_delimiter(chunk, "}")
450
451         if object then
452                 return chunk, array
453         end
454
455         repeat
456                 chunk = self:parse_space(chunk)
457                 assert(chunk, "Unexpected EOS")
458
459                 chunk, name   = self:parse_string(chunk)
460
461                 chunk, object = self:parse_delimiter(chunk, ":")
462                 assert(object, "Separator expected")
463
464                 chunk, object = self:dispatch(chunk, nil, true)
465                 array[name] = object
466
467                 chunk, object = self:parse_delimiter(chunk, ",}")
468                 assert(object, "Delimiter expected")
469         until object == "}"
470
471         return chunk, array
472 end
473
474
475 function Decoder.parse_delimiter(self, chunk, delimiter)
476         while true do
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
484                 else
485                         return chunk, nil
486                 end
487         end
488 end
489
490
491 Decoder.parsers = {
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
498 }
499
500
501 ActiveDecoder = util.class(Decoder)
502
503 function ActiveDecoder.__init__(self, source, customnull)
504         Decoder.__init__(self, customnull)
505         self.source = source
506         self.chunk = nil
507         getmetatable(self).__call = self.get
508 end
509
510
511 function ActiveDecoder.get(self)
512         local chunk, src_err, object
513         if not self.chunk then
514                 chunk, src_err = self.source()
515         else
516                 chunk = self.chunk
517         end
518
519         self.chunk, object = self:dispatch(chunk, src_err, true)
520         return object
521 end
522
523
524 function ActiveDecoder.fetch(self)
525         local chunk, src_err = self.source()
526         assert(chunk or not src_err, src_err)
527         return chunk
528 end