]> git.armaanb.net Git - gen-shell.git/blob - src/libshared/src/JSON.cpp
added install instructions
[gen-shell.git] / src / libshared / src / JSON.cpp
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2006 - 2017, Paul Beckingham, Federico Hernandez.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <JSON.h>
29 #include <shared.h>
30 #include <format.h>
31 #include <utf8.h>
32
33 const char *json_encode[] = {
34   "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
35    "\\b",  "\\t",  "\\n", "\x0b",  "\\f",  "\\r", "\x0e", "\x0f",
36   "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
37   "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
38   "\x20", "\x21", "\\\"", "\x23", "\x24", "\x25", "\x26", "\x27",
39   "\x28", "\x29", "\x2a", "\x2b", "\x2c", "\x2d", "\x2e",  "\\/",
40   "\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37",
41   "\x38", "\x39", "\x3a", "\x3b", "\x3c", "\x3d", "\x3e", "\x3f",
42   "\x40", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47",
43   "\x48", "\x49", "\x4a", "\x4b", "\x4c", "\x4d", "\x4e", "\x4f",
44   "\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57",
45   "\x58", "\x59", "\x5a", "\x5b", "\\\\", "\x5d", "\x5e", "\x5f",
46   "\x60", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67",
47   "\x68", "\x69", "\x6a", "\x6b", "\x6c", "\x6d", "\x6e", "\x6f",
48   "\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77",
49   "\x78", "\x79", "\x7a", "\x7b", "\x7c", "\x7d", "\x7e", "\x7f",
50   "\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
51   "\x88", "\x89", "\x8a", "\x8b", "\x8c", "\x8d", "\x8e", "\x8f",
52   "\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
53   "\x98", "\x99", "\x9a", "\x9b", "\x9c", "\x9d", "\x9e", "\x9f",
54   "\xa0", "\xa1", "\xa2", "\xa3", "\xa4", "\xa5", "\xa6", "\xa7",
55   "\xa8", "\xa9", "\xaa", "\xab", "\xac", "\xad", "\xae", "\xaf",
56   "\xb0", "\xb1", "\xb2", "\xb3", "\xb4", "\xb5", "\xb6", "\xb7",
57   "\xb8", "\xb9", "\xba", "\xbb", "\xbc", "\xbd", "\xbe", "\xbf",
58   "\xc0", "\xc1", "\xc2", "\xc3", "\xc4", "\xc5", "\xc6", "\xc7",
59   "\xc8", "\xc9", "\xca", "\xcb", "\xcc", "\xcd", "\xce", "\xcf",
60   "\xd0", "\xd1", "\xd2", "\xd3", "\xd4", "\xd5", "\xd6", "\xd7",
61   "\xd8", "\xd9", "\xda", "\xdb", "\xdc", "\xdd", "\xde", "\xdf",
62   "\xe0", "\xe1", "\xe2", "\xe3", "\xe4", "\xe5", "\xe6", "\xe7",
63   "\xe8", "\xe9", "\xea", "\xeb", "\xec", "\xed", "\xee", "\xef",
64   "\xf0", "\xf1", "\xf2", "\xf3", "\xf4", "\xf5", "\xf6", "\xf7",
65   "\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff"
66 };
67
68 ////////////////////////////////////////////////////////////////////////////////
69 json::value* json::value::parse (Pig& pig)
70 {
71   json::value* v;
72   if ((v = json::object::parse  (pig)) ||
73       (v = json::array::parse   (pig)) ||
74       (v = json::string::parse  (pig)) ||
75       (v = json::number::parse  (pig)) ||
76       (v = json::literal::parse (pig)))
77     return v;
78
79   return NULL;
80 }
81
82 ////////////////////////////////////////////////////////////////////////////////
83 json::jtype json::value::type ()
84 {
85   return json::j_value;
86 }
87
88 ////////////////////////////////////////////////////////////////////////////////
89 std::string json::value::dump () const
90 {
91   return "<value>";
92 }
93
94 ////////////////////////////////////////////////////////////////////////////////
95 json::string::string (const std::string& other)
96 {
97   _data = other;
98 }
99
100 ////////////////////////////////////////////////////////////////////////////////
101 json::string* json::string::parse (Pig& pig)
102 {
103   std::string value;
104   if (pig.getQuoted ('"', value))
105   {
106     json::string* s = new json::string ();
107     s->_data = value;
108     return s;
109   }
110
111   return NULL;
112 }
113
114 ////////////////////////////////////////////////////////////////////////////////
115 json::jtype json::string::type ()
116 {
117   return json::j_string;
118 }
119
120 ////////////////////////////////////////////////////////////////////////////////
121 std::string json::string::dump () const
122 {
123   return std::string ("\"") + _data + '"';
124 }
125
126 ////////////////////////////////////////////////////////////////////////////////
127 json::number* json::number::parse (Pig& pig)
128 {
129   double d;
130   if (pig.getNumber (d))
131   {
132     json::number* s = new json::number ();
133     s->_dvalue = d;
134     return s;
135   }
136
137   return NULL;
138 }
139
140 ////////////////////////////////////////////////////////////////////////////////
141 json::jtype json::number::type ()
142 {
143   return json::j_number;
144 }
145
146 ////////////////////////////////////////////////////////////////////////////////
147 std::string json::number::dump () const
148 {
149   return format (_dvalue);
150 }
151
152 ////////////////////////////////////////////////////////////////////////////////
153 json::number::operator double () const
154 {
155   return _dvalue;
156 }
157
158 ////////////////////////////////////////////////////////////////////////////////
159 json::literal* json::literal::parse (Pig& pig)
160 {
161   if (pig.skipLiteral ("null"))
162   {
163     json::literal* s = new json::literal ();
164     s->_lvalue = nullvalue;
165     return s;
166   }
167   else if (pig.skipLiteral ("false"))
168   {
169     json::literal* s = new json::literal ();
170     s->_lvalue = falsevalue;
171     return s;
172   }
173   else if (pig.skipLiteral ("true"))
174   {
175     json::literal* s = new json::literal ();
176     s->_lvalue = truevalue;
177     return s;
178   }
179
180   return NULL;
181 }
182
183 ////////////////////////////////////////////////////////////////////////////////
184 json::jtype json::literal::type ()
185 {
186   return json::j_literal;
187 }
188
189 ////////////////////////////////////////////////////////////////////////////////
190 std::string json::literal::dump () const
191 {
192        if (_lvalue == nullvalue)  return "null";
193   else if (_lvalue == falsevalue) return "false";
194   else                            return "true";
195 }
196
197 ////////////////////////////////////////////////////////////////////////////////
198 json::array::~array ()
199 {
200   for (auto& i : _data)
201     delete i;
202 }
203
204 ////////////////////////////////////////////////////////////////////////////////
205 json::array* json::array::parse (Pig& pig)
206 {
207   auto checkpoint = pig.cursor ();
208
209   pig.skipWS ();
210   if (pig.skip ('['))
211   {
212     pig.skipWS ();
213
214     json::array* arr = new json::array ();
215
216     json::value* value;
217     if ((value = json::value::parse (pig)))
218     {
219       arr->_data.push_back (value);
220       value = NULL; // Not a leak.  Looks like a leak.
221       pig.skipWS ();
222       while (pig.skip (','))
223       {
224         pig.skipWS ();
225
226         if ((value = json::value::parse (pig)))
227         {
228           arr->_data.push_back (value);
229           pig.skipWS ();
230         }
231         else
232         {
233           delete arr;
234           throw format ("Error: missing value after ',' at position {1}", (int) pig.cursor ());
235         }
236       }
237     }
238
239     if (pig.skip (']'))
240       return arr;
241     else
242       throw format ("Error: missing ']' at position {1}", (int) pig.cursor ());
243
244     delete arr;
245   }
246
247   pig.restoreTo (checkpoint);
248   return NULL;
249 }
250
251 ////////////////////////////////////////////////////////////////////////////////
252 json::jtype json::array::type ()
253 {
254   return json::j_array;
255 }
256
257 ////////////////////////////////////////////////////////////////////////////////
258 std::string json::array::dump () const
259 {
260   std::string output;
261   output += '[';
262
263   for (auto i = _data.begin (); i != _data.end (); ++i)
264   {
265     if (i != _data.begin ())
266       output += ',';
267
268     output += (*i)->dump ();
269   }
270
271   output += ']';
272   return output;
273 }
274
275 ////////////////////////////////////////////////////////////////////////////////
276 json::object::~object ()
277 {
278   for (auto& i : _data)
279     delete i.second;
280 }
281
282 ////////////////////////////////////////////////////////////////////////////////
283 json::object* json::object::parse (Pig& pig)
284 {
285   auto checkpoint = pig.cursor ();
286
287   pig.skipWS ();
288   if (pig.skip ('{'))
289   {
290     pig.skipWS ();
291
292     json::object* obj = new json::object ();
293
294     std::string name;
295     json::value* value;
296     if (json::object::parse_pair (pig, name, value))
297     {
298       obj->_data.insert (std::pair <std::string, json::value*> (name, value));
299       value = NULL; // Not a leak.  Looks like a leak.
300
301       pig.skipWS ();
302       while (pig.skip (','))
303       {
304         pig.skipWS ();
305
306         if (json::object::parse_pair (pig, name, value))
307         {
308           obj->_data.insert (std::pair <std::string, json::value*> (name, value));
309           pig.skipWS ();
310         }
311         else
312         {
313           delete obj;
314           throw format ("Error: missing value after ',' at position {1}", (int) pig.cursor ());
315         }
316       }
317     }
318
319     if (pig.skip ('}'))
320       return obj;
321     else
322       throw format ("Error: missing '}' at position {1}", (int) pig.cursor ());
323
324     delete obj;
325   }
326
327   pig.restoreTo (checkpoint);
328   return NULL;
329 }
330
331 ////////////////////////////////////////////////////////////////////////////////
332 bool json::object::parse_pair (
333   Pig& pig,
334   std::string& name,
335   json::value*& val)
336 {
337   auto checkpoint = pig.cursor ();
338
339   if (pig.getQuoted ('"', name))
340   {
341     pig.skipWS ();
342     if (pig.skip (':'))
343     {
344       pig.skipWS ();
345       if ((val = json::value::parse (pig)))
346         return true;
347       else
348         throw format ("Error: missing value at position {1}", (int) pig.cursor ());
349     }
350     else
351       throw format ("Error: missing ':' at position {1}", (int) pig.cursor ());
352   }
353
354   pig.restoreTo (checkpoint);
355   return false;
356 }
357
358 ////////////////////////////////////////////////////////////////////////////////
359 json::jtype json::object::type ()
360 {
361   return json::j_object;
362 }
363
364 ////////////////////////////////////////////////////////////////////////////////
365 std::string json::object::dump () const
366 {
367   std::string output;
368   output += '{';
369
370   for (auto i = _data.begin (); i != _data.end (); ++i)
371   {
372     if (i != _data.begin ())
373       output += ',';
374
375     output += '"' + i->first + "\":";
376     output += i->second->dump ();
377   }
378
379   output += '}';
380   return output;
381 }
382
383 ////////////////////////////////////////////////////////////////////////////////
384 json::value* json::parse (const std::string& input)
385 {
386   json::value* root = NULL;
387
388   Pig n (input);
389   n.skipWS ();
390
391        if (n.peek () == '{') root = json::object::parse (n);
392   else if (n.peek () == '[') root = json::array::parse (n);
393   else
394     throw format ("Error: expected '{' or '[' at position {1}", (int) n.cursor ());
395
396   // Check for end condition.
397   n.skipWS ();
398   if (!n.eos ())
399   {
400     delete root;
401     throw format ("Error: extra characters found at position {1}", (int) n.cursor ());
402   }
403
404   return root;
405 }
406
407 ////////////////////////////////////////////////////////////////////////////////
408 std::string json::encode (const std::string& input)
409 {
410   std::string output;
411   output.reserve ((input.size () * 6) / 5);  // 20% increase.
412
413   auto last = input.begin ();
414   for (auto i = input.begin (); i != input.end (); ++i)
415   {
416     switch (*i)
417     {
418     // Simple translations.
419     case  '"':
420     case '\\':
421     case  '/':
422     case '\b':
423     case '\f':
424     case '\n':
425     case '\r':
426     case '\t':
427       output.append (last, i);
428       output += json_encode[(unsigned char)(*i)];
429       last = i + 1;
430
431     // Default NOP.
432     }
433   }
434
435   output.append (last, input.end ());
436
437   return output;
438 }
439
440 ////////////////////////////////////////////////////////////////////////////////
441 std::string json::decode (const std::string& input)
442 {
443   std::string output;
444   output.reserve (input.size ());  // Same size.
445
446   size_t pos = 0;
447
448   while (pos < input.length ())
449   {
450     if (input[pos] == '\\')
451     {
452       ++pos;
453       switch (input[pos])
454       {
455         // Simple translations.
456         case '"':  output += '"';  break;
457         case '\\': output += '\\'; break;
458         case '/':  output += '/';  break;
459         case 'b':  output += '\b'; break;
460         case 'f':  output += '\f'; break;
461         case 'n':  output += '\n'; break;
462         case 'r':  output += '\r'; break;
463         case 't':  output += '\t'; break;
464
465         // Compose a UTF8 unicode character.
466         case 'u':
467           output += utf8_character (utf8_codepoint (input.substr (++pos)));
468           pos += 3;
469           break;
470
471         // If it is an unrecognized sequence, do nothing.
472         default:
473           output += '\\';
474           output += input[pos];
475           break;
476       }
477       ++pos;
478     }
479     else
480     {
481       size_t next_backslash = input.find ('\\', pos);
482       output.append (input, pos, next_backslash - pos);
483       pos = next_backslash;
484     }
485   }
486
487   return output;
488 }
489
490 ////////////////////////////////////////////////////////////////////////////////