Jamoma API  0.6.0.a19
JSONPreparse.cpp
1 /*
2  * JSONPreparse.cpp
3  * TestSuite
4  *
5  * Created by Wallace on 4/13/11.
6  * Copyright 2011 Streamwide. All rights reserved.
7  *
8  */
9 
10 #include "JSONPreparse.h"
11 
12 #if (defined(JSON_PREPARSE) && defined(JSON_READ_PRIORITY))
13 
14 #ifdef JSON_COMMENTS
15  json_string extractComment(json_string::const_iterator & ptr, json_string::const_iterator & end);
16  json_string extractComment(json_string::const_iterator & ptr, json_string::const_iterator & end){
17  json_string::const_iterator start;
18  json_string result;
19 looplabel:
20  if (json_unlikely(((ptr != end) && (*ptr == JSON_TEMP_COMMENT_IDENTIFIER)))){
21  start = ++ptr;
22  for(; (ptr != end) && (*(ptr) != JSON_TEMP_COMMENT_IDENTIFIER); ++ptr){}
23  result += json_string(start, ptr);
24  if (json_unlikely(ptr == end)) return result;
25  ++ptr;
26  if (json_unlikely(((ptr != end) && (*ptr == JSON_TEMP_COMMENT_IDENTIFIER)))){
27  result += JSON_TEXT('\n');
28  goto looplabel;
29  }
30  }
31  return result;
32  }
33  #define GET_COMMENT(x, y, name) json_string name = extractComment(x, y)
34  #define RETURN_NODE(node, name){\
35  JSONNode res = node;\
36  res.set_comment(name);\
37  return res;\
38  }
39  #define RETURN_NODE_NOCOPY(node, name){\
40  node.set_comment(name);\
41  return node;\
42  }
43  #define SET_COMMENT(node, name) node.set_comment(name)
44  #define COMMENT_ARG(name) ,name
45 #else
46  #define GET_COMMENT(x, y, name) (void)0
47  #define RETURN_NODE(node, name) return node
48  #define RETURN_NODE_NOCOPY(node, name) return node
49  #define SET_COMMENT(node, name) (void)0
50  #define COMMENT_ARG(name)
51 #endif
52 
53 inline bool isHex(json_char c) json_pure;
54 inline bool isHex(json_char c) json_nothrow {
55  return (((c >= JSON_TEXT('0')) && (c <= JSON_TEXT('9'))) ||
56  ((c >= JSON_TEXT('A')) && (c <= JSON_TEXT('F'))) ||
57  ((c >= JSON_TEXT('a')) && (c <= JSON_TEXT('f'))));
58 }
59 
60 #ifdef JSON_STRICT
61  #include "NumberToString.h"
62 #endif
63 
64 json_number FetchNumber(const json_string & _string) json_nothrow;
65 json_number FetchNumber(const json_string & _string) json_nothrow {
66  #ifdef JSON_STRICT
67  return NumberToString::_atof(_string.c_str());
68  #else
69  #ifdef JSON_UNICODE
70  const size_t len = _string.length();
71  #if defined(_MSC_VER) && defined(JSON_SAFE)
72  const size_t bytes = (len * (sizeof(json_char) / sizeof(char))) + 1;
73  json_auto<char> temp(bytes);
74  size_t res;
75  errno_t err = std::wcstombs_s(&res, temp.ptr, bytes, _string.c_str(), len);
76  if (err != 0){
77  return (json_number)0.0;
78  }
79  #elif defined(JSON_SAFE)
80  const size_t bytes = (len * (sizeof(json_char) / sizeof(char))) + 1;
81  json_auto<char> temp(bytes);
82  size_t res = std::wcstombs(temp.ptr, _string.c_str(), len);
83  if (res == (size_t)-1){ //-1 is error code for this function
84  return (json_number)0.0;
85  }
86  #else
87  json_auto<char> temp(len + 1);
88  size_t res = std::wcstombs(temp.ptr, _string.c_str(), len);
89  #endif
90  temp.ptr[res] = JSON_TEXT('\0');
91  return (json_number)std::atof(temp.ptr);
92  #else
93  return (json_number)std::atof(_string.c_str());
94  #endif
95  #endif
96 }
97 
98 JSONNode JSONPreparse::isValidNumber(json_string::const_iterator & ptr, json_string::const_iterator & end){
99  //ptr points at the first character in the number
100  //ptr will end up past the last character
101  json_string::const_iterator start = ptr;
102  bool decimal = false;
103  bool scientific = false;
104 
105  //first letter is weird
106  switch(*ptr){
107  #ifndef JSON_STRICT
108  case JSON_TEXT('.'):
109  decimal = true;
110  break;
111  case JSON_TEXT('+'):
112  #endif
113 
114  case JSON_TEXT('-'):
115  case JSON_TEXT('1'):
116  case JSON_TEXT('2'):
117  case JSON_TEXT('3'):
118  case JSON_TEXT('4'):
119  case JSON_TEXT('5'):
120  case JSON_TEXT('6'):
121  case JSON_TEXT('7'):
122  case JSON_TEXT('8'):
123  case JSON_TEXT('9'):
124  break;
125  case JSON_TEXT('0'):
126  ++ptr;
127  switch(*ptr){
128  case JSON_TEXT('.'):
129  decimal = true;
130  break;
131  case JSON_TEXT('e'):
132  case JSON_TEXT('E'):
133  scientific = true;
134  ++ptr;
135  if (ptr == end) throw false;
136  switch(*ptr){
137  case JSON_TEXT('-'):
138  case JSON_TEXT('+'):
139  case JSON_TEXT('0'):
140  case JSON_TEXT('1'):
141  case JSON_TEXT('2'):
142  case JSON_TEXT('3'):
143  case JSON_TEXT('4'):
144  case JSON_TEXT('5'):
145  case JSON_TEXT('6'):
146  case JSON_TEXT('7'):
147  case JSON_TEXT('8'):
148  case JSON_TEXT('9'):
149  break;
150  default:
151  throw false;
152  }
153  break;
154 
155  #ifndef JSON_STRICT
156  case JSON_TEXT('x'):
157  while(isHex(*++ptr)){};
158  return JSONNode(json_global(EMPTY_JSON_STRING), FetchNumber(json_string(start, end - 1)));
159  #ifdef JSON_OCTAL
160  #ifdef __GNUC__
161  case JSON_TEXT('0') ... JSON_TEXT('7'): //octal
162  #else
163  case JSON_TEXT('0'):
164  case JSON_TEXT('1'):
165  case JSON_TEXT('2'):
166  case JSON_TEXT('3'):
167  case JSON_TEXT('4'):
168  case JSON_TEXT('5'):
169  case JSON_TEXT('6'):
170  case JSON_TEXT('7'):
171  #endif
172  while((*++ptr >= JSON_TEXT('0')) && (*ptr <= JSON_TEXT('7'))){};
173  if ((*ptr != JSON_TEXT('8')) && (*ptr != JSON_TEXT('9'))){
174  return JSONNode(json_global(EMPTY_JSON_STRING), FetchNumber(json_string(start, ptr - 1)));
175  }
176  throw false;
177  case JSON_TEXT('8'):
178  case JSON_TEXT('9'):
179  break;
180  #else
181  #ifdef __GNUC__
182  case JSON_TEXT('0') ... JSON_TEXT('9'):
183  #else
184  case JSON_TEXT('0'):
185  case JSON_TEXT('1'):
186  case JSON_TEXT('2'):
187  case JSON_TEXT('3'):
188  case JSON_TEXT('4'):
189  case JSON_TEXT('5'):
190  case JSON_TEXT('6'):
191  case JSON_TEXT('7'):
192  case JSON_TEXT('8'):
193  case JSON_TEXT('9'):
194  #endif
195  break;
196  #endif
197  #else
198  #ifdef __GNUC__
199  case JSON_TEXT('0') ... JSON_TEXT('9'):
200  #else
201  case JSON_TEXT('0'):
202  case JSON_TEXT('1'):
203  case JSON_TEXT('2'):
204  case JSON_TEXT('3'):
205  case JSON_TEXT('4'):
206  case JSON_TEXT('5'):
207  case JSON_TEXT('6'):
208  case JSON_TEXT('7'):
209  case JSON_TEXT('8'):
210  case JSON_TEXT('9'):
211  #endif
212  break;
213  #endif
214  default: //just a 0
215  return JSONNode(json_global(EMPTY_JSON_STRING), FetchNumber(json_string(start, ptr - 1)));;
216  }
217  break;
218  default:
219  throw false;
220  }
221  ++ptr;
222 
223  //next digits
224  while (true){
225  switch(*ptr){
226  case JSON_TEXT('.'):
227  if (json_unlikely(decimal)) throw false; //multiple decimals
228  if (json_unlikely(scientific)) throw false;
229  decimal = true;
230  break;
231  case JSON_TEXT('e'):
232  case JSON_TEXT('E'):
233  if (json_likely(scientific)) throw false;
234  scientific = true;
235  ++ptr;
236  switch(*ptr){
237  case JSON_TEXT('-'):
238  case JSON_TEXT('+'):
239  #ifdef __GNUC__
240  case JSON_TEXT('0') ... JSON_TEXT('9'):
241  #else
242  case JSON_TEXT('0'):
243  case JSON_TEXT('1'):
244  case JSON_TEXT('2'):
245  case JSON_TEXT('3'):
246  case JSON_TEXT('4'):
247  case JSON_TEXT('5'):
248  case JSON_TEXT('6'):
249  case JSON_TEXT('7'):
250  case JSON_TEXT('8'):
251  case JSON_TEXT('9'):
252  #endif
253  break;
254  default:
255  throw false;
256  }
257  break;
258  #ifdef __GNUC__
259  case JSON_TEXT('0') ... JSON_TEXT('9'):
260  #else
261  case JSON_TEXT('0'):
262  case JSON_TEXT('1'):
263  case JSON_TEXT('2'):
264  case JSON_TEXT('3'):
265  case JSON_TEXT('4'):
266  case JSON_TEXT('5'):
267  case JSON_TEXT('6'):
268  case JSON_TEXT('7'):
269  case JSON_TEXT('8'):
270  case JSON_TEXT('9'):
271  #endif
272  break;
273  default:
274  return JSONNode(json_global(EMPTY_JSON_STRING), FetchNumber(json_string(start, ptr)));;
275  }
276  ++ptr;
277  }
278  throw false;
279 }
280 
281 #ifndef JSON_STRICT
282  #define LETTERCASE(x, y)\
283  case JSON_TEXT(x):\
284  case JSON_TEXT(y)
285  #define LETTERCHECK(x, y)\
286  if (json_unlikely((*++ptr != JSON_TEXT(x)) && (*ptr != JSON_TEXT(y)))) throw false
287 #else
288  #define LETTERCASE(x, y)\
289  case JSON_TEXT(x)
290  #define LETTERCHECK(x, y)\
291  if (json_unlikely(*++ptr != JSON_TEXT(x))) throw false
292 #endif
293 JSONNode JSONPreparse::isValidMember(json_string::const_iterator & ptr, json_string::const_iterator & end){
294  //ptr is on the first character of the member
295  //ptr will end up immediately after the last character in the member
296  if (ptr == end) throw false;
297 
298  switch(*ptr){
299  case JSON_TEXT('\"'):{
300  return JSONNode::stringType(isValidString(++ptr, end));
301  }
302  case JSON_TEXT('{'):
303  return isValidObject(++ptr, end);
304  case JSON_TEXT('['):
305  return isValidArray(++ptr, end);
306  LETTERCASE('t', 'T'):
307  LETTERCHECK('r', 'R');
308  LETTERCHECK('u', 'U');
309  LETTERCHECK('e', 'E');
310  ++ptr;
311  return JSONNode(json_global(EMPTY_JSON_STRING), true);
312  LETTERCASE('f', 'F'):
313  LETTERCHECK('a', 'A');
314  LETTERCHECK('l', 'L');
315  LETTERCHECK('s', 'S');
316  LETTERCHECK('e', 'E');
317  ++ptr;
318  return JSONNode(json_global(EMPTY_JSON_STRING), false);
319  LETTERCASE('n', 'N'):
320  LETTERCHECK('u', 'U');
321  LETTERCHECK('l', 'L');
322  LETTERCHECK('l', 'L');
323  ++ptr;
324  return JSONNode(JSON_NULL);
325  #ifndef JSON_STRICT
326  case JSON_TEXT('}'): //null in libjson
327  case JSON_TEXT(']'): //null in libjson
328  case JSON_TEXT(','): //null in libjson
329  return JSONNode(JSON_NULL);
330  #endif
331  }
332  //a number
333  return isValidNumber(ptr, end);
334 }
335 
336 json_string JSONPreparse::isValidString(json_string::const_iterator & ptr, json_string::const_iterator & end){
337  //ptr is pointing to the first character after the quote
338  //ptr will end up behind the closing "
339  json_string::const_iterator start = ptr;
340 
341  while(ptr != end){
342  switch(*ptr){
343  case JSON_TEXT('\\'):
344  switch(*(++ptr)){
345  case JSON_TEXT('\"'):
346  case JSON_TEXT('\\'):
347  case JSON_TEXT('/'):
348  case JSON_TEXT('b'):
349  case JSON_TEXT('f'):
350  case JSON_TEXT('n'):
351  case JSON_TEXT('r'):
352  case JSON_TEXT('t'):
353  break;
354  case JSON_TEXT('u'):
355  if (json_unlikely(!isHex(*++ptr))) throw false;
356  if (json_unlikely(!isHex(*++ptr))) throw false;
357  //fallthrough to \x
358  #ifndef JSON_STRICT
359  case JSON_TEXT('x'): //hex
360  #endif
361  if (json_unlikely(!isHex(*++ptr))) throw false;
362  if (json_unlikely(!isHex(*++ptr))) throw false;
363  break;
364  #ifndef JSON_OCTAL
365  #ifdef __GNUC__
366  case JSON_TEXT('0') ... JSON_TEXT('7'): //octal
367  #else
368  case JSON_TEXT('0'):
369  case JSON_TEXT('1'):
370  case JSON_TEXT('2'):
371  case JSON_TEXT('3'):
372  case JSON_TEXT('4'):
373  case JSON_TEXT('5'):
374  case JSON_TEXT('6'):
375  case JSON_TEXT('7'):
376  #endif
377  if (json_unlikely((*++ptr < JSON_TEXT('0')) || (*ptr > JSON_TEXT('7')))) throw false;
378  if (json_unlikely((*++ptr < JSON_TEXT('0')) || (*ptr > JSON_TEXT('7')))) throw false;
379  break;
380  #endif
381  default:
382  throw false;
383  }
384  break;
385  case JSON_TEXT('\"'):
386  return json_string(start, ptr++);
387  }
388  ++ptr;
389  }
390  throw false;
391 }
392 
393 void JSONPreparse::isValidNamedObject(json_string::const_iterator & ptr, json_string::const_iterator & end, JSONNode & parent COMMENT_PARAM(comment)) {
394  //ptr should be right before the string name
395  {
396  json_string _name = isValidString(++ptr, end);
397  if (json_unlikely(*ptr++ != JSON_TEXT(':'))) throw false;
398  JSONNode res = isValidMember(ptr, end);
399  res.set_name_(_name);
400  SET_COMMENT(res, comment);
401  #ifdef JSON_LIBRARY
402  parent.push_back(&res);
403  #else
404  parent.push_back(res);
405  #endif
406  }
407  if (ptr == end) throw false;
408  switch(*ptr){
409  case JSON_TEXT(','):
410  ++ptr;
411  {
412  GET_COMMENT(ptr, end, nextcomment);
413  isValidNamedObject(ptr, end, parent COMMENT_ARG(nextcomment)); //will handle all of them
414  }
415  return;
416  case JSON_TEXT('}'):
417  ++ptr;
418  return;
419  default:
420  throw false;
421  }
422 }
423 
424 JSONNode JSONPreparse::isValidObject(json_string::const_iterator & ptr, json_string::const_iterator & end) {
425  //ptr should currently be pointing past the {, so this must be the start of a name, or the closing }
426  //ptr will end up past the last }
427  JSONNode res(JSON_NODE);
428  GET_COMMENT(ptr, end, comment);
429  switch(*ptr){
430  case JSON_TEXT('\"'):
431  isValidNamedObject(ptr, end, res COMMENT_ARG(comment));
432  return res;
433  case JSON_TEXT('}'):
434  ++ptr;
435  return res;
436  default:
437  throw false;
438  }
439 }
440 
441 void pushArrayMember(JSONNode & res, json_string::const_iterator & ptr, json_string::const_iterator & end);
442 void pushArrayMember(JSONNode & res, json_string::const_iterator & ptr, json_string::const_iterator & end){
443  GET_COMMENT(ptr, end, comment);
444  JSONNode temp = JSONPreparse::isValidMember(ptr, end);
445  SET_COMMENT(temp, comment);
446  #ifdef JSON_LIBRARY
447  res.push_back(&temp);
448  #else
449  res.push_back(temp);
450  #endif
451 }
452 
453 JSONNode JSONPreparse::isValidArray(json_string::const_iterator & ptr, json_string::const_iterator & end) {
454  //ptr should currently be pointing past the [, so this must be the start of a member, or the closing ]
455  //ptr will end up past the last ]
456  JSONNode res(JSON_ARRAY);
457  do{
458  switch(*ptr){
459  case JSON_TEXT(']'):
460  ++ptr;
461  return res;
462  default:
463  pushArrayMember(res, ptr, end);
464  switch(*ptr){
465  case JSON_TEXT(','):
466  break;
467  case JSON_TEXT(']'):
468  ++ptr;
469  return res;
470  default:
471  throw false;
472  }
473  break;
474  }
475  } while (++ptr != end);
476  throw false;
477 }
478 
479 JSONNode JSONPreparse::isValidRoot(const json_string & json) json_throws(std::invalid_argument) {
480  json_string::const_iterator it = json.begin();
481  json_string::const_iterator end = json.end();
482  try {
483  GET_COMMENT(it, end, comment);
484  switch(*it){
485  case JSON_TEXT('{'):
486  RETURN_NODE(isValidObject(++it, end), comment);
487  case JSON_TEXT('['):
488  RETURN_NODE(isValidArray(++it, end), comment);
489  }
490  } catch (...){}
491 
492  #ifndef JSON_NO_EXCEPTIONS
493  throw std::invalid_argument(json_global(EMPTY_STD_STRING));
494  #else
495  return JSONNode(JSON_NULL);
496  #endif
497 }
498 
499 #endif