1#include <string>
2#include <string.h>
3
4#include <Poco/UTF8Encoding.h>
5#include <Poco/NumberParser.h>
6#include <common/JSON.h>
7#include <common/find_symbols.h>
8#include <common/preciseExp10.h>
9
10#include <iostream>
11
12#define JSON_MAX_DEPTH 100
13
14
15POCO_IMPLEMENT_EXCEPTION(JSONException, Poco::Exception, "JSONException")
16
17
18/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
19static UInt64 readUIntText(const char * buf, const char * end)
20{
21 UInt64 x = 0;
22
23 if (buf == end)
24 throw JSONException("JSON: cannot parse unsigned integer: unexpected end of data.");
25
26 while (buf != end)
27 {
28 switch (*buf)
29 {
30 case '+':
31 break;
32 case '0':
33 case '1':
34 case '2':
35 case '3':
36 case '4':
37 case '5':
38 case '6':
39 case '7':
40 case '8':
41 case '9':
42 x *= 10;
43 x += *buf - '0';
44 break;
45 default:
46 return x;
47 }
48 ++buf;
49 }
50
51 return x;
52}
53
54
55/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
56static Int64 readIntText(const char * buf, const char * end)
57{
58 bool negative = false;
59 UInt64 x = 0;
60
61 if (buf == end)
62 throw JSONException("JSON: cannot parse signed integer: unexpected end of data.");
63
64 bool run = true;
65 while (buf != end && run)
66 {
67 switch (*buf)
68 {
69 case '+':
70 break;
71 case '-':
72 negative = true;
73 break;
74 case '0':
75 case '1':
76 case '2':
77 case '3':
78 case '4':
79 case '5':
80 case '6':
81 case '7':
82 case '8':
83 case '9':
84 x *= 10;
85 x += *buf - '0';
86 break;
87 default:
88 run = false;
89 break;
90 }
91 ++buf;
92 }
93
94 return negative ? -x : x;
95}
96
97
98/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
99static double readFloatText(const char * buf, const char * end)
100{
101 bool negative = false;
102 double x = 0;
103 bool after_point = false;
104 double power_of_ten = 1;
105
106 if (buf == end)
107 throw JSONException("JSON: cannot parse floating point number: unexpected end of data.");
108
109 bool run = true;
110 while (buf != end && run)
111 {
112 switch (*buf)
113 {
114 case '+':
115 break;
116 case '-':
117 negative = true;
118 break;
119 case '.':
120 after_point = true;
121 break;
122 case '0':
123 case '1':
124 case '2':
125 case '3':
126 case '4':
127 case '5':
128 case '6':
129 case '7':
130 case '8':
131 case '9':
132 if (after_point)
133 {
134 power_of_ten /= 10;
135 x += (*buf - '0') * power_of_ten;
136 }
137 else
138 {
139 x *= 10;
140 x += *buf - '0';
141 }
142 break;
143 case 'e':
144 case 'E':
145 {
146 ++buf;
147 Int32 exponent = readIntText(buf, end);
148 x *= preciseExp10(exponent);
149
150 run = false;
151 break;
152 }
153 default:
154 run = false;
155 break;
156 }
157 ++buf;
158 }
159 if (negative)
160 x = -x;
161
162 return x;
163}
164
165
166void JSON::checkInit() const
167{
168 if (!(ptr_begin < ptr_end))
169 throw JSONException("JSON: begin >= end.");
170
171 if (level > JSON_MAX_DEPTH)
172 throw JSONException("JSON: too deep.");
173}
174
175
176JSON::ElementType JSON::getType() const
177{
178 switch (*ptr_begin)
179 {
180 case '{':
181 return TYPE_OBJECT;
182 case '[':
183 return TYPE_ARRAY;
184 case 't':
185 case 'f':
186 return TYPE_BOOL;
187 case 'n':
188 return TYPE_NULL;
189 case '-':
190 case '0':
191 case '1':
192 case '2':
193 case '3':
194 case '4':
195 case '5':
196 case '6':
197 case '7':
198 case '8':
199 case '9':
200 return TYPE_NUMBER;
201 case '"':
202 {
203 /// Проверим - это просто строка или name-value pair
204 Pos after_string = skipString();
205 if (after_string < ptr_end && *after_string == ':')
206 return TYPE_NAME_VALUE_PAIR;
207 else
208 return TYPE_STRING;
209 }
210 default:
211 throw JSONException(std::string("JSON: unexpected char ") + *ptr_begin + ", expected one of '{[tfn-0123456789\"'");
212 }
213}
214
215
216void JSON::checkPos(Pos pos) const
217{
218 if (pos >= ptr_end)
219 throw JSONException("JSON: unexpected end of data.");
220}
221
222
223JSON::Pos JSON::skipString() const
224{
225 //std::cerr << "skipString()\t" << data() << std::endl;
226
227 Pos pos = ptr_begin;
228 checkPos(pos);
229 if (*pos != '"')
230 throw JSONException(std::string("JSON: expected \", got ") + *pos);
231 ++pos;
232
233 /// fast path: находим следующую двойную кавычку. Если перед ней нет бэкслеша - значит это конец строки (при допущении корректности JSON).
234 Pos closing_quote = reinterpret_cast<const char *>(memchr(reinterpret_cast<const void *>(pos), '\"', ptr_end - pos));
235 if (nullptr != closing_quote && closing_quote[-1] != '\\')
236 return closing_quote + 1;
237
238 /// slow path
239 while (pos < ptr_end && *pos != '"')
240 {
241 if (*pos == '\\')
242 {
243 ++pos;
244 checkPos(pos);
245 if (*pos == 'u')
246 {
247 pos += 4;
248 checkPos(pos);
249 }
250 }
251 ++pos;
252 }
253
254 checkPos(pos);
255 if (*pos != '"')
256 throw JSONException(std::string("JSON: expected \", got ") + *pos);
257 ++pos;
258
259 return pos;
260}
261
262
263JSON::Pos JSON::skipNumber() const
264{
265 //std::cerr << "skipNumber()\t" << data() << std::endl;
266
267 Pos pos = ptr_begin;
268
269 checkPos(pos);
270 if (*pos == '-')
271 ++pos;
272
273 while (pos < ptr_end && *pos >= '0' && *pos <= '9')
274 ++pos;
275 if (pos < ptr_end && *pos == '.')
276 ++pos;
277 while (pos < ptr_end && *pos >= '0' && *pos <= '9')
278 ++pos;
279 if (pos < ptr_end && (*pos == 'e' || *pos == 'E'))
280 ++pos;
281 if (pos < ptr_end && *pos == '-')
282 ++pos;
283 while (pos < ptr_end && *pos >= '0' && *pos <= '9')
284 ++pos;
285
286 return pos;
287}
288
289
290JSON::Pos JSON::skipBool() const
291{
292 //std::cerr << "skipBool()\t" << data() << std::endl;
293
294 Pos pos = ptr_begin;
295 checkPos(pos);
296
297 if (*ptr_begin == 't')
298 pos += 4;
299 else if (*ptr_begin == 'f')
300 pos += 5;
301 else
302 throw JSONException("JSON: expected true or false.");
303
304 return pos;
305}
306
307
308JSON::Pos JSON::skipNull() const
309{
310 //std::cerr << "skipNull()\t" << data() << std::endl;
311
312 return ptr_begin + 4;
313}
314
315
316JSON::Pos JSON::skipNameValuePair() const
317{
318 //std::cerr << "skipNameValuePair()\t" << data() << std::endl;
319
320 Pos pos = skipString();
321 checkPos(pos);
322
323 if (*pos != ':')
324 throw JSONException("JSON: expected :.");
325 ++pos;
326
327 return JSON(pos, ptr_end, level + 1).skipElement();
328
329}
330
331
332JSON::Pos JSON::skipArray() const
333{
334 //std::cerr << "skipArray()\t" << data() << std::endl;
335
336 if (!isArray())
337 throw JSONException("JSON: expected [");
338 Pos pos = ptr_begin;
339 ++pos;
340 checkPos(pos);
341 if (*pos == ']')
342 return ++pos;
343
344 while (1)
345 {
346 pos = JSON(pos, ptr_end, level + 1).skipElement();
347
348 checkPos(pos);
349
350 switch (*pos)
351 {
352 case ',':
353 ++pos;
354 break;
355 case ']':
356 return ++pos;
357 default:
358 throw JSONException(std::string("JSON: expected one of ',]', got ") + *pos);
359 }
360 }
361}
362
363
364JSON::Pos JSON::skipObject() const
365{
366 //std::cerr << "skipObject()\t" << data() << std::endl;
367
368 if (!isObject())
369 throw JSONException("JSON: expected {");
370 Pos pos = ptr_begin;
371 ++pos;
372 checkPos(pos);
373 if (*pos == '}')
374 return ++pos;
375
376 while (1)
377 {
378 pos = JSON(pos, ptr_end, level + 1).skipNameValuePair();
379
380 checkPos(pos);
381
382 switch (*pos)
383 {
384 case ',':
385 ++pos;
386 break;
387 case '}':
388 return ++pos;
389 default:
390 throw JSONException(std::string("JSON: expected one of ',}', got ") + *pos);
391 }
392 }
393}
394
395
396JSON::Pos JSON::skipElement() const
397{
398 //std::cerr << "skipElement()\t" << data() << std::endl;
399
400 ElementType type = getType();
401
402 switch(type)
403 {
404 case TYPE_NULL:
405 return skipNull();
406 case TYPE_BOOL:
407 return skipBool();
408 case TYPE_NUMBER:
409 return skipNumber();
410 case TYPE_STRING:
411 return skipString();
412 case TYPE_NAME_VALUE_PAIR:
413 return skipNameValuePair();
414 case TYPE_ARRAY:
415 return skipArray();
416 case TYPE_OBJECT:
417 return skipObject();
418 default:
419 throw JSONException("Logical error in JSON: unknown element type: " + std::to_string(type));
420 }
421}
422
423size_t JSON::size() const
424{
425 size_t i = 0;
426
427 for (const_iterator it = begin(); it != end(); ++it)
428 ++i;
429
430 return i;
431}
432
433
434bool JSON::empty() const
435{
436 return size() == 0;
437}
438
439
440JSON JSON::operator[] (size_t n) const
441{
442 ElementType type = getType();
443
444 if (type != TYPE_ARRAY)
445 throw JSONException("JSON: not array when calling operator[](size_t) method.");
446
447 Pos pos = ptr_begin;
448 ++pos;
449 checkPos(pos);
450
451 size_t i = 0;
452 const_iterator it = begin();
453 while (i < n && it != end())
454 ++it, ++i;
455
456 if (i != n)
457 throw JSONException("JSON: array index " + std::to_string(n) + " out of bounds.");
458
459 return *it;
460}
461
462
463JSON::Pos JSON::searchField(const char * data, size_t size) const
464{
465 ElementType type = getType();
466
467 if (type != TYPE_OBJECT)
468 throw JSONException("JSON: not object when calling operator[](const char *) or has(const char *) method.");
469
470 const_iterator it = begin();
471 for (; it != end(); ++it)
472 {
473 if (!it->hasEscapes())
474 {
475 if (static_cast<int>(size) + 2 > it->dataEnd() - it->data())
476 continue;
477 if (!strncmp(data, it->data() + 1, size))
478 break;
479 }
480 else
481 {
482 std::string current_name = it->getName();
483 if (current_name.size() == size && 0 == memcmp(current_name.data(), data, size))
484 break;
485 }
486 }
487
488 if (it == end())
489 return nullptr;
490 else
491 return it->data();
492}
493
494
495bool JSON::hasEscapes() const
496{
497 Pos pos = ptr_begin + 1;
498 while (pos < ptr_end && *pos != '"' && *pos != '\\')
499 ++pos;
500
501 if (*pos == '"')
502 return false;
503 else if (*pos == '\\')
504 return true;
505 throw JSONException("JSON: unexpected end of data.");
506}
507
508
509bool JSON::hasSpecialChars() const
510{
511 Pos pos = ptr_begin + 1;
512 while (pos < ptr_end && *pos != '"'
513 && *pos != '\\' && *pos != '\r' && *pos != '\n' && *pos != '\t'
514 && *pos != '\f' && *pos != '\b' && *pos != '\0' && *pos != '\'')
515 ++pos;
516
517 if (*pos == '"')
518 return false;
519 else if (pos < ptr_end)
520 return true;
521 throw JSONException("JSON: unexpected end of data.");
522}
523
524
525JSON JSON::operator[] (const std::string & name) const
526{
527 Pos pos = searchField(name);
528 if (!pos)
529 throw JSONException("JSON: there is no element '" + std::string(name) + "' in object.");
530
531 return JSON(pos, ptr_end, level + 1).getValue();
532}
533
534
535bool JSON::has(const char * data, size_t size) const
536{
537 return nullptr != searchField(data, size);
538}
539
540
541double JSON::getDouble() const
542{
543 return readFloatText(ptr_begin, ptr_end);
544}
545
546Int64 JSON::getInt() const
547{
548 return readIntText(ptr_begin, ptr_end);
549}
550
551UInt64 JSON::getUInt() const
552{
553 return readUIntText(ptr_begin, ptr_end);
554}
555
556bool JSON::getBool() const
557{
558 if (*ptr_begin == 't')
559 return true;
560 if (*ptr_begin == 'f')
561 return false;
562 throw JSONException("JSON: cannot parse boolean.");
563}
564
565std::string JSON::getString() const
566{
567 Pos s = ptr_begin;
568
569 if (*s != '"')
570 throw JSONException(std::string("JSON: expected \", got ") + *s);
571 ++s;
572 checkPos(s);
573
574 std::string buf;
575 do
576 {
577 Pos p = find_first_symbols<'\\','"'>(s, ptr_end);
578 if (p >= ptr_end)
579 {
580 break;
581 }
582 buf.append(s, p);
583 s = p;
584 switch (*s)
585 {
586 case '\\':
587 ++s;
588 checkPos(s);
589
590 switch(*s)
591 {
592 case '"':
593 buf += '"';
594 break;
595 case '\\':
596 buf += '\\';
597 break;
598 case '/':
599 buf += '/';
600 break;
601 case 'b':
602 buf += '\b';
603 break;
604 case 'f':
605 buf += '\f';
606 break;
607 case 'n':
608 buf += '\n';
609 break;
610 case 'r':
611 buf += '\r';
612 break;
613 case 't':
614 buf += '\t';
615 break;
616 case 'u':
617 {
618 Poco::UTF8Encoding utf8;
619
620 ++s;
621 checkPos(s + 4);
622 std::string hex(s, 4);
623 s += 3;
624 int unicode;
625 try
626 {
627 unicode = Poco::NumberParser::parseHex(hex);
628 }
629 catch (const Poco::SyntaxException & e)
630 {
631 throw JSONException("JSON: incorrect syntax: incorrect HEX code.");
632 }
633 buf.resize(buf.size() + 6); /// максимальный размер UTF8 многобайтовой последовательности
634 int res = utf8.convert(unicode,
635 reinterpret_cast<unsigned char *>(const_cast<char*>(buf.data())) + buf.size() - 6, 6);
636 if (!res)
637 throw JSONException("JSON: cannot convert unicode " + std::to_string(unicode)
638 + " to UTF8.");
639 buf.resize(buf.size() - 6 + res);
640 break;
641 }
642 default:
643 buf += *s;
644 break;
645 }
646 ++s;
647 break;
648 case '"':
649 return buf;
650 default:
651 throw JSONException("find_first_symbols<...>() failed in unexpected way");
652 }
653 } while (s < ptr_end);
654 throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
655}
656
657std::string JSON::getName() const
658{
659 return getString();
660}
661
662StringRef JSON::getRawString() const
663{
664 Pos s = ptr_begin;
665 if (*s != '"')
666 throw JSONException(std::string("JSON: expected \", got ") + *s);
667 while (++s != ptr_end && *s != '"');
668 if (s != ptr_end )
669 return StringRef(ptr_begin + 1, s - ptr_begin - 1);
670 throw JSONException("JSON: incorrect syntax (expected end of string, found end of JSON).");
671}
672
673StringRef JSON::getRawName() const
674{
675 return getRawString();
676}
677
678JSON JSON::getValue() const
679{
680 Pos pos = skipString();
681 checkPos(pos);
682 if (*pos != ':')
683 throw JSONException("JSON: expected :.");
684 ++pos;
685 checkPos(pos);
686 return JSON(pos, ptr_end, level + 1);
687}
688
689
690double JSON::toDouble() const
691{
692 ElementType type = getType();
693
694 if (type == TYPE_NUMBER)
695 return getDouble();
696 else if (type == TYPE_STRING)
697 return JSON(ptr_begin + 1, ptr_end, level + 1).getDouble();
698 else
699 throw JSONException("JSON: cannot convert value to double.");
700}
701
702Int64 JSON::toInt() const
703{
704 ElementType type = getType();
705
706 if (type == TYPE_NUMBER)
707 return getInt();
708 else if (type == TYPE_STRING)
709 return JSON(ptr_begin + 1, ptr_end, level + 1).getInt();
710 else
711 throw JSONException("JSON: cannot convert value to signed integer.");
712}
713
714UInt64 JSON::toUInt() const
715{
716 ElementType type = getType();
717
718 if (type == TYPE_NUMBER)
719 return getUInt();
720 else if (type == TYPE_STRING)
721 return JSON(ptr_begin + 1, ptr_end, level + 1).getUInt();
722 else
723 throw JSONException("JSON: cannot convert value to unsigned integer.");
724}
725
726std::string JSON::toString() const
727{
728 ElementType type = getType();
729
730 if (type == TYPE_STRING)
731 return getString();
732 else
733 {
734 Pos pos = skipElement();
735 return std::string(ptr_begin, pos - ptr_begin);
736 }
737}
738
739
740JSON::iterator JSON::iterator::begin() const
741{
742 ElementType type = getType();
743
744 if (type != TYPE_ARRAY && type != TYPE_OBJECT)
745 throw JSONException("JSON: not array or object when calling begin() method.");
746
747 //std::cerr << "begin()\t" << data() << std::endl;
748
749 Pos pos = ptr_begin + 1;
750 checkPos(pos);
751 if (*pos == '}' || *pos == ']')
752 return end();
753
754 return JSON(pos, ptr_end, level + 1);
755}
756
757JSON::iterator JSON::iterator::end() const
758{
759 return JSON(nullptr, ptr_end, level + 1);
760}
761
762JSON::iterator & JSON::iterator::operator++()
763{
764 Pos pos = skipElement();
765 checkPos(pos);
766
767 if (*pos != ',')
768 ptr_begin = nullptr;
769 else
770 {
771 ++pos;
772 checkPos(pos);
773 ptr_begin = pos;
774 }
775
776 return *this;
777}
778
779JSON::iterator JSON::iterator::operator++(int)
780{
781 iterator copy(*this);
782 ++*this;
783 return copy;
784}
785
786template <>
787double JSON::get<double>() const
788{
789 return getDouble();
790}
791
792template <>
793std::string JSON::get<std::string>() const
794{
795 return getString();
796}
797
798template <>
799Int64 JSON::get<Int64>() const
800{
801 return getInt();
802}
803
804template <>
805UInt64 JSON::get<UInt64>() const
806{
807 return getUInt();
808}
809
810template <>
811bool JSON::get<bool>() const
812{
813 return getBool();
814}
815
816template <>
817bool JSON::isType<std::string>() const
818{
819 return isString();
820}
821
822template <>
823bool JSON::isType<UInt64>() const
824{
825 return isNumber();
826}
827
828template <>
829bool JSON::isType<Int64>() const
830{
831 return isNumber();
832}
833
834template <>
835bool JSON::isType<bool>() const
836{
837 return isBool();
838}
839
840