f7b57f916c1e7c7d62a30dd62a1fa5e52ee4c7b5
[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 if key ~= null then
153 first = first or self:put(",")
154 first = first and false
155 self:parse_string(tostring(key))
156 self:put(":")
157 self:dispatch(entry)
158 end
159 end
160
161 self:put("}")
162 else
163 self:put("[")
164 local first = true
165
166 if type(obj) == "table" then
167 for i=1, #obj do
168 first = first or self:put(",")
169 first = first and nil
170 self:dispatch(obj[i])
171 end
172 else
173 for entry in obj do
174 first = first or self:put(",")
175 first = first and nil
176 self:dispatch(entry)
177 end
178 end
179
180 self:put("]")
181 end
182 end
183
184 Encoder.parsers = {
185 ['nil'] = Encoder.parse_nil,
186 ['table'] = Encoder.parse_iter,
187 ['number'] = Encoder.parse_number,
188 ['string'] = Encoder.parse_string,
189 ['boolean'] = Encoder.parse_bool,
190 ['function'] = Encoder.parse_iter
191 }
192
193
194 Decoder = util.class()
195
196 function Decoder.__init__(self, customnull)
197 self.cnull = customnull
198 getmetatable(self).__call = Decoder.sink
199 end
200
201 function Decoder.sink(self)
202 local sink = coroutine.create(self.dispatch)
203 return function(...)
204 return coroutine.resume(sink, self, ...)
205 end
206 end
207
208
209 function Decoder.get(self)
210 return self.data
211 end
212
213 function Decoder.dispatch(self, chunk, src_err, strict)
214 local robject, object
215 local oset = false
216
217 while chunk do
218 while chunk and #chunk < 1 do
219 chunk = self:fetch()
220 end
221
222 assert(not strict or chunk, "Unexpected EOS")
223 if not chunk then break end
224
225 local char = chunk:sub(1, 1)
226 local parser = self.parsers[char]
227 or (char:match("%s") and self.parse_space)
228 or (char:match("[0-9-]") and self.parse_number)
229 or error("Unexpected char '%s'" % char)
230
231 chunk, robject = parser(self, chunk)
232
233 if parser ~= self.parse_space then
234 assert(not oset, "Scope violation: Too many objects")
235 object = robject
236 oset = true
237
238 if strict then
239 return chunk, object
240 end
241 end
242 end
243
244 assert(not src_err, src_err)
245 assert(oset, "Unexpected EOS")
246
247 self.data = object
248 end
249
250
251 function Decoder.fetch(self)
252 local tself, chunk, src_err = coroutine.yield()
253 assert(chunk or not src_err, src_err)
254 return chunk
255 end
256
257
258 function Decoder.fetch_atleast(self, chunk, bytes)
259 while #chunk < bytes do
260 local nchunk = self:fetch()
261 assert(nchunk, "Unexpected EOS")
262 chunk = chunk .. nchunk
263 end
264
265 return chunk
266 end
267
268
269 function Decoder.fetch_until(self, chunk, pattern)
270 local start = chunk:find(pattern)
271
272 while not start do
273 local nchunk = self:fetch()
274 assert(nchunk, "Unexpected EOS")
275 chunk = chunk .. nchunk
276 start = chunk:find(pattern)
277 end
278
279 return chunk, start
280 end
281
282
283 function Decoder.parse_space(self, chunk)
284 local start = chunk:find("[^%s]")
285
286 while not start do
287 chunk = self:fetch()
288 if not chunk then
289 return nil
290 end
291 start = chunk:find("[^%s]")
292 end
293
294 return chunk:sub(start)
295 end
296
297
298 function Decoder.parse_literal(self, chunk, literal, value)
299 chunk = self:fetch_atleast(chunk, #literal)
300 assert(chunk:sub(1, #literal) == literal, "Invalid character sequence")
301 return chunk:sub(#literal + 1), value
302 end
303
304
305 function Decoder.parse_null(self, chunk)
306 return self:parse_literal(chunk, "null", self.cnull and null)
307 end
308
309
310 function Decoder.parse_true(self, chunk)
311 return self:parse_literal(chunk, "true", true)
312 end
313
314
315 function Decoder.parse_false(self, chunk)
316 return self:parse_literal(chunk, "false", false)
317 end
318
319
320 function Decoder.parse_number(self, chunk)
321 local chunk, start = self:fetch_until(chunk, "[^0-9eE.+-]")
322 local number = tonumber(chunk:sub(1, start - 1))
323 assert(number, "Invalid number specification")
324 return chunk:sub(start), number
325 end
326
327
328 function Decoder.parse_string(self, chunk)
329 local str = ""
330 local object = nil
331 assert(chunk:sub(1, 1) == '"', 'Expected "')
332 chunk = chunk:sub(2)
333
334 while true do
335 local spos = chunk:find('[\\"]')
336 if spos then
337 str = str .. chunk:sub(1, spos - 1)
338
339 local char = chunk:sub(spos, spos)
340 if char == '"' then -- String end
341 chunk = chunk:sub(spos + 1)
342 break
343 elseif char == "\\" then -- Escape sequence
344 chunk, object = self:parse_escape(chunk:sub(spos))
345 str = str .. object
346 end
347 else
348 str = str .. chunk
349 chunk = self:fetch()
350 assert(chunk, "Unexpected EOS while parsing a string")
351 end
352 end
353
354 return chunk, str
355 end
356
357
358 function Decoder.utf8_encode(self, s1, s2)
359 local n = s1 * 256 + s2
360
361 if n >= 0 and n <= 0x7F then
362 return char(n)
363 elseif n >= 0 and n <= 0x7FF then
364 return char(
365 bor(band(rshift(n, 6), 0x1F), 0xC0),
366 bor(band(n, 0x3F), 0x80)
367 )
368 elseif n >= 0 and n <= 0xFFFF then
369 return char(
370 bor(band(rshift(n, 12), 0x0F), 0xE0),
371 bor(band(rshift(n, 6), 0x3F), 0x80),
372 bor(band(n, 0x3F), 0x80)
373 )
374 elseif n >= 0 and n <= 0x10FFFF then
375 return char(
376 bor(band(rshift(n, 18), 0x07), 0xF0),
377 bor(band(rshift(n, 12), 0x3F), 0x80),
378 bor(band(rshift(n, 6), 0x3F), 0x80),
379 bor(band(n, 0x3F), 0x80)
380 )
381 else
382 return "?"
383 end
384 end
385
386
387 function Decoder.parse_escape(self, chunk)
388 local str = ""
389 chunk = self:fetch_atleast(chunk:sub(2), 1)
390 local char = chunk:sub(1, 1)
391 chunk = chunk:sub(2)
392
393 if char == '"' then
394 return chunk, '"'
395 elseif char == "\\" then
396 return chunk, "\\"
397 elseif char == "u" then
398 chunk = self:fetch_atleast(chunk, 4)
399 local s1, s2 = chunk:sub(1, 2), chunk:sub(3, 4)
400 s1, s2 = tonumber(s1, 16), tonumber(s2, 16)
401 assert(s1 and s2, "Invalid Unicode character")
402
403 return chunk:sub(5), self:utf8_encode(s1, s2)
404 elseif char == "/" then
405 return chunk, "/"
406 elseif char == "b" then
407 return chunk, "\b"
408 elseif char == "f" then
409 return chunk, "\f"
410 elseif char == "n" then
411 return chunk, "\n"
412 elseif char == "r" then
413 return chunk, "\r"
414 elseif char == "t" then
415 return chunk, "\t"
416 else
417 error("Unexpected escaping sequence '\\%s'" % char)
418 end
419 end
420
421
422 function Decoder.parse_array(self, chunk)
423 chunk = chunk:sub(2)
424 local array = {}
425 local nextp = 1
426
427 local chunk, object = self:parse_delimiter(chunk, "%]")
428
429 if object then
430 return chunk, array
431 end
432
433 repeat
434 chunk, object = self:dispatch(chunk, nil, true)
435 table.insert(array, nextp, object)
436 nextp = nextp + 1
437
438 chunk, object = self:parse_delimiter(chunk, ",%]")
439 assert(object, "Delimiter expected")
440 until object == "]"
441
442 return chunk, array
443 end
444
445
446 function Decoder.parse_object(self, chunk)
447 chunk = chunk:sub(2)
448 local array = {}
449 local name
450
451 local chunk, object = self:parse_delimiter(chunk, "}")
452
453 if object then
454 return chunk, array
455 end
456
457 repeat
458 chunk = self:parse_space(chunk)
459 assert(chunk, "Unexpected EOS")
460
461 chunk, name = self:parse_string(chunk)
462
463 chunk, object = self:parse_delimiter(chunk, ":")
464 assert(object, "Separator expected")
465
466 chunk, object = self:dispatch(chunk, nil, true)
467 array[name] = object
468
469 chunk, object = self:parse_delimiter(chunk, ",}")
470 assert(object, "Delimiter expected")
471 until object == "}"
472
473 return chunk, array
474 end
475
476
477 function Decoder.parse_delimiter(self, chunk, delimiter)
478 while true do
479 chunk = self:fetch_atleast(chunk, 1)
480 local char = chunk:sub(1, 1)
481 if char:match("%s") then
482 chunk = self:parse_space(chunk)
483 assert(chunk, "Unexpected EOS")
484 elseif char:match("[%s]" % delimiter) then
485 return chunk:sub(2), char
486 else
487 return chunk, nil
488 end
489 end
490 end
491
492
493 Decoder.parsers = {
494 ['"'] = Decoder.parse_string,
495 ['t'] = Decoder.parse_true,
496 ['f'] = Decoder.parse_false,
497 ['n'] = Decoder.parse_null,
498 ['['] = Decoder.parse_array,
499 ['{'] = Decoder.parse_object
500 }
501
502
503 ActiveDecoder = util.class(Decoder)
504
505 function ActiveDecoder.__init__(self, source, customnull)
506 Decoder.__init__(self, customnull)
507 self.source = source
508 self.chunk = nil
509 getmetatable(self).__call = self.get
510 end
511
512
513 function ActiveDecoder.get(self)
514 local chunk, src_err, object
515 if not self.chunk then
516 chunk, src_err = self.source()
517 else
518 chunk = self.chunk
519 end
520
521 self.chunk, object = self:dispatch(chunk, src_err, true)
522 return object
523 end
524
525
526 function ActiveDecoder.fetch(self)
527 local chunk, src_err = self.source()
528 assert(chunk or not src_err, src_err)
529 return chunk
530 end