summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_json.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/unqlite/jx9_json.c')
-rw-r--r--common/unqlite/jx9_json.c730
1 files changed, 730 insertions, 0 deletions
diff --git a/common/unqlite/jx9_json.c b/common/unqlite/jx9_json.c
new file mode 100644
index 0000000..d92f786
--- /dev/null
+++ b/common/unqlite/jx9_json.c
@@ -0,0 +1,730 @@
1/*
2 * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON.
3 * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/
4 * Version 1.7.2
5 * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES
6 * please contact Symisc Systems via:
7 * legal@symisc.net
8 * licensing@symisc.net
9 * contact@symisc.net
10 * or visit:
11 * http://jx9.symisc.net/
12 */
13 /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */
14#ifndef JX9_AMALGAMATION
15#include "jx9Int.h"
16#endif
17/* This file deals with JSON serialization, decoding and stuff like that. */
18/*
19 * Section:
20 * JSON encoding/decoding routines.
21 * Authors:
22 * Symisc Systems, devel@symisc.net.
23 * Copyright (C) Symisc Systems, http://jx9.symisc.net
24 * Status:
25 * Devel.
26 */
27/* Forward reference */
28static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
29static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData);
30/*
31 * JSON encoder state is stored in an instance
32 * of the following structure.
33 */
34typedef struct json_private_data json_private_data;
35struct json_private_data
36{
37 SyBlob *pOut; /* Output consumer buffer */
38 int isFirst; /* True if first encoded entry */
39 int iFlags; /* JSON encoding flags */
40 int nRecCount; /* Recursion count */
41};
42/*
43 * Returns the JSON representation of a value.In other word perform a JSON encoding operation.
44 * According to wikipedia
45 * JSON's basic types are:
46 * Number (double precision floating-point format in JavaScript, generally depends on implementation)
47 * String (double-quoted Unicode, with backslash escaping)
48 * Boolean (true or false)
49 * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
50 * do not need to be of the same type)
51 * Object (an unordered collection of key:value pairs with the ':' character separating the key
52 * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
53 * be distinct from each other)
54 * null (empty)
55 * Non-significant white space may be added freely around the "structural characters"
56 * (i.e. the brackets "[{]}", colon ":" and comma ",").
57 */
58static sxi32 VmJsonEncode(
59 jx9_value *pIn, /* Encode this value */
60 json_private_data *pData /* Context data */
61 ){
62 SyBlob *pOut = pData->pOut;
63 int nByte;
64 if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){
65 /* null */
66 SyBlobAppend(pOut, "null", sizeof("null")-1);
67 }else if( jx9_value_is_bool(pIn) ){
68 int iBool = jx9_value_to_bool(pIn);
69 sxu32 iLen;
70 /* true/false */
71 iLen = iBool ? sizeof("true") : sizeof("false");
72 SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1);
73 }else if( jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){
74 const char *zNum;
75 /* Get a string representation of the number */
76 zNum = jx9_value_to_string(pIn, &nByte);
77 SyBlobAppend(pOut,zNum,nByte);
78 }else if( jx9_value_is_string(pIn) ){
79 const char *zIn, *zEnd;
80 int c;
81 /* Encode the string */
82 zIn = jx9_value_to_string(pIn, &nByte);
83 zEnd = &zIn[nByte];
84 /* Append the double quote */
85 SyBlobAppend(pOut,"\"", sizeof(char));
86 for(;;){
87 if( zIn >= zEnd ){
88 /* No more input to process */
89 break;
90 }
91 c = zIn[0];
92 /* Advance the stream cursor */
93 zIn++;
94 if( c == '"' || c == '\\' ){
95 /* Unescape the character */
96 SyBlobAppend(pOut,"\\", sizeof(char));
97 }
98 /* Append character verbatim */
99 SyBlobAppend(pOut,(const char *)&c,sizeof(char));
100 }
101 /* Append the double quote */
102 SyBlobAppend(pOut,"\"",sizeof(char));
103 }else if( jx9_value_is_json_array(pIn) ){
104 /* Encode the array/object */
105 pData->isFirst = 1;
106 if( jx9_value_is_json_object(pIn) ){
107 /* Encode the object instance */
108 pData->isFirst = 1;
109 /* Append the curly braces */
110 SyBlobAppend(pOut, "{",sizeof(char));
111 /* Iterate throw object attribute */
112 jx9_array_walk(pIn,VmJsonObjectEncode, pData);
113 /* Append the closing curly braces */
114 SyBlobAppend(pOut, "}",sizeof(char));
115 }else{
116 /* Append the square bracket or curly braces */
117 SyBlobAppend(pOut,"[",sizeof(char));
118 /* Iterate throw array entries */
119 jx9_array_walk(pIn, VmJsonArrayEncode, pData);
120 /* Append the closing square bracket or curly braces */
121 SyBlobAppend(pOut,"]",sizeof(char));
122 }
123 }else{
124 /* Can't happen */
125 SyBlobAppend(pOut,"null",sizeof("null")-1);
126 }
127 /* All done */
128 return JX9_OK;
129}
130/*
131 * The following walker callback is invoked each time we need
132 * to encode an array to JSON.
133 */
134static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData)
135{
136 json_private_data *pJson = (json_private_data *)pUserData;
137 if( pJson->nRecCount > 31 ){
138 /* Recursion limit reached, return immediately */
139 SXUNUSED(pKey); /* cc warning */
140 return JX9_OK;
141 }
142 if( !pJson->isFirst ){
143 /* Append the colon first */
144 SyBlobAppend(pJson->pOut,",",(int)sizeof(char));
145 }
146 /* Encode the value */
147 pJson->nRecCount++;
148 VmJsonEncode(pValue, pJson);
149 pJson->nRecCount--;
150 pJson->isFirst = 0;
151 return JX9_OK;
152}
153/*
154 * The following walker callback is invoked each time we need to encode
155 * a object instance [i.e: Object in the JX9 jargon] to JSON.
156 */
157static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData)
158{
159 json_private_data *pJson = (json_private_data *)pUserData;
160 const char *zKey;
161 int nByte;
162 if( pJson->nRecCount > 31 ){
163 /* Recursion limit reached, return immediately */
164 return JX9_OK;
165 }
166 if( !pJson->isFirst ){
167 /* Append the colon first */
168 SyBlobAppend(pJson->pOut,",",sizeof(char));
169 }
170 /* Extract a string representation of the key */
171 zKey = jx9_value_to_string(pKey, &nByte);
172 /* Append the key and the double colon */
173 if( nByte > 0 ){
174 SyBlobAppend(pJson->pOut,"\"",sizeof(char));
175 SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte);
176 SyBlobAppend(pJson->pOut,"\"",sizeof(char));
177 }else{
178 /* Can't happen */
179 SyBlobAppend(pJson->pOut,"null",sizeof("null")-1);
180 }
181 SyBlobAppend(pJson->pOut,":",sizeof(char));
182 /* Encode the value */
183 pJson->nRecCount++;
184 VmJsonEncode(pValue, pJson);
185 pJson->nRecCount--;
186 pJson->isFirst = 0;
187 return JX9_OK;
188}
189/*
190 * Returns a string containing the JSON representation of value.
191 * In other words, perform the serialization of the given JSON object.
192 */
193JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut)
194{
195 json_private_data sJson;
196 /* Prepare the JSON data */
197 sJson.nRecCount = 0;
198 sJson.pOut = pOut;
199 sJson.isFirst = 1;
200 sJson.iFlags = 0;
201 /* Perform the encoding operation */
202 VmJsonEncode(pValue, &sJson);
203 /* All done */
204 return JX9_OK;
205}
206/* Possible tokens from the JSON tokenization process */
207#define JSON_TK_TRUE 0x001 /* Boolean true */
208#define JSON_TK_FALSE 0x002 /* Boolean false */
209#define JSON_TK_STR 0x004 /* String enclosed in double quotes */
210#define JSON_TK_NULL 0x008 /* null */
211#define JSON_TK_NUM 0x010 /* Numeric */
212#define JSON_TK_OCB 0x020 /* Open curly braces '{' */
213#define JSON_TK_CCB 0x040 /* Closing curly braces '}' */
214#define JSON_TK_OSB 0x080 /* Open square bracke '[' */
215#define JSON_TK_CSB 0x100 /* Closing square bracket ']' */
216#define JSON_TK_COLON 0x200 /* Single colon ':' */
217#define JSON_TK_COMMA 0x400 /* Single comma ',' */
218#define JSON_TK_ID 0x800 /* ID */
219#define JSON_TK_INVALID 0x1000 /* Unexpected token */
220/*
221 * Tokenize an entire JSON input.
222 * Get a single low-level token from the input file.
223 * Update the stream pointer so that it points to the first
224 * character beyond the extracted token.
225 */
226static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData)
227{
228 int *pJsonErr = (int *)pUserData;
229 SyString *pStr;
230 int c;
231 /* Ignore leading white spaces */
232 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){
233 /* Advance the stream cursor */
234 if( pStream->zText[0] == '\n' ){
235 /* Update line counter */
236 pStream->nLine++;
237 }
238 pStream->zText++;
239 }
240 if( pStream->zText >= pStream->zEnd ){
241 /* End of input reached */
242 SXUNUSED(pCtxData); /* cc warning */
243 return SXERR_EOF;
244 }
245 /* Record token starting position and line */
246 pToken->nLine = pStream->nLine;
247 pToken->pUserData = 0;
248 pStr = &pToken->sData;
249 SyStringInitFromBuf(pStr, pStream->zText, 0);
250 if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){
251 /* The following code fragment is taken verbatim from the xPP source tree.
252 * xPP is a modern embeddable macro processor with advanced features useful for
253 * application seeking for a production quality, ready to use macro processor.
254 * xPP is a widely used library developed and maintened by Symisc Systems.
255 * You can reach the xPP home page by following this link:
256 * http://xpp.symisc.net/
257 */
258 const unsigned char *zIn;
259 /* Isolate UTF-8 or alphanumeric stream */
260 if( pStream->zText[0] < 0xc0 ){
261 pStream->zText++;
262 }
263 for(;;){
264 zIn = pStream->zText;
265 if( zIn[0] >= 0xc0 ){
266 zIn++;
267 /* UTF-8 stream */
268 while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){
269 zIn++;
270 }
271 }
272 /* Skip alphanumeric stream */
273 while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){
274 zIn++;
275 }
276 if( zIn == pStream->zText ){
277 /* Not an UTF-8 or alphanumeric stream */
278 break;
279 }
280 /* Synchronize pointers */
281 pStream->zText = zIn;
282 }
283 /* Record token length */
284 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
285 /* A simple identifier */
286 pToken->nType = JSON_TK_ID;
287 if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){
288 /* boolean true */
289 pToken->nType = JSON_TK_TRUE;
290 }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){
291 /* boolean false */
292 pToken->nType = JSON_TK_FALSE;
293 }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){
294 /* NULL */
295 pToken->nType = JSON_TK_NULL;
296 }
297 return SXRET_OK;
298 }
299 if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']'
300 || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){
301 /* Single character */
302 c = pStream->zText[0];
303 /* Set token type */
304 switch(c){
305 case '[': pToken->nType = JSON_TK_OSB; break;
306 case '{': pToken->nType = JSON_TK_OCB; break;
307 case '}': pToken->nType = JSON_TK_CCB; break;
308 case ']': pToken->nType = JSON_TK_CSB; break;
309 case ':': pToken->nType = JSON_TK_COLON; break;
310 case ',': pToken->nType = JSON_TK_COMMA; break;
311 default:
312 break;
313 }
314 /* Advance the stream cursor */
315 pStream->zText++;
316 }else if( pStream->zText[0] == '"') {
317 /* JSON string */
318 pStream->zText++;
319 pStr->zString++;
320 /* Delimit the string */
321 while( pStream->zText < pStream->zEnd ){
322 if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){
323 break;
324 }
325 if( pStream->zText[0] == '\n' ){
326 /* Update line counter */
327 pStream->nLine++;
328 }
329 pStream->zText++;
330 }
331 if( pStream->zText >= pStream->zEnd ){
332 /* Missing closing '"' */
333 pToken->nType = JSON_TK_INVALID;
334 *pJsonErr = SXERR_SYNTAX;
335 }else{
336 pToken->nType = JSON_TK_STR;
337 pStream->zText++; /* Jump the closing double quotes */
338 }
339 }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
340 /* Number */
341 pStream->zText++;
342 pToken->nType = JSON_TK_NUM;
343 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
344 pStream->zText++;
345 }
346 if( pStream->zText < pStream->zEnd ){
347 c = pStream->zText[0];
348 if( c == '.' ){
349 /* Real number */
350 pStream->zText++;
351 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
352 pStream->zText++;
353 }
354 if( pStream->zText < pStream->zEnd ){
355 c = pStream->zText[0];
356 if( c=='e' || c=='E' ){
357 pStream->zText++;
358 if( pStream->zText < pStream->zEnd ){
359 c = pStream->zText[0];
360 if( c =='+' || c=='-' ){
361 pStream->zText++;
362 }
363 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
364 pStream->zText++;
365 }
366 }
367 }
368 }
369 }else if( c=='e' || c=='E' ){
370 /* Real number */
371 pStream->zText++;
372 if( pStream->zText < pStream->zEnd ){
373 c = pStream->zText[0];
374 if( c =='+' || c=='-' ){
375 pStream->zText++;
376 }
377 while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){
378 pStream->zText++;
379 }
380 }
381 }
382 }
383 }else{
384 /* Unexpected token */
385 pToken->nType = JSON_TK_INVALID;
386 /* Advance the stream cursor */
387 pStream->zText++;
388 *pJsonErr = SXERR_SYNTAX;
389 /* Abort processing immediatley */
390 return SXERR_ABORT;
391 }
392 /* record token length */
393 pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString);
394 if( pToken->nType == JSON_TK_STR ){
395 pStr->nByte--;
396 }
397 /* Return to the lexer */
398 return SXRET_OK;
399}
400/*
401 * JSON decoded input consumer callback signature.
402 */
403typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *);
404/*
405 * JSON decoder state is kept in the following structure.
406 */
407typedef struct json_decoder json_decoder;
408struct json_decoder
409{
410 jx9_context *pCtx; /* Call context */
411 ProcJSONConsumer xConsumer; /* Consumer callback */
412 void *pUserData; /* Last argument to xConsumer() */
413 int iFlags; /* Configuration flags */
414 SyToken *pIn; /* Token stream */
415 SyToken *pEnd; /* End of the token stream */
416 int rec_count; /* Current nesting level */
417 int *pErr; /* JSON decoding error if any */
418};
419/* Forward declaration */
420static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData);
421/*
422 * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store
423 * the result in the given jx9_value.
424 */
425static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker)
426{
427 const char *zIn = pStr->zString;
428 const char *zEnd = &pStr->zString[pStr->nByte];
429 const char *zCur;
430 int c;
431 /* Mark the value as a string */
432 jx9_value_string(pWorker, "", 0); /* Empty string */
433 for(;;){
434 zCur = zIn;
435 while( zIn < zEnd && zIn[0] != '\\' ){
436 zIn++;
437 }
438 if( zIn > zCur ){
439 /* Append chunk verbatim */
440 jx9_value_string(pWorker, zCur, (int)(zIn-zCur));
441 }
442 zIn++;
443 if( zIn >= zEnd ){
444 /* End of the input reached */
445 break;
446 }
447 c = zIn[0];
448 /* Unescape the character */
449 switch(c){
450 case '"': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
451 case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break;
452 case 'n': jx9_value_string(pWorker, "\n", (int)sizeof(char)); break;
453 case 'r': jx9_value_string(pWorker, "\r", (int)sizeof(char)); break;
454 case 't': jx9_value_string(pWorker, "\t", (int)sizeof(char)); break;
455 case 'f': jx9_value_string(pWorker, "\f", (int)sizeof(char)); break;
456 default:
457 jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char));
458 break;
459 }
460 /* Advance the stream cursor */
461 zIn++;
462 }
463}
464/*
465 * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation.
466 * According to wikipedia
467 * JSON's basic types are:
468 * Number (double precision floating-point format in JavaScript, generally depends on implementation)
469 * String (double-quoted Unicode, with backslash escaping)
470 * Boolean (true or false)
471 * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values
472 * do not need to be of the same type)
473 * Object (an unordered collection of key:value pairs with the ':' character separating the key
474 * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should
475 * be distinct from each other)
476 * null (empty)
477 * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", ").
478 */
479static sxi32 VmJsonDecode(
480 json_decoder *pDecoder, /* JSON decoder */
481 jx9_value *pArrayKey /* Key for the decoded array */
482 ){
483 jx9_value *pWorker; /* Worker variable */
484 sxi32 rc;
485 /* Check if we do not nest to much */
486 if( pDecoder->rec_count > 31 ){
487 /* Nesting limit reached, abort decoding immediately */
488 return SXERR_ABORT;
489 }
490 if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){
491 /* Scalar value */
492 pWorker = jx9_context_new_scalar(pDecoder->pCtx);
493 if( pWorker == 0 ){
494 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
495 /* Abort the decoding operation immediately */
496 return SXERR_ABORT;
497 }
498 /* Reflect the JSON image */
499 if( pDecoder->pIn->nType & JSON_TK_NULL ){
500 /* Nullify the value.*/
501 jx9_value_null(pWorker);
502 }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){
503 /* Boolean value */
504 jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 );
505 }else if( pDecoder->pIn->nType & JSON_TK_NUM ){
506 SyString *pStr = &pDecoder->pIn->sData;
507 /*
508 * Numeric value.
509 * Get a string representation first then try to get a numeric
510 * value.
511 */
512 jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
513 /* Obtain a numeric representation */
514 jx9MemObjToNumeric(pWorker);
515 }else if( pDecoder->pIn->nType & JSON_TK_ID ){
516 SyString *pStr = &pDecoder->pIn->sData;
517 jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte);
518 }else{
519 /* Dequote the string */
520 VmJsonDequoteString(&pDecoder->pIn->sData, pWorker);
521 }
522 /* Invoke the consumer callback */
523 rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData);
524 if( rc == SXERR_ABORT ){
525 return SXERR_ABORT;
526 }
527 /* All done, advance the stream cursor */
528 pDecoder->pIn++;
529 }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) {
530 ProcJSONConsumer xOld;
531 void *pOld;
532 /* Array representation*/
533 pDecoder->pIn++;
534 /* Create a working array */
535 pWorker = jx9_context_new_array(pDecoder->pCtx);
536 if( pWorker == 0 ){
537 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
538 /* Abort the decoding operation immediately */
539 return SXERR_ABORT;
540 }
541 /* Save the old consumer */
542 xOld = pDecoder->xConsumer;
543 pOld = pDecoder->pUserData;
544 /* Set the new consumer */
545 pDecoder->xConsumer = VmJsonArrayDecoder;
546 pDecoder->pUserData = pWorker;
547 /* Decode the array */
548 for(;;){
549 /* Jump trailing comma. Note that the standard JX9 engine will not let you
550 * do this.
551 */
552 while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
553 pDecoder->pIn++;
554 }
555 if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){
556 if( pDecoder->pIn < pDecoder->pEnd ){
557 pDecoder->pIn++; /* Jump the trailing ']' */
558 }
559 break;
560 }
561 /* Recurse and decode the entry */
562 pDecoder->rec_count++;
563 rc = VmJsonDecode(pDecoder, 0);
564 pDecoder->rec_count--;
565 if( rc == SXERR_ABORT ){
566 /* Abort processing immediately */
567 return SXERR_ABORT;
568 }
569 /*The cursor is automatically advanced by the VmJsonDecode() function */
570 if( (pDecoder->pIn < pDecoder->pEnd) &&
571 ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){
572 /* Unexpected token, abort immediatley */
573 *pDecoder->pErr = SXERR_SYNTAX;
574 return SXERR_ABORT;
575 }
576 }
577 /* Restore the old consumer */
578 pDecoder->xConsumer = xOld;
579 pDecoder->pUserData = pOld;
580 /* Invoke the old consumer on the decoded array */
581 xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
582 }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) {
583 ProcJSONConsumer xOld;
584 jx9_value *pKey;
585 void *pOld;
586 /* Object representation*/
587 pDecoder->pIn++;
588 /* Create a working array */
589 pWorker = jx9_context_new_array(pDecoder->pCtx);
590 pKey = jx9_context_new_scalar(pDecoder->pCtx);
591 if( pWorker == 0 || pKey == 0){
592 jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory");
593 /* Abort the decoding operation immediately */
594 return SXERR_ABORT;
595 }
596 /* Save the old consumer */
597 xOld = pDecoder->xConsumer;
598 pOld = pDecoder->pUserData;
599 /* Set the new consumer */
600 pDecoder->xConsumer = VmJsonArrayDecoder;
601 pDecoder->pUserData = pWorker;
602 /* Decode the object */
603 for(;;){
604 /* Jump trailing comma. Note that the standard JX9 engine will not let you
605 * do this.
606 */
607 while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){
608 pDecoder->pIn++;
609 }
610 if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){
611 if( pDecoder->pIn < pDecoder->pEnd ){
612 pDecoder->pIn++; /* Jump the trailing ']' */
613 }
614 break;
615 }
616 if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd
617 || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){
618 /* Syntax error, return immediately */
619 *pDecoder->pErr = SXERR_SYNTAX;
620 return SXERR_ABORT;
621 }
622 if( pDecoder->pIn->nType & JSON_TK_ID ){
623 SyString *pStr = &pDecoder->pIn->sData;
624 jx9_value_string(pKey, pStr->zString, (int)pStr->nByte);
625 }else{
626 /* Dequote the key */
627 VmJsonDequoteString(&pDecoder->pIn->sData, pKey);
628 }
629 /* Jump the key and the colon */
630 pDecoder->pIn += 2;
631 /* Recurse and decode the value */
632 pDecoder->rec_count++;
633 rc = VmJsonDecode(pDecoder, pKey);
634 pDecoder->rec_count--;
635 if( rc == SXERR_ABORT ){
636 /* Abort processing immediately */
637 return SXERR_ABORT;
638 }
639 /* Reset the internal buffer of the key */
640 jx9_value_reset_string_cursor(pKey);
641 /*The cursor is automatically advanced by the VmJsonDecode() function */
642 }
643 /* Restore the old consumer */
644 pDecoder->xConsumer = xOld;
645 pDecoder->pUserData = pOld;
646 /* Invoke the old consumer on the decoded object*/
647 xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld);
648 /* Release the key */
649 jx9_context_release_value(pDecoder->pCtx, pKey);
650 }else{
651 /* Unexpected token */
652 return SXERR_ABORT; /* Abort immediately */
653 }
654 /* Release the worker variable */
655 jx9_context_release_value(pDecoder->pCtx, pWorker);
656 return SXRET_OK;
657}
658/*
659 * The following JSON decoder callback is invoked each time
660 * a JSON array representation [i.e: [15, "hello", FALSE] ]
661 * is being decoded.
662 */
663static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
664{
665 jx9_value *pArray = (jx9_value *)pUserData;
666 /* Insert the entry */
667 jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */
668 SXUNUSED(pCtx); /* cc warning */
669 /* All done */
670 return SXRET_OK;
671}
672/*
673 * Standard JSON decoder callback.
674 */
675static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData)
676{
677 /* Return the value directly */
678 jx9_result_value(pCtx, pWorker); /* Will make it's own copy */
679 SXUNUSED(pKey); /* cc warning */
680 SXUNUSED(pUserData);
681 /* All done */
682 return SXRET_OK;
683}
684/*
685 * Exported JSON decoding interface
686 */
687JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte)
688{
689 jx9_vm *pVm = pCtx->pVm;
690 json_decoder sDecoder;
691 SySet sToken;
692 SyLex sLex;
693 sxi32 rc;
694 /* Tokenize the input */
695 SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken));
696 rc = SXRET_OK;
697 SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc);
698 SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0);
699 if( rc != SXRET_OK ){
700 /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */
701 SyLexRelease(&sLex);
702 SySetRelease(&sToken);
703 /* return NULL */
704 jx9_result_null(pCtx);
705 return JX9_OK;
706 }
707 /* Fill the decoder */
708 sDecoder.pCtx = pCtx;
709 sDecoder.pErr = &rc;
710 sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken);
711 sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)];
712 sDecoder.iFlags = 0;
713 sDecoder.rec_count = 0;
714 /* Set a default consumer */
715 sDecoder.xConsumer = VmJsonDefaultDecoder;
716 sDecoder.pUserData = 0;
717 /* Decode the raw JSON input */
718 rc = VmJsonDecode(&sDecoder, 0);
719 if( rc == SXERR_ABORT ){
720 /*
721 * Something goes wrong while decoding JSON input.Return NULL.
722 */
723 jx9_result_null(pCtx);
724 }
725 /* Clean-up the mess left behind */
726 SyLexRelease(&sLex);
727 SySetRelease(&sToken);
728 /* All done */
729 return JX9_OK;
730}