1 /*
2 * json: Reading and Writing JSON
3 */
4
5 #include "src/compiled.h" /* GAP headers */
6
7 #include "gap-functions.h"
8
9 #include "picojson/picojson.h"
10 #include "picojson/gap-traits.h"
11
12 typedef picojson::value_t<gap_type_traits> gmp_value;
13
JsonToGap(const gmp_value & v)14 Obj JsonToGap(const gmp_value& v)
15 {
16 if (v.is<picojson::null>()) {
17 return Fail;
18 } else if (v.is<bool>()) {
19 if(v.get<bool>())
20 return True;
21 else
22 return False;
23 } else if (v.is<gap_val>()) {
24 return v.get<gap_val>().obj;
25 } else if (v.is<std::string>()) {
26 Obj str;
27 const Char* c_str = v.get<std::string>().c_str();
28 Int len = v.get<std::string>().size();
29 str = NEW_STRING(len);
30 memcpy(CHARS_STRING(str), c_str, len);
31 return str;
32 } else if (v.is<gmp_value::array>()) {
33 const gmp_value::array& a = v.get<gmp_value::array>();
34 Obj list = NEW_PLIST(T_PLIST_DENSE, a.size());
35 SET_LEN_PLIST(list, a.size());
36 for(int i = 1; i <= a.size(); ++i)
37 {
38 Obj val = JsonToGap(a[i-1]);
39 SET_ELM_PLIST(list, i, val);
40 CHANGED_BAG(list);
41 }
42 return list;
43 } else if (v.is<gmp_value::object>()) {
44 const gmp_value::object& o = v.get<gmp_value::object>();
45 Obj rec = NEW_PREC(0);
46 for (gmp_value::object::const_iterator i = o.begin(); i != o.end(); ++i) {
47 Obj res = JsonToGap(i->second);
48 AssPRec(rec, RNamName(i->first.c_str()), res);
49 CHANGED_BAG(rec);
50 }
51 return rec;
52 }
53 return Fail;
54 }
55
56 // Add function prototype missing from string.h
57 extern Obj CopyToStringRep(Obj string);
58
numberOfBytes(UInt c)59 static Int numberOfBytes(UInt c)
60 {
61 if (c < 128)
62 return 1;
63 if (c < 224)
64 return 2;
65 if (c < 240)
66 return 3;
67 return 4;
68 }
69
getChar(Obj list,Int pos)70 static Int getChar(Obj list, Int pos)
71 {
72 Obj c = ELM_LIST(list, pos);
73 if (c == NULL)
74 return 0;
75 else
76 return *((UChar *)ADDR_OBJ(c));
77 }
78
getUTF8Char(Obj list,Int * basepos)79 static Int getUTF8Char(Obj list, Int * basepos)
80 {
81 Int pos = *basepos;
82 UInt val = getChar(list, pos);
83 UInt singlebyte_val = val;
84 Int len = numberOfBytes(val);
85 pos++;
86
87 if (len == 1) {
88 *basepos = pos;
89 return val;
90 }
91
92 switch (len) {
93 case 2:
94 val = val & 0x3F;
95 if (val & 0x20)
96 goto invalid;
97 break;
98 case 3:
99 val = val & 0x1F;
100 if (val & 0x10)
101 goto invalid;
102 break;
103 case 4:
104 val = val & 0x0F;
105 if (val & 0x08)
106 goto invalid;
107 break;
108 default:
109 abort();
110 }
111 val = val & 0x3F;
112
113 for (Int i = 1; i < len; ++i) {
114 UInt c = getChar(list, pos);
115 if ((c & 0xC0) != 0x80)
116 goto invalid;
117 val = (val << 6) | (c & 0x3F);
118 pos++;
119 }
120
121 // Too high
122 if (val > 0x10ffff)
123 goto invalid;
124
125 // UTF-16 Surrogate pair
126 if (val >= 0xd800 && val <= 0xdfff)
127 goto invalid;
128
129 *basepos = pos;
130 return val;
131
132
133 // Hope this is Latin-1
134 invalid:
135 *basepos = *basepos + 1;
136 return singlebyte_val;
137 }
138
outputUnicodeChar(UChar * s,UInt val)139 static UChar * outputUnicodeChar(UChar * s, UInt val)
140 {
141 if (val <= 0x7f)
142 *(s++) = val;
143 else if (val <= 0x7ff) {
144 *(s++) = (0xc0 | ((val >> 6) & 0x1f));
145 *(s++) = (0x80 | (val & 0x3f));
146 }
147 else if (val <= 0xffff) {
148 *(s++) = (0xe0 | ((val >> 12) & 0x0f));
149 *(s++) = (0x80 | ((val >> 6) & 0x3f));
150 *(s++) = (0x80 | (val & 0x3f));
151 }
152 else {
153 *(s++) = (0xf0 | ((val >> 18) & 0x07));
154 *(s++) = (0x80 | ((val >> 12) & 0x3f));
155 *(s++) = (0x80 | ((val >> 6) & 0x3f));
156 *(s++) = (0x80 | (val & 0x3f));
157 }
158 return s;
159 }
160
JSON_ESCAPE_STRING(Obj self,Obj param)161 Obj JSON_ESCAPE_STRING(Obj self, Obj param)
162 {
163 if(!IS_STRING(param))
164 {
165 ErrorQuit("Input to JsonEscapeString must be a string", 0, 0);
166 }
167
168 Int needEscaping = 0;
169 Int lenString = LEN_LIST(param);
170 for (Int i = 1; i <= lenString && needEscaping == 0; ++i) {
171 Obj gapchar = ELMW_LIST(param, i);
172 UChar u = *((UChar*)ADDR_OBJ(gapchar));
173 switch(u)
174 {
175 case '\\': case '"': case '/': case '\b':
176 case '\t': case '\n': case '\f': case '\r':
177 needEscaping = 1;
178 break;
179 default:
180 if (u < ' ' || u >= 128)
181 needEscaping = 1;
182 }
183 }
184
185 if (needEscaping == 0)
186 return param;
187
188 // Massively over-long string
189 Obj copy = NEW_STRING(lenString * 6);
190 UChar * base = CHARS_STRING(copy);
191 UChar * out = base;
192 Int i = 1;
193 while (i <= lenString) {
194 Int u = getUTF8Char(param, &i);
195 switch(u)
196 {
197 case '\\': case '"': case '/':
198 out[0] = '\\';
199 out[1] = u;
200 out += 2;
201 break;
202 #define ESCAPE_CASE(x,y) case x: out[0] = '\\'; out[1] = y; out += 2; break;
203 ESCAPE_CASE('\b', 'b');
204 ESCAPE_CASE('\t', 't');
205 ESCAPE_CASE('\n', 'n');
206 ESCAPE_CASE('\f', 'f');
207 ESCAPE_CASE('\r', 'r');
208 #undef ESCAPE_CASE
209 default:
210 if(u < ' ')
211 {
212 sprintf((char*)out,"\\u%04X",(unsigned)u);
213 out += 6;
214 }
215 else
216 {
217 out = outputUnicodeChar(out, u);
218 }
219 }
220 }
221
222 SET_LEN_STRING(copy, out - base);
223 ResizeBag(copy, SIZEBAG_STRINGLEN(out - base));
224 return copy;
225 }
226
227
228
229 // WARNING: This class is only complete enough to work with
230 // picojson's iterator support.
231 struct GapStreamToInputIterator
232 {
233 Obj stream;
234 enum State {
235 notread, failed, cached
236 };
237 State state;
238 char store;
239
GapStreamToInputIteratorGapStreamToInputIterator240 GapStreamToInputIterator()
241 : stream(0), state(notread), store(0)
242 { }
243
GapStreamToInputIteratorGapStreamToInputIterator244 GapStreamToInputIterator(Obj s)
245 : stream(s), state(notread), store(0)
246 { }
247
GapStreamToInputIteratorGapStreamToInputIterator248 GapStreamToInputIterator(const GapStreamToInputIterator& gstii)
249 : stream(gstii.stream), state(gstii.state), store(gstii.store)
250 { }
251
operator *GapStreamToInputIterator252 char operator*()
253 {
254 if(state == cached)
255 return store;
256
257 if(state == failed)
258 return 0;
259
260 Obj val = callGAPFunction(ReadByteFunction, stream);
261 if(val == Fail)
262 {
263 state = failed;
264 return 0;
265 }
266 else
267 {
268 state = cached;
269 store = INT_INTOBJ(val);
270 return store;
271 }
272 }
273
operator ++GapStreamToInputIterator274 void operator++()
275 {
276 if(state == failed)
277 return;
278
279 // skip character
280 if(state == notread)
281 {
282 *(*this);
283 }
284
285 state = notread;
286 }
287
operator ==(GapStreamToInputIterator & lhs,GapStreamToInputIterator & rhs)288 friend bool operator==(GapStreamToInputIterator& lhs, GapStreamToInputIterator& rhs)
289 { return (lhs.state == failed) == (rhs.state == failed); }
290
291 };
292
293
endGapStreamIterator()294 static GapStreamToInputIterator endGapStreamIterator()
295 {
296 GapStreamToInputIterator g;
297 g.state = GapStreamToInputIterator::failed;
298 return g;
299 }
300
301 // making an object of this type will ensure when the scope is exited
302 // we clean up any GAP objects we cached
303 struct CleanupCacheGuard
304 {
~CleanupCacheGuardCleanupCacheGuard305 ~CleanupCacheGuard()
306 { callGAPFunction(ClearGAPCacheFunction); }
307 };
308
JSON_STREAM_TO_GAP(Obj self,Obj stream)309 Obj JSON_STREAM_TO_GAP(Obj self, Obj stream)
310 {
311 JSON_setupGAPFunctions();
312 CleanupCacheGuard ccg;
313
314 typedef picojson::value_t<gap_type_traits> gmp_value;
315
316 gmp_value v;
317
318 std::string err;
319 bool ungotc_check = false;
320 picojson::parse(v, GapStreamToInputIterator(stream), endGapStreamIterator(), &err, &ungotc_check);
321 if (! err.empty()) {
322 ErrorQuit(err.c_str(), 0, 0);
323 return Fail;
324 }
325
326 return JsonToGap(v);
327 }
328
329 // WARNING: This class is only complete enough to work with
330 // picojson's iterator support.
331 struct GapStringToInputIterator {
332 Obj obj;
333 size_t pos;
334
GapStringToInputIteratorGapStringToInputIterator335 GapStringToInputIterator() : obj(0), pos(0)
336 {
337 }
338
GapStringToInputIteratorGapStringToInputIterator339 GapStringToInputIterator(Obj s, size_t startpos = 0)
340 : obj(s), pos(startpos)
341 {
342 }
343
GapStringToInputIteratorGapStringToInputIterator344 GapStringToInputIterator(const GapStringToInputIterator & gstii)
345 : obj(gstii.obj), pos(gstii.pos)
346 {
347 }
348
operator *GapStringToInputIterator349 char operator*()
350 {
351 return CSTR_STRING(obj)[pos];
352 }
353
operator ++GapStringToInputIterator354 void operator++()
355 {
356 pos++;
357 }
358
operator ==(GapStringToInputIterator & lhs,GapStringToInputIterator & rhs)359 friend bool operator==(GapStringToInputIterator & lhs,
360 GapStringToInputIterator & rhs)
361 {
362 return (lhs.pos == rhs.pos);
363 }
364 };
365
366
endGapStringIterator(Obj obj)367 static GapStringToInputIterator endGapStringIterator(Obj obj)
368 {
369 GapStringToInputIterator g(obj, GET_LEN_STRING(obj));
370 return g;
371 }
372
JSON_STRING_TO_GAP(Obj self,Obj param)373 Obj JSON_STRING_TO_GAP(Obj self, Obj param)
374 {
375 JSON_setupGAPFunctions();
376 CleanupCacheGuard ccg;
377
378 if(!IS_STRING(param))
379 {
380 ErrorQuit("Input to JsonToGap must be a string", 0, 0);
381 }
382
383 Obj real_string = param;
384
385 if(!IS_STRING_REP(param))
386 {
387 real_string = CopyToStringRep(param);
388 }
389
390
391 typedef picojson::value_t<gap_type_traits> gmp_value;
392
393 gmp_value v;
394
395 std::string err;
396 bool ungotc_check = false;
397 GapStringToInputIterator endparse = picojson::parse(
398 v, GapStringToInputIterator(real_string),
399 endGapStringIterator(real_string), &err, &ungotc_check);
400
401 // Char* res = picojson::parse(v, ptr, ptrend, &err, &ungotc_check);
402 if (! err.empty()) {
403 ErrorQuit(err.c_str(), 0, 0);
404 return Fail;
405 }
406
407 // Check end of string
408 Char * ptr = CSTR_STRING(real_string);
409 Char * ptrend = ptr + strlen(ptr);
410
411 // Extra position in the string
412 Char * res = ptr + endparse.pos;
413
414 // Woo, this is horrible. The parser steps one character too far
415 // if the only thing parsed is a number. So step back.
416 if(ungotc_check) {
417 res--;
418 }
419
420 for(; res != ptrend; res++)
421 {
422 if(!(isspace(*res)) && *res)
423 {
424 ErrorMayQuit("Failed to parse end of string: '%s'",(Int)res,0);
425 return Fail;
426 }
427 }
428
429 return JsonToGap(v);
430 }
431
432 typedef Obj (* GVarFunc)(/*arguments*/);
433
434 #define GVAR_FUNC_TABLE_ENTRY(srcfile, name, nparam, params) \
435 {#name, nparam, \
436 params, \
437 (GVarFunc)name, \
438 srcfile ":Func" #name }
439
440 // Table of functions to export
441 static StructGVarFunc GVarFuncs [] = {
442 GVAR_FUNC_TABLE_ENTRY("json.c", JSON_STRING_TO_GAP, 1, "string"),
443 GVAR_FUNC_TABLE_ENTRY("json.c", JSON_ESCAPE_STRING, 1, "string"),
444 GVAR_FUNC_TABLE_ENTRY("json.c", JSON_STREAM_TO_GAP, 1, "string"),
445 { 0 } /* Finish with an empty entry */
446
447 };
448
449 /******************************************************************************
450 *F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
451 */
InitKernel(StructInitInfo * module)452 static Int InitKernel( StructInitInfo *module )
453 {
454 /* init filters and functions */
455 InitHdlrFuncsFromTable( GVarFuncs );
456
457 /* return success */
458 return 0;
459 }
460
461 /******************************************************************************
462 *F InitLibrary( <module> ) . . . . . . . initialise library data structures
463 */
InitLibrary(StructInitInfo * module)464 static Int InitLibrary( StructInitInfo *module )
465 {
466 /* init filters and functions */
467 InitGVarFuncsFromTable( GVarFuncs );
468
469 /* return success */
470 return 0;
471 }
472
473 /******************************************************************************
474 *F InitInfopl() . . . . . . . . . . . . . . . . . table of init functions
475 */
476 static StructInitInfo module = {
477 /* type = */ MODULE_DYNAMIC,
478 /* name = */ "json",
479 /* revision_c = */ 0,
480 /* revision_h = */ 0,
481 /* version = */ 0,
482 /* crc = */ 0,
483 /* initKernel = */ InitKernel,
484 /* initLibrary = */ InitLibrary,
485 /* checkInit = */ 0,
486 /* preSave = */ 0,
487 /* postSave = */ 0,
488 /* postRestore = */ 0
489 };
490
491 extern "C"
Init__Dynamic(void)492 StructInitInfo * Init__Dynamic ( void )
493 {
494 return &module;
495 }
496