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