• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 API_CORE_JSON_JSON_H
17 #define API_CORE_JSON_JSON_H
18 
19 #include <cstdint>
20 #include <cstdio>
21 #include <securec.h>
22 
23 #include <base/containers/fixed_string.h>
24 #include <base/containers/string.h>
25 #include <base/containers/string_view.h>
26 #include <base/containers/vector.h>
27 #include <base/util/uid.h>
28 #include <core/namespace.h>
29 
CORE_BEGIN_NAMESPACE()30 CORE_BEGIN_NAMESPACE()
31 namespace json {
32 using readonly_string_t = BASE_NS::string_view;
33 using writable_string_t = BASE_NS::string;
34 template<typename T>
35 using array_t = BASE_NS::vector<T>;
36 
37 /** Type of a JSON value. */
38 enum class type : uint8_t {
39     uninitialized = 0,
40     object,
41     array,
42     string,
43     floating_point,
44     signed_int,
45     unsigned_int,
46     boolean,
47     null,
48 };
49 
50 /** Tag for JSON values which contain read-only strings. The source JSON string must be kept alive until the parsing
51  * results are not used. */
52 struct readonly_tag {};
53 
54 /** Tag for JSON values which contain writable strings. Life time of the parsing results doens't depend on the source
55  * JSON string. */
56 struct writable_tag {};
57 
58 template<typename T = readonly_tag>
59 struct value_t;
60 
61 /** JSON structure which contains read-only strings. The source JSON string must be kept alive until the parsing
62  * results are not used. */
63 using value = value_t<readonly_tag>;
64 
65 /** JSON structure which contains writable strings. String values can be modified and if the instance was generated by
66  * the parser, the source string doesn't need to be stored while the instance is used. */
67 using standalone_value = value_t<writable_tag>;
68 
69 /** Parses 'data' and returns JSON structure. the value::type will be 'uninitialized' if parsing failed.
70  * @param data JSON as a null terminated string.
71  * @return Parsed JSON structure.
72  */
73 template<typename T = readonly_tag>
74 value_t<T> parse(const char* data);
75 
76 /** Converts a JSON structure into a string.
77  * @param value JSON structure.
78  * @return JSON as string.
79  */
80 template<typename T = readonly_tag>
81 BASE_NS::string to_string(const value_t<T>& value);
82 
83 BASE_NS::string unescape(BASE_NS::string_view str);
84 /** JSON value. */
85 template<typename Tag>
86 struct value_t {
87     /** Type used for JSON strings and JSON object keys. */
88     using string =
89         typename BASE_NS::conditional_t<BASE_NS::is_same_v<Tag, writable_tag>, writable_string_t, readonly_string_t>;
90 
91     /** Type used for JSON null */
92     struct null {};
93 
94     /** Type used for key-value pairs inside JSON objects. */
95     struct pair {
96         pair(string&& k, value_t&& v) : key(BASE_NS::forward<string>(k)), value(BASE_NS::forward<value_t>(v)) {}
97         string key;
98         value_t value;
99     };
100 
101     /** Type used for JSON objects. */
102     using object = array_t<pair>;
103 
104     /** Type used for JSON arrays. */
105     using array = array_t<value_t>;
106 
107     /** Type of this JSON value. */
108     type type { type::uninitialized };
109     union {
110         object object_;
111         array array_;
112         string string_;
113         double float_;
114         int64_t signed_;
115         uint64_t unsigned_;
116         bool boolean_;
117     };
118 
119     value_t() noexcept : type { type::uninitialized } {}
120 
121     value_t(object&& value) noexcept : type { type::object }, object_(BASE_NS::move(value)) {}
122 
123     value_t(array&& value) noexcept : type { type::array }, array_(BASE_NS::move(value)) {}
124 
125     value_t(string value) noexcept : type { type::string }, string_(value) {}
126 
127     value_t(const char* value) noexcept : value_t(string(value)) {}
128 
129     explicit value_t(bool value) noexcept : type { type::boolean }, boolean_(value) {}
130 
131     value_t(null value) noexcept : type { type::null } {}
132 
133     template<typename Number, BASE_NS::enable_if_t<BASE_NS::is_floating_point_v<Number>, bool> = true>
134     value_t(Number value) noexcept : type { type::floating_point }, float_(static_cast<double>(value))
135     {}
136 
137     template<typename Number,
138         BASE_NS::enable_if_t<!BASE_NS::is_floating_point_v<Number> && BASE_NS::is_signed_v<Number>, bool> = true>
139     value_t(Number value) noexcept : type { type::signed_int }, signed_(static_cast<int64_t>(value))
140     {}
141 
142     template<typename Number, BASE_NS::enable_if_t<BASE_NS::is_unsigned_v<Number>, bool> = true>
143     value_t(Number value) noexcept : type { type::unsigned_int }, unsigned_(static_cast<uint64_t>(value))
144     {}
145 
146     template<typename Value>
147     value_t(array_t<Value> values) noexcept : type { type::array }, array_(array {})
148     {
149         array_.reserve(values.size());
150         for (const auto& value : values) {
151             array_.emplace_back(value);
152         }
153     }
154 
155     template<typename Value, size_t N>
156     value_t(Value (&value)[N]) : type { type::array }, array_(array {})
157     {
158         array_.reserve(N);
159         for (size_t i = 0; i < N; ++i) {
160             array_.emplace_back(value[i]);
161         }
162     }
163 
164     template<typename OtherT>
165     value_t(const value_t<OtherT>& other) : type(other.type)
166     {
167         switch (type) {
168             case type::uninitialized:
169                 break;
170             case type::object:
171                 new (&object_) object(BASE_NS::default_allocator());
172                 object_.reserve(other.object_.size());
173                 for (const auto& p : other.object_) {
174                     object_.push_back({ string(p.key), p.value });
175                 }
176                 break;
177             case type::array:
178                 new (&array_) array(BASE_NS::default_allocator());
179                 array_.reserve(other.array_.size());
180                 for (const auto& v : other.array_) {
181                     array_.emplace_back(v);
182                 }
183                 break;
184             case type::string:
185                 new (&string_) string;
186                 string_ = other.string_;
187                 break;
188             case type::floating_point:
189                 float_ = other.float_;
190                 break;
191             case type::signed_int:
192                 signed_ = other.signed_;
193                 break;
194             case type::unsigned_int:
195                 unsigned_ = other.unsigned_;
196                 break;
197             case type::boolean:
198                 boolean_ = other.boolean_;
199                 break;
200             case type::null:
201                 break;
202             default:
203                 break;
204         }
205     }
206 
207     value_t(value_t&& rhs) : type { BASE_NS::exchange(rhs.type, type::uninitialized) }
208     {
209         switch (type) {
210             case type::uninitialized:
211                 break;
212             case type::object:
213                 new (&object_) object(BASE_NS::move(rhs.object_));
214                 break;
215             case type::array:
216                 new (&array_) array(BASE_NS::move(rhs.array_));
217                 break;
218             case type::string:
219                 new (&string_) string(BASE_NS::move(rhs.string_));
220                 break;
221             case type::floating_point:
222                 float_ = rhs.float_;
223                 break;
224             case type::signed_int:
225                 signed_ = rhs.signed_;
226                 break;
227             case type::unsigned_int:
228                 unsigned_ = rhs.unsigned_;
229                 break;
230             case type::boolean:
231                 boolean_ = rhs.boolean_;
232                 break;
233             case type::null:
234                 break;
235             default:
236                 break;
237         }
238     }
239 
240     value_t& operator=(value_t&& rhs)
241     {
242         if (this != &rhs) {
243             cleanup();
244             type = BASE_NS::exchange(rhs.type, type::uninitialized);
245             switch (type) {
246                 case type::uninitialized:
247                     break;
248                 case type::object:
249                     new (&object_) object(BASE_NS::move(rhs.object_));
250                     break;
251                 case type::array:
252                     new (&array_) array(BASE_NS::move(rhs.array_));
253                     break;
254                 case type::string:
255                     new (&string_) string(BASE_NS::move(rhs.string_));
256                     break;
257                 case type::floating_point:
258                     float_ = rhs.float_;
259                     break;
260                 case type::signed_int:
261                     signed_ = rhs.signed_;
262                     break;
263                 case type::unsigned_int:
264                     unsigned_ = rhs.unsigned_;
265                     break;
266                 case type::boolean:
267                     boolean_ = rhs.boolean_;
268                     break;
269                 case type::null:
270                     break;
271                 default:
272                     break;
273             }
274         }
275         return *this;
276     }
277 
278 #if _MSC_VER
279 #pragma warning(push)
280 #pragma warning(disable : 4583)
281 #endif
282     ~value_t()
283     {
284         cleanup();
285     }
286 #if _MSC_VER
287 #pragma warning(pop)
288 #endif
289     template<typename T>
290     inline void destroy(T& t)
291     {
292         t.~T();
293     }
294 
295     void cleanup()
296     {
297         switch (type) {
298             case type::uninitialized:
299                 break;
300             case type::object:
301                 destroy(object_);
302                 break;
303             case type::array:
304                 destroy(array_);
305                 break;
306             case type::string:
307                 destroy(string_);
308                 break;
309             case type::floating_point:
310                 break;
311             case type::signed_int:
312                 break;
313             case type::unsigned_int:
314                 break;
315             case type::boolean:
316                 break;
317             case type::null:
318                 break;
319             default:
320                 break;
321         }
322     }
323 
324     explicit operator bool() const noexcept
325     {
326         return type != type::uninitialized;
327     }
328 
329     bool is_object() const noexcept
330     {
331         return type == type::object;
332     }
333 
334     bool is_array() const noexcept
335     {
336         return type == type::array;
337     }
338 
339     bool is_string() const noexcept
340     {
341         return type == type::string;
342     }
343 
344     bool is_floating_point() const noexcept
345     {
346         return type == type::floating_point;
347     }
348 
349     bool is_signed_int() const noexcept
350     {
351         return type == type::signed_int;
352     }
353 
354     bool is_unsigned_int() const noexcept
355     {
356         return type == type::unsigned_int;
357     }
358 
359     bool is_number() const noexcept
360     {
361         return type == type::floating_point || type == type::signed_int || type == type::unsigned_int;
362     }
363 
364     bool is_boolean() const noexcept
365     {
366         return type == type::boolean;
367     }
368 
369     bool is_null() const noexcept
370     {
371         return type == type::null;
372     }
373 
374     bool empty() const noexcept
375     {
376         if (is_object()) {
377             return object_.empty();
378         } else if (is_array()) {
379             return array_.empty();
380         }
381         return true;
382     }
383 
384     template<typename T>
385     T as_number() const
386     {
387         switch (type) {
388             case type::floating_point:
389                 return static_cast<T>(float_);
390             case type::signed_int:
391                 return static_cast<T>(signed_);
392             case type::unsigned_int:
393                 return static_cast<T>(unsigned_);
394             default:
395                 return 0;
396         }
397     }
398 
399     const value_t* find(BASE_NS::string_view key) const noexcept
400     {
401         if (type == type::object) {
402             for (auto& t : object_) {
403                 if (t.key == key) {
404                     return &t.value;
405                 }
406             }
407         }
408         return nullptr;
409     }
410 
411     value_t& operator[](const BASE_NS::string_view& key)
412     {
413         if (type == type::object) {
414             for (auto& t : object_) {
415                 if (t.key == key) {
416                     return t.value;
417                 }
418             }
419             object_.emplace_back(value_t<Tag>::string(key), value_t<Tag> {});
420             return object_.back().value;
421         }
422         return *this;
423     }
424 };
425 
426 #ifdef JSON_IMPL
427 inline bool isWhite(char data)
428 {
429     return ((data == ' ') || (data == '\n') || (data == '\r') || (data == '\t'));
430 }
431 
432 inline bool isSign(char data)
433 {
434     return ((data == '+') || (data == '-'));
435 }
436 
437 inline bool isDigit(char data)
438 {
439     return ((data >= '0') && (data <= '9'));
440 }
441 
442 inline bool isHex(char data)
443 {
444     return ((data >= '0') && (data <= '9')) || ((data >= 'a') && (data <= 'f')) || ((data >= 'A') && (data <= 'F'));
445 }
446 
447 inline const char* trim(const char* data)
448 {
449     while (*data && isWhite(*data)) {
450         data++;
451     }
452     return data;
453 }
454 
455 // values
456 template<typename T>
457 const char* parse_string(const char* data, value_t<T>& res)
458 {
459     const char* start = data;
460     for (; *data != 0; data++) {
461         if (*data == '\\' && data[1]) {
462             // escape.. (parse just enough to not stop too early)
463             if (data[1] == '\\' || data[1] == '"' || data[1] == '/' || data[1] == 'b' || data[1] == 'f' ||
464                 data[1] == 'n' || data[1] == 'r' || data[1] == 't') {
465                 ++data;
466                 continue;
467             } else if (data[1] == 'u') {
468                 data += 2;
469                 for (const char* end = data + 4; data != end; ++data) {
470                     if (*data == 0 || !isHex(*data)) {
471                         // invalid Unicode
472                         return data;
473                     }
474                 }
475                 --data;
476             } else {
477                 // invalid escape
478                 return data;
479             }
480         } else if (*data == '"') {
481             res = value_t<T> { typename value_t<T>::string { start, static_cast<size_t>(data - start) } };
482             return data + 1;
483         } else if (static_cast<unsigned char>(*data) < 0x20) {
484             // unescaped control
485             return data;
486         }
487     }
488     return data;
489 }
490 
491 template<typename T>
492 const char* parse_number(const char* data, value_t<T>& res)
493 {
494     bool negative = false;
495     const char* beg = data;
496     if (*data == '-') {
497         negative = true;
498         data++;
499         if (!isDigit(*data)) {
500             // no digits after '-'
501             return data;
502         }
503     }
504     bool fraction = false;
505     bool exponent = false;
506 
507     if (*data == '0') {
508         ++data;
509         // after leading zero only '.', 'e' and 'E' allowed
510         if (*data == '.') {
511             ++data;
512             fraction = true;
513         } else if (*data == 'e' || *data == 'E') {
514             ++data;
515             exponent = true;
516         }
517     } else {
518         while (isDigit(*data)) {
519             ++data;
520         }
521         if (*data == '.') {
522             ++data;
523             fraction = true;
524         } else if (*data == 'e' || *data == 'E') {
525             ++data;
526             exponent = true;
527         }
528     }
529 
530     if (fraction) {
531         // fraction must start with a digit
532         if (isDigit(*data)) {
533             ++data;
534         } else {
535             // fraction missing first digit
536             return data;
537         }
538         // fraction may contain digits up to begining of exponent ('e' or 'E')
539         while (isDigit(*data)) {
540             ++data;
541         }
542         if (*data == 'e' || *data == 'E') {
543             ++data;
544             exponent = true;
545         }
546     }
547     if (exponent) {
548         // exponent must start with '-' or '+' followed by a digit, or digit
549         if (*data == '-' || *data == '+') {
550             ++data;
551         }
552         if (isDigit(*data)) {
553             ++data;
554         } else {
555             // exponent missing first digit
556             return data;
557         }
558         while (isDigit(*data)) {
559             ++data;
560         }
561     }
562     if (data != beg) {
563         char* end;
564         if (fraction || exponent) {
565             res = value(strtod(beg, &end));
566         } else if (negative) {
567             res = value(strtoll(beg, &end, 10));
568         } else {
569             res = value(strtoull(beg, &end, 10));
570         }
571         return data;
572     }
573     // invalid json
574     return data;
575 }
576 
577 template<typename T>
578 const char* parse_boolean(const char* data, value_t<T>& res)
579 {
580     if (*data == 't') {
581         ++data;
582         const char rue[] = { 'r', 'u', 'e' };
583         for (unsigned i = 0u; i < sizeof(rue); ++i) {
584             if (data[i] == 0 || data[i] != rue[i]) {
585                 // non-string starting with 't' but != "true"
586                 return data;
587             }
588         }
589 
590         res = value(true);
591         data += sizeof(rue);
592     } else if (*data == 'f') {
593         ++data;
594         const char alse[] = { 'a', 'l', 's', 'e' };
595         for (unsigned i = 0u; i < sizeof(alse); ++i) {
596             if (data[i] == 0 || data[i] != alse[i]) {
597                 // non-string starting with 'f' but != "false"
598                 return data;
599             }
600         }
601         res = value(false);
602         data += sizeof(alse);
603     } else {
604         // non-string not starting with 'f' or 't'
605         return data;
606     }
607     return data;
608 }
609 
610 template<typename T>
611 const char* parse_null(const char* data, value_t<T>& res)
612 {
613     if (*data == 'n') {
614         ++data;
615         const char ull[] = { 'u', 'l', 'l' };
616         for (unsigned i = 0u; i < sizeof(ull); ++i) {
617             if (data[i] == 0 || data[i] != ull[i]) {
618                 // non-string starting with 'n' but != "null"
619                 return data;
620             }
621         }
622         res = value(value::null {});
623         data += sizeof(ull);
624     } else {
625         // invalid json
626         return data;
627     }
628     return data;
629 }
630 
631 template<typename T>
632 void add(value_t<T>& v, value_t<T>&& value)
633 {
634     switch (v.type) {
635         case type::uninitialized:
636             v = BASE_NS::move(value);
637             break;
638         case type::object:
639             v.object_.back().value = BASE_NS::move(value);
640             break;
641         case type::array:
642             v.array_.push_back(BASE_NS::move(value));
643             break;
644         case type::string:
645         case type::floating_point:
646         case type::signed_int:
647         case type::unsigned_int:
648         case type::boolean:
649         case type::null:
650         default:
651             break;
652     }
653 }
654 
655 template<typename T>
656 value_t<T> parse(const char* data)
657 {
658     if (!data) {
659         return {};
660     }
661     using jsonValue = value_t<T>;
662     typename jsonValue::array stack;
663     // push an uninitialized value which will get the final value during parsing
664     stack.emplace_back();
665 
666     bool acceptValue = true;
667     while (*data) {
668         data = trim(data);
669         if (*data == '{') {
670             // start of an object
671             if (!acceptValue) {
672                 return {};
673             }
674             data = trim(data + 1);
675             if (*data == '}') {
676                 data = trim(data + 1);
677                 // handle empty object.
678                 add(stack.back(), jsonValue(typename jsonValue::object {}));
679                 acceptValue = false;
680             } else if (*data == '"') {
681                 // try to read the key
682                 jsonValue key;
683                 data = trim(parse_string(data + 1, key));
684 
685                 if (*data != ':') {
686                     // missing : after key
687                     return {};
688                 }
689                 data = trim(data + 1);
690                 // push the object with key and missing value on the stack and hope to find a value next
691                 stack.emplace_back(typename jsonValue::object {})
692                     .object_.emplace_back(BASE_NS::move(key.string_), jsonValue {});
693                 acceptValue = true;
694             } else {
695                 // missing start of key or end of object
696                 return {};
697             }
698         } else if (*data == '}') {
699             // end of an object
700             if (stack.back().type != type::object) {
701                 // unexpected }
702                 return {};
703             }
704             // check are we missing a value ('{"":}', '{"":"",}' )
705             if (acceptValue) {
706                 return {};
707             }
708             data = trim(data + 1);
709             // move this object to the next in the stack
710             auto value = BASE_NS::move(stack.back());
711             stack.pop_back();
712             if (stack.empty()) {
713                 // invalid json
714                 return {};
715             }
716             add(stack.back(), BASE_NS::move(value));
717             acceptValue = false;
718         } else if (*data == '[') {
719             // start of an array
720             if (!acceptValue) {
721                 // unexpected [
722                 return {};
723             }
724             data = trim(data + 1);
725             if (*data == ']') {
726                 data = trim(data + 1);
727                 // handle empty array.
728                 add(stack.back(), jsonValue(typename jsonValue::array {}));
729                 acceptValue = false;
730             } else {
731                 // push the empty array on the stack and hope to find values
732                 stack.emplace_back(typename jsonValue::array {});
733                 acceptValue = true;
734             }
735         } else if (*data == ']') {
736             // end of an array
737             if (stack.back().type != type::array) {
738                 // unexpected ]
739                 return {};
740             }
741             // check are we missing a value ('[1,]' '[1]]')
742             if (acceptValue) {
743                 // unexpected ]
744                 return {};
745             }
746             data = trim(data + 1);
747 
748             auto value = BASE_NS::move(stack.back());
749             stack.pop_back();
750             if (stack.empty()) {
751                 // invalid json
752                 return {};
753             }
754             add(stack.back(), BASE_NS::move(value));
755             acceptValue = false;
756         } else if (*data == ',') {
757             // comma is allowed when the previous value was complete and we have an incomplete object or array on the
758             // stack.
759             if (!acceptValue && stack.back().type == type::object) {
760                 data = trim(data + 1);
761                 if (*data != '"') {
762                     // missing key for next object
763                     return {};
764                 }
765                 // try to read the key
766                 jsonValue key;
767                 data = trim(parse_string(data + 1, key));
768 
769                 if (*data != ':') {
770                     // missing value for next object
771                     return {};
772                 }
773                 data = trim(data + 1);
774                 stack.back().object_.emplace_back(BASE_NS::move(key.string_), jsonValue {});
775                 acceptValue = true;
776             } else if (!acceptValue && stack.back().type == type::array) {
777                 data = trim(data + 1);
778                 acceptValue = true;
779             } else {
780                 // comma allowed only between objects and values inside an array
781                 return {};
782             }
783         } else if (*data == '"') {
784             jsonValue value;
785             data = trim(parse_string(data + 1, value));
786             if (acceptValue && value.type == type::string) {
787                 add(stack.back(), BASE_NS::move(value));
788                 acceptValue = false;
789             } else {
790                 // unexpected "
791                 return {};
792             }
793         } else if (isSign(*data) || isDigit(*data)) {
794             jsonValue value;
795             data = trim(parse_number(data, value));
796             if (acceptValue && value.type != type::uninitialized) {
797                 add(stack.back(), BASE_NS::move(value));
798                 acceptValue = false;
799             } else {
800                 // failed parsing number
801                 return {};
802             }
803         } else if ((*data == 't') || (*data == 'f')) {
804             jsonValue value;
805             data = trim(parse_boolean(data, value));
806             if (acceptValue && value.type == type::boolean) {
807                 add(stack.back(), BASE_NS::move(value));
808                 acceptValue = false;
809             } else {
810                 // failed parsing boolean
811                 return {};
812             }
813         } else if (*data == 'n') {
814             jsonValue value;
815             data = trim(parse_null(data, value));
816             if (acceptValue && value.type == type::null) {
817                 add(stack.back(), BASE_NS::move(value));
818                 acceptValue = false;
819             } else {
820                 // failed parsing null
821                 return {};
822             }
823         } else {
824             // unexpected character
825             return {};
826         }
827     }
828     // check if we are missing a value ('{"":' '[')
829     if (acceptValue) {
830         return {};
831     }
832 
833     auto value = BASE_NS::move(stack.front());
834     return value;
835 }
836 
837 template value parse(const char*);
838 template standalone_value parse(const char*);
839 // end of parser
840 template<typename T>
841 void append(BASE_NS::string& out, const typename value_t<T>::object& object)
842 {
843     out += '{';
844     int count = 0;
845     for (const auto& v : object) {
846         if (count++) {
847             out += ',';
848         }
849         out += '"';
850         out.append(v.key.data(), v.key.size());
851         out += '"';
852         out += ':';
853         out += to_string(v.value);
854     }
855     out += '}';
856 }
857 
858 template<typename T>
859 void append(BASE_NS::string& out, const typename value_t<T>::array& array)
860 {
861     out += '[';
862     int count = 0;
863     for (const auto& v : array) {
864         if (count++) {
865             out += ',';
866         }
867         out += to_string(v);
868     }
869     out += ']';
870 }
871 
872 template<typename T>
873 void append(BASE_NS::string& out, const typename value_t<T>::string& string)
874 {
875     out += '"';
876     out.append(string.data(), string.size());
877     out += '"';
878 }
879 
880 template<typename T>
881 void append(BASE_NS::string& out, const double floatingPoint)
882 {
883     const char* FLOATING_FORMAT_STR = "%.17g";
884     // use snprintf to calculate the required size (not supported by the _s variant)
885     const int size = snprintf(nullptr, 0, FLOATING_FORMAT_STR, floatingPoint);
886     const size_t oldSize = out.size();
887     out.resize(oldSize + size);
888     const size_t newSize = out.size();
889     // "At most bufsz - 1 characters are written." string has size() characters + 1 for null so use size() +
890     // 1 as the total size. If resize() failed string size() hasn't changed, buffer will point to the null
891     // character and bufsz will be 1 i.e. only the null character will be written.
892     int ret = snprintf_s(out.data() + oldSize, newSize + 1 - oldSize, size, FLOATING_FORMAT_STR, floatingPoint);
893     if (ret < 0) {
894         return;
895     }
896 }
897 
898 template<typename T>
899 BASE_NS::string to_string(const value_t<T>& value)
900 {
901     BASE_NS::string out;
902     switch (value.type) {
903         case type::uninitialized:
904             break;
905 
906         case type::object:
907             append<T>(out, value.object_);
908             break;
909 
910         case type::array:
911             append<T>(out, value.array_);
912             break;
913 
914         case type::string:
915             append<T>(out, value.string_);
916             break;
917 
918         case type::floating_point:
919             append<T>(out, value.float_);
920             break;
921 
922         case type::signed_int:
923             out += BASE_NS::to_string(value.signed_);
924             break;
925 
926         case type::unsigned_int:
927             out += BASE_NS::to_string(value.unsigned_);
928             break;
929 
930         case type::boolean:
931             if (value.boolean_) {
932                 out += "true";
933             } else {
934                 out += "false";
935             }
936             break;
937 
938         case type::null:
939             out += "null";
940             break;
941 
942         default:
943             break;
944     }
945     return out;
946 }
947 
948 template BASE_NS::string to_string(const value& value);
949 template BASE_NS::string to_string(const standalone_value& value);
950 
951 int codepoint(BASE_NS::string_view str)
952 {
953     int code = 0;
954     for (size_t u = 0; u < 4; ++u) {
955         const char chr = str[u];
956         code <<= 4U;
957         code += BASE_NS::HexToDec(chr);
958     }
959     return code;
960 }
961 
962 BASE_NS::string unescape(BASE_NS::string_view str)
963 {
964     BASE_NS::string unescaped;
965     unescaped.reserve(str.size());
966     for (size_t i = 0; i < str.size();) {
967         auto chr = str[i];
968         if (chr == '\\') {
969             ++i;
970             chr = str[i];
971             if (chr == '"') {
972                 unescaped += '"'; // Quotation mark
973             } else if (chr == '\\') {
974                 unescaped += '\\'; // Reverse solidus
975             } else if (chr == '/') {
976                 unescaped += '/'; // Solidus.. we do unescape this..
977             } else if (chr == 'b') {
978                 unescaped += '\b'; // Backspace
979             } else if (chr == 'f') {
980                 unescaped += '\f'; // Formfeed
981             } else if (chr == 'n') {
982                 unescaped += '\n'; // Linefeed
983             } else if (chr == 'r') {
984                 unescaped += '\r'; // Carriage return
985             } else if (chr == 't') {
986                 unescaped += '\t';           // Horizontal tab
987             } else if (chr == 'u') {         // Unicode character
988                 if ((i + 4U) < str.size()) { // Expecting 4 hexadecimal values
989                     // Read the Unicode code point.
990                     int code = codepoint(str.substr(i + 1, 4U));
991                     i += 4U;
992                     // Codepoints U+010000 to U+10FFFF are encoded as UTF-16 surrogate pairs. High surrogate between
993                     // 0xD800-0xDBFF and low between 0xDC00-0xDFFF.
994                     if (code >= 0xd800 && code <= 0xdbff) {
995                         // See if there's an other \uXXXX value in the correct range.
996                         if ((i + 6U) < str.size()) {
997                             auto next = str.substr(i + 1, 6U);
998                             if (next[0] == '\\' && next[1] == 'u') {
999                                 next.remove_prefix(2);
1000                                 int low = codepoint(next);
1001                                 i += 6U;
1002                                 if (low >= 0xdc00 && low <= 0xdfff) {
1003                                     // Surragete pair encoding: 0x10000 + (Hi - 0xD800) * 0x400 + (Lo - 0xDC00)
1004                                     code = (static_cast<unsigned>(code) << 10U) + static_cast<unsigned>(low) -
1005                                            ((0xd800 << 10U) + 0xdc00U - 0x10000U);
1006                                 }
1007                             }
1008                         }
1009                     }
1010                     // Convert code point to UTF-8.
1011                     if (code < 0x80) {
1012                         // 1-byte characters: 0xxxxxxx (ASCII)
1013                         unescaped += static_cast<char>(code);
1014                     } else if (code < 0x7ff) {
1015                         // 2-byte characters: 110xxxxx 10xxxxxx
1016                         unescaped += static_cast<char>(0xc0U | (static_cast<unsigned int>(code) >> 6U));
1017                         unescaped += static_cast<char>(0x80U | (static_cast<unsigned int>(code) & 0x3fU));
1018                     } else if (code <= 0xffff) {
1019                         // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
1020                         unescaped += static_cast<char>(0xe0U | (static_cast<unsigned int>(code) >> 12u));
1021                         unescaped += static_cast<char>(0x80U | ((static_cast<unsigned int>(code) >> 6U) & 0x3FU));
1022                         unescaped += static_cast<char>(0x80U | (static_cast<unsigned int>(code) & 0x3Fu));
1023                     } else {
1024                         // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1025                         unescaped += (static_cast<char>(0xf0U | (static_cast<unsigned int>(code) >> 18U)));
1026                         unescaped += (static_cast<char>(0x80U | ((static_cast<unsigned int>(code) >> 12U) & 0x3fU)));
1027                         unescaped += (static_cast<char>(0x80U | ((static_cast<unsigned int>(code) >> 6U) & 0x3fU)));
1028                         unescaped += (static_cast<char>(0x80U | (static_cast<unsigned int>(code) & 0x3fU)));
1029                     }
1030                 }
1031             } else {
1032             }
1033             ++i;
1034         } else {
1035             unescaped += chr;
1036             ++i;
1037         }
1038     }
1039     return unescaped;
1040 }
1041 #endif // JSON_IMPL
1042 } // namespace json
1043 CORE_END_NAMESPACE()
1044 
1045 #endif // !API_CORE_JSON_JSON_H
1046