1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef LUMEJSON_H
17 #define LUMEJSON_H
18
19 #if defined __has_include
20 #if __has_include(<charconv>)
21 #define HAS_CHARCONV 1
22 #include <charconv>
23 #endif
24 #endif
25
26 #ifndef JSON_STRING_TYPE
27 #include <string_view>
28 #endif // JSON_STRING_TYPE
29 #ifndef JSON_ARRAY_TYPE
30 #include <vector>
31 #endif // JSON_ARRAY_TYPE
32
33 #include <string>
34
35 namespace json {
36 #ifndef JSON_STRING_TYPE
37 using string_t = std::string_view;
38 #else
39 using string_t = JSON_STRING_TYPE;
40 #endif
41
42 #ifndef JSON_ARRAY_TYPE
43 template<typename T>
44 using array_t = std::vector<T>;
45 #else
46 template<typename T>
47 using array_t = JSON_ARRAY_TYPE<T>;
48 #endif
49
50 /// simple json parser parser
51 enum class type : char {
52 uninitialized = 0,
53 object,
54 array,
55 string,
56 number,
57 boolean,
58 null,
59 };
60
61 struct value;
62
63 // parses 'data' and writes the corresponding JSON structure to 'value'
64 char* parse_value(char* data, value& value);
65
66 // parses 'data' and return JSON structure. the value::type will be 'uninitialized' if parsing failed.
67 value parse(char* data);
68
69 std::string to_string(const value& value);
70
71 struct pair;
72
73 struct null {};
74 using object = array_t<pair>;
75 using array = array_t<value>;
76 using string = string_t;
77
78 struct value {
79 type type{ type::uninitialized };
80 union {
81 object object_;
82 array array_;
83 string string_;
84 double number_;
85 bool boolean_;
86 };
87
valuevalue88 value() : type{ type::uninitialized } {}
valuevalue89 value(object&& value) : type{ type::object }, object_(std::move(value)) {}
valuevalue90 value(array&& value) : type{ type::array }, array_(std::move(value)) {}
valuevalue91 value(string value) : type{ type::string }, string_(value) {}
valuevalue92 value(double value) : type{ type::number }, number_(value) {}
valuevalue93 explicit value(bool value) : type{ type::boolean }, boolean_(value) {}
valuevalue94 value(null value) : type{ type::null } {}
95
96 template<typename T, std::enable_if_t<!std::is_same_v<T, bool> && std::is_arithmetic_v<T>, bool> = true>
valuevalue97 value(T value) : type{ type::number }, number_(static_cast<double>(value))
98 {}
99
100 template<typename T>
valuevalue101 value(array_t<T> values) : type{ type::array }, array_(array{})
102 {
103 array_.reserve(values.size());
104 for (const auto& value : values) {
105 array_.push_back({ value });
106 }
107 }
108
109 template<typename T, size_t N>
valuevalue110 value(T (&value)[N]) : type{ type::array }, array_(array{})
111 {
112 array_.reserve(N);
113 for (size_t i = 0; i < N; ++i) {
114 array_.push_back({ value[i] });
115 }
116 }
117
valuevalue118 value(value&& rhs) : type{ std::exchange(rhs.type, type::uninitialized) }
119 {
120 switch (type) {
121 case type::uninitialized:
122 break;
123 case type::object:
124 new (&object_) object(std::move(rhs.object_));
125 break;
126 case type::array:
127 new (&array_) array(std::move(rhs.array_));
128 break;
129 case type::string:
130 new (&string_) string_t(std::move(rhs.string_));
131 break;
132 case type::number:
133 number_ = rhs.number_;
134 break;
135 case type::boolean:
136 boolean_ = rhs.boolean_;
137 break;
138 case type::null:
139 break;
140 default:
141 break;
142 }
143 }
144
145 value& operator=(value&& rhs)
146 {
147 if (this != &rhs) {
148 cleanup();
149 type = std::exchange(rhs.type, type::uninitialized);
150 switch (type) {
151 case type::uninitialized:
152 break;
153 case type::object:
154 new (&object_) object(std::move(rhs.object_));
155 break;
156 case type::array:
157 new (&array_) array(std::move(rhs.array_));
158 break;
159 case type::string:
160 new (&string_) string_t(std::move(rhs.string_));
161 break;
162 case type::number:
163 number_ = rhs.number_;
164 break;
165 case type::boolean:
166 boolean_ = rhs.boolean_;
167 break;
168 case type::null:
169 break;
170 default:
171 break;
172 }
173 }
174 return *this;
175 }
176
~valuevalue177 ~value()
178 {
179 cleanup();
180 }
181
cleanupvalue182 void cleanup()
183 {
184 switch (type) {
185 case type::uninitialized:
186 break;
187 case type::object:
188 object_.~vector();
189 break;
190 case type::array:
191 array_.~vector();
192 break;
193 case type::string:
194 break;
195 case type::number:
196 break;
197 case type::boolean:
198 break;
199 case type::null:
200 break;
201 default:
202 break;
203 }
204 }
205
is_objectvalue206 inline bool is_object() const noexcept
207 {
208 return type == type::object;
209 }
is_arrayvalue210 inline bool is_array() const noexcept
211 {
212 return type == type::array;
213 }
is_stringvalue214 inline bool is_string() const noexcept
215 {
216 return type == type::string;
217 }
is_numbervalue218 inline bool is_number() const noexcept
219 {
220 return type == type::number;
221 }
is_booleanvalue222 inline bool is_boolean() const noexcept
223 {
224 return type == type::boolean;
225 }
is_nullvalue226 inline bool is_null() const noexcept
227 {
228 return type == type::null;
229 }
230
emptyvalue231 inline bool empty() const noexcept
232 {
233 if (is_object()) {
234 return object_.empty();
235 } else if (is_array()) {
236 return array_.empty();
237 }
238 return true;
239 }
240
241 const value* find(string_t key) const noexcept;
242
243 value& operator[](const string_t& key);
244 };
245
246 struct pair {
247 string_t key;
248 value value;
249 };
250
find(string_t key)251 inline const value* value::find(string_t key) const noexcept
252 {
253 if (type == type::object) {
254 for (auto& t : object_) {
255 if (t.key == key) {
256 return &t.value;
257 }
258 }
259 }
260 return nullptr;
261 }
262
263 inline value& value::operator[](const string_t& key)
264 {
265 if (type == type::object) {
266 for (auto& t : object_) {
267 if (t.key == key) {
268 return t.value;
269 }
270 }
271 object_.push_back({ key, value{} });
272 return object_.back().value;
273 }
274 return *this;
275 }
276
HasKey(const value & object,const string_t key)277 inline bool HasKey(const value& object, const string_t key)
278 {
279 if (object.type == type::object) {
280 for (auto& t : object.object_) {
281 if (t.key == key) {
282 return true;
283 }
284 }
285 }
286 return false;
287 }
288
GetKey(const value & object,const string_t key)289 inline const value& GetKey(const value& object, const string_t key)
290 {
291 static value dummy;
292 if (object.type == type::object) {
293 for (auto& t : object.object_) {
294 if (t.key == key) {
295 return t.value;
296 }
297 }
298 }
299 return dummy;
300 }
301
302 #ifdef JSON_IMPL
303 char* parse_value(char* data, value& value);
304 char* parse_number(char* data, value&);
305 char* parse_string(char* data, value&);
306
isWhite(char data)307 inline bool isWhite(char data)
308 {
309 return ((data == ' ') || (data == '\n') || (data == '\r') || (data == '\t'));
310 }
311
isSign(char data)312 inline bool isSign(char data)
313 {
314 return ((data == '+') || (data == '-'));
315 }
316
isDigit(char data)317 inline bool isDigit(char data)
318 {
319 return ((data >= '0') && (data <= '9'));
320 }
321
isHex(char data)322 inline bool isHex(char data)
323 {
324 return ((data >= '0') && (data <= '9')) || ((data >= 'a') && (data <= 'f')) || ((data >= 'A') && (data <= 'F'));
325 }
326
trim(char * data)327 inline char* trim(char* data)
328 {
329 while (*data && isWhite(*data)) {
330 data++;
331 }
332 return data;
333 }
334
parse_object(char * data,value & res)335 char* parse_object(char* data, value& res)
336 {
337 data = trim(data);
338 if (*data == '}') {
339 // handle empty object.
340 res = value(object{});
341 return data + 1;
342 }
343 object values;
344 for (; *data != 0;) {
345 if (*data != '"') {
346 // invalid json
347 *data = 0;
348 return data;
349 }
350
351 pair& pair = values.emplace_back();
352 value key;
353 data = trim(parse_string(data + 1, key));
354 pair.key = key.string_;
355 if (*data == ':') {
356 data++;
357 } else {
358 // invalid json
359 *data = 0;
360 return data;
361 }
362 data = trim(data);
363 data = trim(parse_value(data, pair.value));
364 if (*data == '}') {
365 // handle end
366 res = value(std::move(values));
367 return data + 1;
368 } else if (*data == ',') {
369 data = trim(data + 1);
370 } else {
371 // invalid json
372 *data = 0;
373 return data;
374 }
375 }
376 return data;
377 }
378
parse_array(char * data,value & res)379 char* parse_array(char* data, value& res)
380 {
381 data = trim(data);
382 if (*data == ']') {
383 // empty array.
384 res = value(array{});
385 return data + 1;
386 }
387 array values;
388 for (; *data != 0;) {
389 value tmp;
390 data = trim(parse_value(data, tmp));
391 values.push_back(std::move(tmp));
392 if (*data == ',') {
393 data = trim(data + 1);
394 } else if (*data == ']') {
395 res = value(std::move(values));
396 return data + 1;
397 } else {
398 // invalid json
399 *data = 0;
400 return data;
401 }
402 }
403 return data;
404 }
405
406 // values
parse_string(char * data,value & res)407 char* parse_string(char* data, value& res)
408 {
409 char* start = data;
410 for (; *data != 0; data++) {
411 if (*data == '\\' && data[1]) {
412 // escape.. (parse just enough to not stop too early)
413 if (data[1] == '\\' || data[1] == '"' || data[1] == '/' || data[1] == 'b' || data[1] == 'f' ||
414 data[1] == 'n' || data[1] == 'r' || data[1] == 't') {
415 ++data;
416 continue;
417 } else if (data[1] == 'u') {
418 data += 2;
419 for (char* end = data + 4; data != end; ++data) {
420 if (*data == 0 || !isHex(*data)) {
421 *data = 0;
422 return data;
423 }
424 }
425 --data;
426 } else {
427 // invalid json
428 *data = 0;
429 return data;
430 }
431 } else if (*data == '"') {
432 res = value(string_t{ start, (size_t)(data - start) });
433 return data + 1;
434 } else if (static_cast<unsigned char>(*data) < 0x20) {
435 // invalid json
436 *data = 0;
437 return data;
438 }
439 }
440 return data;
441 }
442
parse_number(char * data,value & res)443 char* parse_number(char* data, value& res)
444 {
445 char* beg = data;
446 if (*data == '-') {
447 // is neg..
448 data++;
449 if (!isDigit(*data)) {
450 *data = 0;
451 return data;
452 }
453 }
454 bool fraction = false;
455 bool exponent = false;
456
457 if (*data == '0') {
458 ++data;
459 // after leading zero only '.', 'e' and 'E' allowed
460 if (*data == '.') {
461 ++data;
462 fraction = true;
463 } else if (*data == 'e' || *data == 'E') {
464 ++data;
465 exponent = true;
466 }
467 } else {
468 for (; *data != 0; data++) {
469 if (isDigit(*data))
470 continue;
471 if (*data == '.') {
472 ++data;
473 fraction = true;
474 } else if (*data == 'e' || *data == 'E') {
475 ++data;
476 exponent = true;
477 }
478 break;
479 }
480 }
481
482 if (fraction) {
483 // exponent must start with a digit
484 if (isDigit(*data)) {
485 ++data;
486 } else {
487 // invalid json
488 *data = 0;
489 return data;
490 }
491 // fraction may contain digits up to begining of exponent ('e' or 'E')
492 for (; *data != 0; data++) {
493 if (isDigit(*data))
494 continue;
495 if (*data == 'e' || *data == 'E') {
496 ++data;
497 exponent = true;
498 }
499 break;
500 }
501 }
502 if (exponent) {
503 // exponent must start with '-' or '+' followed by a digit, or digit
504 if (*data == '-' || *data == '+') {
505 ++data;
506 }
507 if (isDigit(*data)) {
508 ++data;
509 } else {
510 // invalid json
511 *data = 0;
512 return data;
513 }
514 for (; *data != 0; data++) {
515 if (isDigit(*data))
516 continue;
517 break;
518 }
519 }
520 if (data != beg) {
521 char* end = data - 1;
522 res = value(strtod(beg, &end));
523 return data;
524 }
525 *data = 0;
526 return data;
527 }
528
parse_boolean(char * data,value & res)529 char* parse_boolean(char* data, value& res)
530 {
531 if (*data == 't') {
532 ++data;
533 const char rue[] = { 'r', 'u', 'e' };
534 for (unsigned i = 0u; i < sizeof(rue); ++i) {
535 if (data[i] == 0 || data[i] != rue[i]) {
536 *data = 0;
537 return data;
538 }
539 }
540
541 res = value(true);
542 data += sizeof(rue);
543 } else if (*data == 'f') {
544 ++data;
545 const char alse[] = { 'a', 'l', 's', 'e' };
546 for (unsigned i = 0u; i < sizeof(alse); ++i) {
547 if (data[i] == 0 || data[i] != alse[i]) {
548 *data = 0;
549 return data;
550 }
551 }
552 res = value(false);
553 data += sizeof(alse);
554 } else {
555 // invalid json
556 *data = 0;
557 return data;
558 }
559 return data;
560 }
561
parse_null(char * data,value & res)562 char* parse_null(char* data, value& res)
563 {
564 if (*data == 'n') {
565 ++data;
566 const char ull[] = { 'u', 'l', 'l' };
567 for (unsigned i = 0u; i < sizeof(ull); ++i) {
568 if (data[i] == 0 || data[i] != ull[i]) {
569 *data = 0;
570 return data;
571 }
572 }
573 res = value(null{});
574 data += sizeof(ull);
575 } else {
576 // invalid json
577 *data = 0;
578 return data;
579 }
580 return data;
581 }
582
parse_value(char * data,value & value)583 char* parse_value(char* data, value& value)
584 {
585 data = trim(data);
586 if (*data == '{') {
587 data = trim(parse_object(data + 1, value));
588 } else if (*data == '[') {
589 data = trim(parse_array(data + 1, value));
590 } else if (*data == '"') {
591 data = trim(parse_string(data + 1, value));
592 } else if (isSign(*data) || isDigit(*data)) {
593 data = trim(parse_number(data, value));
594 } else if ((*data == 't') || (*data == 'f')) {
595 data = trim(parse_boolean(data, value));
596 } else if (*data == 'n') {
597 data = trim(parse_null(data, value));
598 } else {
599 // invalid json
600 *data = 0;
601 return data;
602 }
603 return data;
604 }
605
add(value & v,value && value)606 void add(value& v, value&& value)
607 {
608 switch (v.type) {
609 case type::uninitialized:
610 v = std::move(value);
611 break;
612 case type::object:
613 v.object_.back().value = std::move(value);
614 break;
615 case type::array:
616 v.array_.push_back(std::move(value));
617 break;
618 case type::string:
619 case type::number:
620 case type::boolean:
621 case type::null:
622 default:
623 break;
624 }
625 }
626
parse(char * data)627 value parse(char* data)
628 {
629 array stack;
630 // push an uninitialized value which will get the final value during parsing
631 stack.push_back(value{});
632
633 bool acceptValue = true;
634 while (*data) {
635 data = trim(data);
636 if (*data == '{') {
637 // start of an object
638 if (!acceptValue) {
639 return {};
640 }
641 data = trim(data + 1);
642 if (*data == '}') {
643 data = trim(data + 1);
644 // handle empty object.
645 add(stack.back(), value(object{}));
646 acceptValue = false;
647 } else if (*data == '"') {
648 // try to read the key
649 value key;
650 data = trim(parse_string(data + 1, key));
651
652 if (*data != ':') {
653 // invalid json
654 return {};
655 }
656 data = trim(data + 1);
657 // push the object with key and missing value on the stack and hope to find a value next
658 stack.push_back(value(object{}));
659 stack.back().object_.push_back(pair{ key.string_, value{} });
660 acceptValue = true;
661 } else {
662 // invalid json
663 return {};
664 }
665 } else if (*data == '}') {
666 // end of an object
667 if (stack.back().type != type::object) {
668 // invalid json
669 return {};
670 }
671 // check are we missing a value ('{"":}', '{"":"",}' )
672 if (acceptValue) {
673 return {};
674 }
675 data = trim(data + 1);
676 // move this object to the next in the stack
677 auto value = std::move(stack.back());
678 stack.pop_back();
679 if (stack.empty()) {
680 // invalid json
681 return {};
682 }
683 add(stack.back(), std::move(value));
684 acceptValue = false;
685 } else if (*data == '[') {
686 // start of an array
687 if (!acceptValue) {
688 return {};
689 }
690 data = trim(data + 1);
691 if (*data == ']') {
692 data = trim(data + 1);
693 // handle empty array.
694 add(stack.back(), value(array{}));
695 acceptValue = false;
696 } else {
697 // push the empty array on the stack and hope to find values
698 stack.push_back(value(array{}));
699 acceptValue = true;
700 }
701 } else if (*data == ']') {
702 // end of an array
703 if (stack.back().type != type::array) {
704 // invalid json
705 return {};
706 }
707 // check are we missing a value ('[1,]' '[1]]')
708 if (acceptValue) {
709 // invalid json
710 return {};
711 }
712 data = trim(data + 1);
713
714 auto value = std::move(stack.back());
715 stack.pop_back();
716 if (stack.empty()) {
717 // invalid json
718 return {};
719 }
720 add(stack.back(), std::move(value));
721 acceptValue = false;
722 } else if (*data == ',') {
723 // comma is allowed when the previous value was complete and we have an incomplete object or array on the
724 // stack.
725 if (!acceptValue && stack.back().type == type::object) {
726 data = trim(data + 1);
727 if (*data != '"') {
728 // invalid json
729 return {};
730 }
731 // try to read the key
732 value key;
733 data = trim(parse_string(data + 1, key));
734
735 if (*data != ':') {
736 // invalid json
737 return {};
738 }
739 data = trim(data + 1);
740 stack.back().object_.push_back(pair{ key.string_, value{} });
741 acceptValue = true;
742 } else if (!acceptValue && stack.back().type == type::array) {
743 data = trim(data + 1);
744 acceptValue = true;
745 } else {
746 // invalid json
747 return {};
748 }
749 } else if (*data == '"') {
750 value value;
751 data = trim(parse_string(data + 1, value));
752 if (acceptValue && value.type == type::string) {
753 add(stack.back(), std::move(value));
754 acceptValue = false;
755 } else {
756 // invalid json
757 return {};
758 }
759 } else if (isSign(*data) || isDigit(*data)) {
760 value value;
761 data = trim(parse_number(data, value));
762 if (acceptValue && value.type == type::number) {
763 add(stack.back(), std::move(value));
764 acceptValue = false;
765 } else {
766 // invalid json
767 return {};
768 }
769 } else if ((*data == 't') || (*data == 'f')) {
770 value value;
771 data = trim(parse_boolean(data, value));
772 if (acceptValue && value.type == type::boolean) {
773 add(stack.back(), std::move(value));
774 acceptValue = false;
775 } else {
776 // invalid json
777 return {};
778 }
779 } else if (*data == 'n') {
780 value value;
781 data = trim(parse_null(data, value));
782 if (acceptValue && value.type == type::null) {
783 add(stack.back(), std::move(value));
784 acceptValue = false;
785 } else {
786 // invalid json
787 return {};
788 }
789 } else {
790 // invalid json
791 return {};
792 }
793 }
794 // check if we are missing a value ('{"":' '[')
795 if (acceptValue) {
796 return {};
797 }
798
799 auto value = std::move(stack.front());
800 return value;
801 }
802
803 // end of parser
804
to_string(const value & value)805 std::string to_string(const value& value)
806 {
807 std::string out;
808 switch (value.type) {
809 case type::uninitialized:
810 break;
811
812 case type::object: {
813 out += '{';
814 int count = 0;
815 for (const auto& v : value.object_) {
816 if (count++) {
817 out += ',';
818 }
819 out += '"';
820 out.append(v.key.data(), v.key.size());
821 out += '"';
822 out += ':';
823 out += to_string(v.value);
824 }
825 out += '}';
826 break;
827 }
828
829 case type::array: {
830 out += '[';
831 int count = 0;
832 for (const auto& v : value.array_) {
833 if (count++) {
834 out += ',';
835 }
836 out += to_string(v);
837 }
838 out += ']';
839 break;
840 }
841
842 case type::string:
843 out += '"';
844 out.append(value.string_.data(), value.string_.size());
845 out += '"';
846 break;
847
848 case type::number:
849 if (value.number_ == static_cast<int64_t>(value.number_)) {
850 out += std::to_string(static_cast<int64_t>(value.number_));
851 } else {
852 #if defined(HAS_CHARCONV) && !defined(__linux__) && !defined(__APPLE__)
853 char asStr[64]{};
854 if (auto result = std::to_chars(std::begin(asStr), std::end(asStr), value.number_);
855 result.ec == std::errc()) {
856 out.append(asStr, static_cast<size_t>(result.ptr - asStr));
857 }
858 #else
859 out += std::to_string(value.number_).data();
860 #endif
861 }
862 break;
863
864 case type::boolean:
865 if (value.boolean_) {
866 out += "true";
867 } else {
868 out += "false";
869 }
870 break;
871
872 case type::null:
873 out += "null";
874 break;
875
876 default:
877 break;
878 }
879 return out;
880 }
881 #endif // JSON_IMPL
882 } // namespace json
883 #endif // !LUMEJSON_H
884