Gaia-ECS v1.0.0
A simple and powerful entity component system
Loading...
Searching...
No Matches
ser_json.h
1#pragma once
2#include "gaia/config/config.h"
3
4#if GAIA_JSON_ENABLED
5
6 #include <cctype>
7 #include <cmath>
8 #include <cstdint>
9 #include <cstdio>
10 #include <cstdlib>
11 #include <cstring>
12 #include <limits>
13 #include <type_traits>
14
15 #include "gaia/cnt/darray.h"
16 #include "gaia/core/utility.h"
17 #include "gaia/ser/ser_buffer_binary.h"
18 #include "gaia/util/str.h"
19
20namespace gaia {
21 namespace ser {
23 using json_str_view = util::str_view;
25 using json_str = util::str;
26
28 enum JsonSaveFlags : uint32_t {
30 None = 0,
32 BinarySnapshot = 1u << 0,
34 RawFallback = 1u << 1,
36 Default = BinarySnapshot | RawFallback
37 };
38
41 struct RuntimeJsonPolicy final {
43 bool symbolicEnums = false;
45 bool symbolicBitmasks = false;
48 bool requireAllFields = false;
51 bool enforceRanges = false;
52 };
53
55 enum class JsonDiagSeverity : uint8_t {
57 Info,
59 Warning,
61 Error
62 };
64 enum class JsonDiagReason : uint8_t {
66 None,
68 UnknownField,
70 FieldOutOfBounds,
72 FieldValueAdjusted,
74 TagValueIgnored,
76 NullComponentPayload,
78 MissingRuntimeFieldsOrRawPayload,
80 SoaRawUnsupported,
82 UnknownComponent,
84 TagComponentUnsupported,
86 DuplicateEntityName,
88 MissingComponentStorage,
90 MissingArchetypesSection,
92 MissingFormatField,
94 UnsupportedFormatVersion,
96 InvalidJson,
98 UnknownRuntimeConstant,
100 InvalidRuntimeConstant,
102 InvalidPatchPath,
104 ReadOnlyField,
106 HiddenField,
108 UnsupportedPatchValue,
110 RangeViolation,
112 StaleSchema,
114 MissingField
115 };
116
118 struct JsonDiagnostic {
120 JsonDiagSeverity severity = JsonDiagSeverity::Warning;
122 JsonDiagReason reason = JsonDiagReason::None;
124 json_str path;
126 json_str message;
127 };
128
130 struct JsonDiagnostics {
132 static constexpr uint32_t MaxDiagPathLength = 1024;
134 static constexpr uint32_t MaxDiagMessageLength = 2048;
135
137 cnt::darray<JsonDiagnostic> items;
139 bool hasWarnings = false;
141 bool hasErrors = false;
142
148 void add(JsonDiagSeverity severity, JsonDiagReason reason, json_str_view path, json_str_view message) {
149 JsonDiagnostic diag;
150 diag.severity = severity;
151 diag.reason = reason;
152 diag.path.assign(path);
153 diag.message.assign(message);
154 items.push_back(diag);
155
156 if (severity == JsonDiagSeverity::Warning)
157 hasWarnings = true;
158 else if (severity == JsonDiagSeverity::Error)
159 hasErrors = true;
160 }
166 void add(JsonDiagSeverity severity, JsonDiagReason reason, json_str_view path, const char* message) {
167 add(severity, reason, path, json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
168 }
174 void add(JsonDiagSeverity severity, JsonDiagReason reason, const char* path, const char* message) {
175 add(severity, reason, json_str_view(path, (uint32_t)GAIA_STRLEN(path, MaxDiagPathLength)),
176 json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
177 }
178
181 GAIA_NODISCARD bool has_issues() const {
182 return hasWarnings || hasErrors;
183 }
184
185 void clear() {
186 items.clear();
187 hasWarnings = false;
188 hasErrors = false;
189 }
190 };
191
196 class ser_json {
197 static constexpr uint32_t MaxImplicitKeyLength = 384;
198 static constexpr uint32_t MaxImplicitStringLength = 16u * 1024u * 1024u;
199 static constexpr uint32_t MaxLiteralLength = 256u;
201 static constexpr uint32_t MaxNumberLength = 128u;
202
203 enum class CtxType : uint8_t { Object, Array };
204
205 struct Ctx {
206 CtxType type = CtxType::Object;
207 bool first = true;
208 bool needsValue = false;
209 };
210
211 json_str m_out;
212 json_str m_parseScratch;
213 cnt::darray<Ctx> m_ctx;
214 const char* m_it = nullptr;
215 const char* m_end = nullptr;
216
217 GAIA_NODISCARD static bool parse_hex_quad(const char* str, uint32_t& value) {
218 value = 0;
219 GAIA_FOR(4) {
220 const char ch = str[i];
221 uint32_t digit = 0;
222 if (ch >= '0' && ch <= '9')
223 digit = (uint32_t)(ch - '0');
224 else if (ch >= 'a' && ch <= 'f')
225 digit = (uint32_t)(ch - 'a') + 10;
226 else if (ch >= 'A' && ch <= 'F')
227 digit = (uint32_t)(ch - 'A') + 10;
228 else
229 return false;
230 value = (value << 4U) | digit;
231 }
232 return true;
233 }
234
235 static void append_utf8(json_str& out, uint32_t codePoint) {
236 if (codePoint <= 0x7fU) {
237 out.append((char)codePoint);
238 } else if (codePoint <= 0x7ffU) {
239 out.append((char)(0xc0U | (codePoint >> 6U)));
240 out.append((char)(0x80U | (codePoint & 0x3fU)));
241 } else if (codePoint <= 0xffffU) {
242 out.append((char)(0xe0U | (codePoint >> 12U)));
243 out.append((char)(0x80U | ((codePoint >> 6U) & 0x3fU)));
244 out.append((char)(0x80U | (codePoint & 0x3fU)));
245 } else {
246 out.append((char)(0xf0U | (codePoint >> 18U)));
247 out.append((char)(0x80U | ((codePoint >> 12U) & 0x3fU)));
248 out.append((char)(0x80U | ((codePoint >> 6U) & 0x3fU)));
249 out.append((char)(0x80U | (codePoint & 0x3fU)));
250 }
251 }
252
253 static void add_escaped(json_str& out, const char* str, uint32_t len) {
254 GAIA_FOR(len) {
255 const char ch = str[i];
256 switch (ch) {
257 case '"':
258 out.append("\\\"");
259 break;
260 case '\\':
261 out.append("\\\\");
262 break;
263 case '\n':
264 out.append("\\n");
265 break;
266 case '\r':
267 out.append("\\r");
268 break;
269 case '\t':
270 out.append("\\t");
271 break;
272 default:
273 out.append(ch);
274 break;
275 }
276 }
277 }
278
279 void before_value() {
280 if (m_ctx.empty())
281 return;
282
283 auto& ctx = m_ctx.back();
284 if (ctx.type == CtxType::Array) {
285 if (!ctx.first)
286 m_out.append(",");
287 ctx.first = false;
288 } else {
289 GAIA_ASSERT(ctx.needsValue);
290 ctx.needsValue = false;
291 }
292 }
293
294 public:
295 ser_json() = default;
296
299 ser_json(const char* json, uint32_t len) {
300 reset_input(json, len);
301 }
305 template <size_t N>
306 explicit ser_json(const char (&json)[N]) {
307 static_assert(N > 0);
308 reset_input(json, (uint32_t)(N - 1));
309 }
310
314 void reset_input(const char* json, uint32_t len) {
315 if (json == nullptr) {
316 m_it = nullptr;
317 m_end = nullptr;
318 return;
319 }
320
321 m_it = json;
322 m_end = json + len;
323 }
327 template <size_t N>
328 void reset_input(const char (&json)[N]) {
329 static_assert(N > 0);
330 reset_input(json, (uint32_t)(N - 1));
331 }
332
334 void clear() {
335 m_out.clear();
336 m_parseScratch.clear();
337 m_ctx.clear();
338 }
339
342 GAIA_NODISCARD const json_str& str() const {
343 return m_out;
344 }
345
348 GAIA_NODISCARD bool eof() const {
349 return m_it == nullptr || m_end == nullptr || m_it >= m_end;
350 }
351
354 GAIA_NODISCARD char peek() const {
355 GAIA_ASSERT(m_it != nullptr && m_it < m_end);
356 return *m_it;
357 }
358
360 void ws() {
361 if (m_it == nullptr || m_end == nullptr)
362 return;
363 while (m_it < m_end && std::isspace((unsigned char)*m_it))
364 ++m_it;
365 }
366
367 GAIA_NODISCARD const char* pos() const {
368 return m_it;
369 }
370
371 GAIA_NODISCARD const char* end() const {
372 return m_end;
373 }
374
375 void begin_object() {
376 before_value();
377 m_out.append("{");
378 m_ctx.push_back({CtxType::Object, true, false});
379 }
380
381 void end_object() {
382 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
383 m_ctx.pop_back();
384 m_out.append("}");
385 }
386
387 void begin_array() {
388 before_value();
389 m_out.append("[");
390 m_ctx.push_back({CtxType::Array, true, false});
391 }
392
393 void end_array() {
394 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Array);
395 m_ctx.pop_back();
396 m_out.append("]");
397 }
398
399 void key(const char* name, uint32_t len = 0) {
400 GAIA_ASSERT(name != nullptr);
401 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
402 auto& ctx = m_ctx.back();
403 GAIA_ASSERT(!ctx.needsValue);
404
405 if (!ctx.first)
406 m_out.append(",");
407 ctx.first = false;
408 ctx.needsValue = true;
409
410 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, MaxImplicitKeyLength) : len;
411 m_out.append("\"");
412 add_escaped(m_out, name, l);
413 m_out.append("\":");
414 }
415
416 void value_null() {
417 before_value();
418 m_out.append("null");
419 }
420
421 void value_bool(bool v) {
422 before_value();
423 if (v)
424 m_out.append("true");
425 else
426 m_out.append("false");
427 }
428
429 template <typename TInt, typename = std::enable_if_t<std::is_integral_v<TInt> && !std::is_same_v<TInt, bool>>>
430 void value_int(TInt v) {
431 before_value();
432
433 char buff[64];
434 if constexpr (std::is_signed_v<TInt>) {
435 (void)GAIA_STRFMT(buff, sizeof(buff), "%lld", (long long)v);
436 } else {
437 (void)GAIA_STRFMT(buff, sizeof(buff), "%llu", (unsigned long long)v);
438 }
439 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (size_t)sizeof(buff)));
440 }
441
442 void value_float(float v) {
443 before_value();
444 char buff[64];
445 (void)GAIA_STRFMT(buff, sizeof(buff), "%.9g", (double)v);
446 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (size_t)sizeof(buff)));
447 }
448
449 void value_float(double v) {
450 before_value();
451 char buff[64];
452 (void)GAIA_STRFMT(buff, sizeof(buff), "%.17g", v);
453 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (size_t)sizeof(buff)));
454 }
455
456 void value_string(const char* str, uint32_t len = 0) {
457 GAIA_ASSERT(str != nullptr);
458 before_value();
459 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(str, MaxImplicitStringLength) : len;
460 m_out.append("\"");
461 add_escaped(m_out, str, l);
462 m_out.append("\"");
463 }
464
465 bool consume(char ch) {
466 ws();
467 if (m_it == nullptr || m_end == nullptr || m_it >= m_end || *m_it != ch)
468 return false;
469 ++m_it;
470 return true;
471 }
472
473 bool expect(char ch) {
474 return consume(ch);
475 }
476
477 bool parse_literal(const char* lit) {
478 ws();
479 if (m_it == nullptr || m_end == nullptr || lit == nullptr)
480 return false;
481
482 const auto litLen = (uint32_t)GAIA_STRLEN(lit, MaxLiteralLength);
483 if (litLen >= MaxLiteralLength)
484 return false;
485 if ((uint32_t)(m_end - m_it) < litLen)
486 return false;
487 if (memcmp(m_it, lit, litLen) != 0)
488 return false;
489 m_it += litLen;
490 return true;
491 }
492
493 bool parse_string_view(json_str_view& out, bool* fromScratch = nullptr) {
494 ws();
495 if (m_it == nullptr || m_end == nullptr || m_it >= m_end || *m_it != '"')
496 return false;
497
498 ++m_it;
499 const char* begin = m_it;
500 bool escaped = false;
501 m_parseScratch.clear();
502 while (m_it < m_end) {
503 const char ch = *m_it++;
504 if (ch == '"')
505 break;
506
507 if (ch == '\\') {
508 if (!escaped) {
509 escaped = true;
510 const auto prefixLen = (size_t)((m_it - 1) - begin);
511 if (prefixLen > 0)
512 m_parseScratch.append(begin, (uint32_t)prefixLen);
513 }
514
515 if (m_it >= m_end)
516 return false;
517 const char esc = *m_it++;
518 switch (esc) {
519 case '"':
520 case '\\':
521 case '/':
522 m_parseScratch.append(esc);
523 break;
524 case 'b':
525 m_parseScratch.append('\b');
526 break;
527 case 'f':
528 m_parseScratch.append('\f');
529 break;
530 case 'n':
531 m_parseScratch.append('\n');
532 break;
533 case 'r':
534 m_parseScratch.append('\r');
535 break;
536 case 't':
537 m_parseScratch.append('\t');
538 break;
539 case 'u': {
540 if ((uint32_t)(m_end - m_it) < 4)
541 return false;
542 uint32_t codePoint = 0;
543 if (!parse_hex_quad(m_it, codePoint))
544 return false;
545 m_it += 4;
546 if (codePoint >= 0xd800U && codePoint <= 0xdbffU) {
547 if ((uint32_t)(m_end - m_it) < 6 || m_it[0] != '\\' || m_it[1] != 'u')
548 return false;
549 uint32_t low = 0;
550 if (!parse_hex_quad(m_it + 2, low) || low < 0xdc00U || low > 0xdfffU)
551 return false;
552 m_it += 6;
553 codePoint = 0x10000U + ((codePoint - 0xd800U) << 10U) + (low - 0xdc00U);
554 } else if (codePoint >= 0xdc00U && codePoint <= 0xdfffU) {
555 return false;
556 }
557 append_utf8(m_parseScratch, codePoint);
558 break;
559 }
560 default:
561 return false;
562 }
563 } else if (escaped)
564 m_parseScratch.append(ch);
565 }
566
567 if (m_it <= begin || m_it > m_end || *(m_it - 1) != '"')
568 return false;
569
570 if (escaped) {
571 if (fromScratch != nullptr)
572 *fromScratch = true;
573 out = m_parseScratch.view();
574 } else {
575 if (fromScratch != nullptr)
576 *fromScratch = false;
577 out = json_str_view(begin, (uint32_t)((m_it - 1) - begin));
578 }
579 return true;
580 }
581
582 bool parse_string(json_str& out) {
583 json_str_view view;
584 if (!parse_string_view(view))
585 return false;
586 out.assign(view);
587 return true;
588 }
589
590 bool parse_string_eq(const char* literal) {
591 json_str_view view;
592 if (!parse_string_view(view))
593 return false;
594 const auto literalLen = (size_t)GAIA_STRLEN(literal, MaxLiteralLength);
595 if (literalLen >= MaxLiteralLength)
596 return false;
597 return view.size() == literalLen && memcmp(view.data(), literal, literalLen) == 0;
598 }
599
609 bool
610 parse_integer_token(bool& negative, uint64_t& magnitude, bool& overflow, bool& adjusted, bool& integerToken) {
611 ws();
612 negative = false;
613 magnitude = 0;
614 overflow = false;
615 adjusted = false;
616 integerToken = false;
617 if (m_it == nullptr || m_end == nullptr || m_it >= m_end)
618 return false;
619
620 const char* p = m_it;
621 if (*p == '-') {
622 negative = true;
623 ++p;
624 }
625 if (p >= m_end || *p < '0' || *p > '9')
626 return false;
627 const char* digitsBegin = p;
628 if (*p == '0' && p + 1 < m_end && p[1] >= '0' && p[1] <= '9')
629 return false;
630
631 constexpr uint64_t MaxValue = (std::numeric_limits<uint64_t>::max)();
632 while (p < m_end && *p >= '0' && *p <= '9') {
633 const auto digit = (uint64_t)(*p - '0');
634 if (magnitude > (MaxValue - digit) / 10)
635 overflow = true;
636 else if (!overflow)
637 magnitude = magnitude * 10 + digit;
638 ++p;
639 }
640 const char* digitsEnd = p;
641
642 if (p < m_end && *p == '.') {
643 const char* decimalIt = p + 1;
644 if (decimalIt >= m_end || *decimalIt < '0' || *decimalIt > '9')
645 return false;
646 while (decimalIt < m_end && *decimalIt >= '0' && *decimalIt <= '9')
647 ++decimalIt;
648 if (decimalIt < m_end && (*decimalIt == 'e' || *decimalIt == 'E')) {
649 ++decimalIt;
650 if (decimalIt < m_end && (*decimalIt == '+' || *decimalIt == '-'))
651 ++decimalIt;
652 if (decimalIt >= m_end || *decimalIt < '0' || *decimalIt > '9')
653 return false;
654 }
655 return true;
656 }
657 if (p < m_end && (*p == 'e' || *p == 'E')) {
658 const char* exponentIt = p + 1;
659 bool negativeExponent = false;
660 if (exponentIt < m_end && (*exponentIt == '+' || *exponentIt == '-')) {
661 negativeExponent = *exponentIt == '-';
662 ++exponentIt;
663 }
664 if (exponentIt >= m_end || *exponentIt < '0' || *exponentIt > '9')
665 return false;
666
667 uint32_t exponent = 0;
668 bool exponentOverflow = false;
669 while (exponentIt < m_end && *exponentIt >= '0' && *exponentIt <= '9') {
670 const auto digit = (uint32_t)(*exponentIt - '0');
671 if (exponent > (UINT32_MAX - digit) / 10)
672 exponentOverflow = true;
673 else if (!exponentOverflow)
674 exponent = exponent * 10 + digit;
675 ++exponentIt;
676 }
677
678 if (negativeExponent && (exponentOverflow || exponent != 0)) {
679 const auto digitCount = (uint32_t)(digitsEnd - digitsBegin);
680 const auto keptCount = exponentOverflow || exponent >= digitCount ? 0U : digitCount - exponent;
681 magnitude = 0;
682 overflow = false;
683 for (uint32_t i = 0; i < keptCount; ++i) {
684 const auto digit = (uint64_t)(digitsBegin[i] - '0');
685 if (magnitude > (MaxValue - digit) / 10)
686 overflow = true;
687 else if (!overflow)
688 magnitude = magnitude * 10 + digit;
689 }
690 for (uint32_t i = keptCount; i < digitCount; ++i)
691 adjusted = adjusted || digitsBegin[i] != '0';
692 } else if (exponentOverflow || exponent > 19) {
693 overflow = magnitude != 0;
694 } else {
695 while (exponent-- > 0) {
696 if (magnitude > MaxValue / 10) {
697 overflow = true;
698 break;
699 }
700 magnitude *= 10;
701 }
702 }
703 p = exponentIt;
704 }
705
706 if (overflow)
707 magnitude = MaxValue;
708
709 integerToken = true;
710 m_it = p;
711 return true;
712 }
713
719 bool parse_number(double& value) {
720 ws();
721 if (m_it == nullptr || m_end == nullptr || m_it >= m_end)
722 return false;
723
724 // std::strtod reads until a byte that cannot continue the number and ignores m_end. Copy the token
725 // into a null-terminated buffer so it stops where the input does.
726 uint32_t len = 0;
727 while (m_it + len < m_end) {
728 const char ch = m_it[len];
729 if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == ',' || ch == ']' || ch == '}')
730 break;
731 if (++len >= MaxNumberLength)
732 return false;
733 }
734 if (len == 0)
735 return false;
736
737 char token[MaxNumberLength];
738 memcpy(token, m_it, len);
739 token[len] = 0;
740
741 char* pEnd = nullptr;
742 const double parsed = std::strtod(token, &pEnd);
743 if (pEnd != token + len)
744 return false;
745
746 value = parsed;
747 m_it += len;
748 return true;
749 }
750
751 bool parse_bool(bool& value) {
752 if (parse_literal("true")) {
753 value = true;
754 return true;
755 }
756 if (parse_literal("false")) {
757 value = false;
758 return true;
759 }
760 return false;
761 }
762
763 bool parse_null() {
764 return parse_literal("null");
765 }
766
767 bool skip_value() {
768 ws();
769 if (m_it == nullptr || m_end == nullptr || m_it >= m_end)
770 return false;
771
772 if (*m_it == '{') {
773 ++m_it;
774 ws();
775 if (consume('}'))
776 return true;
777
778 while (true) {
779 json_str_view key;
780 if (!parse_string_view(key))
781 return false;
782 if (!expect(':'))
783 return false;
784 if (!skip_value())
785 return false;
786
787 ws();
788 if (consume(','))
789 continue;
790 if (consume('}'))
791 return true;
792 return false;
793 }
794 }
795
796 if (*m_it == '[') {
797 ++m_it;
798 ws();
799 if (consume(']'))
800 return true;
801
802 while (true) {
803 if (!skip_value())
804 return false;
805 ws();
806 if (consume(','))
807 continue;
808 if (consume(']'))
809 return true;
810 return false;
811 }
812 }
813
814 if (*m_it == '"') {
815 json_str_view tmp;
816 return parse_string_view(tmp);
817 }
818
819 if (*m_it == 't' || *m_it == 'f') {
820 bool v = false;
821 return parse_bool(v);
822 }
823
824 if (*m_it == 'n')
825 return parse_null();
826
827 double v = 0.0;
828 return parse_number(v);
829 }
830 };
831
832 namespace detail {
834 template <typename T>
835 inline void copy_field_bytes(uint8_t* pFieldData, uint32_t size, const T& v) {
836 memcpy(pFieldData, &v, size < sizeof(v) ? size : (uint32_t)sizeof(v));
837 }
838
839 template <typename TInt>
840 inline bool read_runtime_field_json_int(ser_json& reader, uint8_t* pFieldData, uint32_t size, bool& ok) {
841 bool negative = false;
842 uint64_t magnitude = 0;
843 bool overflow = false;
844 bool adjusted = false;
845 bool integerToken = false;
846 if (!reader.parse_integer_token(negative, magnitude, overflow, adjusted, integerToken))
847 return false;
848
849 if (integerToken) {
850 if (adjusted)
851 ok = false;
852 TInt value = 0;
853 if constexpr (std::is_signed_v<TInt>) {
854 constexpr auto MaxPositive = (uint64_t)(std::numeric_limits<TInt>::max)();
855 constexpr auto MaxNegative = MaxPositive + 1;
856 if (negative) {
857 if (overflow || magnitude > MaxNegative) {
858 value = (std::numeric_limits<TInt>::lowest)();
859 ok = false;
860 } else if (magnitude == MaxNegative) {
861 value = (std::numeric_limits<TInt>::lowest)();
862 } else {
863 value = (TInt) - (int64_t)magnitude;
864 }
865 } else if (overflow || magnitude > MaxPositive) {
866 value = (std::numeric_limits<TInt>::max)();
867 ok = false;
868 } else {
869 value = (TInt)magnitude;
870 }
871 } else {
872 constexpr auto MaxValue = (uint64_t)(std::numeric_limits<TInt>::max)();
873 if (negative) {
874 if (overflow || magnitude != 0)
875 ok = false;
876 } else if (overflow || magnitude > MaxValue) {
877 value = (std::numeric_limits<TInt>::max)();
878 ok = false;
879 } else {
880 value = (TInt)magnitude;
881 }
882 }
883
884 copy_field_bytes(pFieldData, size, value);
885 return true;
886 }
887
888 double d = 0.0;
889 if (!reader.parse_number(d))
890 return false;
891
892 if (!std::isfinite(d)) {
893 ok = false;
894 const TInt v = 0;
895 copy_field_bytes(pFieldData, size, v);
896 return true;
897 }
898
899 double clamped = std::trunc(d);
900 if (clamped != d)
901 ok = false;
902
903 constexpr auto minVal = (double)(std::numeric_limits<TInt>::lowest)();
904 constexpr auto maxVal = (double)(std::numeric_limits<TInt>::max)();
905 if (clamped < minVal) {
906 clamped = minVal;
907 ok = false;
908 } else if constexpr (sizeof(TInt) == sizeof(uint64_t)) {
909 if (clamped >= maxVal) {
910 const TInt v = (std::numeric_limits<TInt>::max)();
911 copy_field_bytes(pFieldData, size, v);
912 ok = false;
913 return true;
914 }
915 } else if (clamped > maxVal) {
916 clamped = maxVal;
917 ok = false;
918 }
919
920 const TInt v = (TInt)clamped;
921 copy_field_bytes(pFieldData, size, v);
922 return true;
923 }
924
925 template <typename TFloat>
926 inline bool read_runtime_field_json_float(ser_json& reader, uint8_t* pFieldData, uint32_t size, bool& ok) {
927 double d = 0.0;
928 if (!reader.parse_number(d))
929 return false;
930
931 if (!std::isfinite(d)) {
932 ok = false;
933 const TFloat v = (TFloat)0;
934 copy_field_bytes(pFieldData, size, v);
935 return true;
936 }
937
938 const TFloat v = (TFloat)d;
939 copy_field_bytes(pFieldData, size, v);
940 return true;
941 }
942
943 inline bool
944 write_runtime_field_json(ser_json& writer, const uint8_t* pFieldData, serialization_type_id type, uint32_t size) {
945 switch (type) {
946 case serialization_type_id::s8: {
947 int8_t v = 0;
948 memcpy(&v, pFieldData, sizeof(v));
949 writer.value_int(v);
950 return true;
951 }
952 case serialization_type_id::u8: {
953 uint8_t v = 0;
954 memcpy(&v, pFieldData, sizeof(v));
955 writer.value_int(v);
956 return true;
957 }
958 case serialization_type_id::s16: {
959 int16_t v = 0;
960 memcpy(&v, pFieldData, sizeof(v));
961 writer.value_int(v);
962 return true;
963 }
964 case serialization_type_id::u16: {
965 uint16_t v = 0;
966 memcpy(&v, pFieldData, sizeof(v));
967 writer.value_int(v);
968 return true;
969 }
970 case serialization_type_id::s32: {
971 int32_t v = 0;
972 memcpy(&v, pFieldData, sizeof(v));
973 writer.value_int(v);
974 return true;
975 }
976 case serialization_type_id::u32: {
977 uint32_t v = 0;
978 memcpy(&v, pFieldData, sizeof(v));
979 writer.value_int(v);
980 return true;
981 }
982 case serialization_type_id::s64: {
983 int64_t v = 0;
984 memcpy(&v, pFieldData, sizeof(v));
985 writer.value_int(v);
986 return true;
987 }
988 case serialization_type_id::u64: {
989 uint64_t v = 0;
990 memcpy(&v, pFieldData, sizeof(v));
991 writer.value_int(v);
992 return true;
993 }
994 case serialization_type_id::b: {
995 bool v = false;
996 memcpy(&v, pFieldData, sizeof(v));
997 writer.value_bool(v);
998 return true;
999 }
1000 case serialization_type_id::f32: {
1001 float v = 0.0f;
1002 memcpy(&v, pFieldData, sizeof(v));
1003 writer.value_float(v);
1004 return true;
1005 }
1006 case serialization_type_id::f64: {
1007 double v = 0.0;
1008 memcpy(&v, pFieldData, sizeof(v));
1009 writer.value_float(v);
1010 return true;
1011 }
1012 case serialization_type_id::c8: {
1013 const auto len = (uint32_t)GAIA_STRLEN((const char*)pFieldData, size);
1014 writer.value_string((const char*)pFieldData, len);
1015 return true;
1016 }
1017 default:
1018 writer.value_null();
1019 return false;
1020 }
1021 }
1022
1023 inline bool read_runtime_field_json(
1024 ser_json& reader, uint8_t* pFieldData, serialization_type_id type, uint32_t size, bool& ok) {
1025 if (reader.parse_null()) {
1026 ok = false;
1027 return true;
1028 }
1029
1030 switch (type) {
1031 case serialization_type_id::s8: {
1032 return read_runtime_field_json_int<int8_t>(reader, pFieldData, size, ok);
1033 }
1034 case serialization_type_id::u8: {
1035 return read_runtime_field_json_int<uint8_t>(reader, pFieldData, size, ok);
1036 }
1037 case serialization_type_id::s16: {
1038 return read_runtime_field_json_int<int16_t>(reader, pFieldData, size, ok);
1039 }
1040 case serialization_type_id::u16: {
1041 return read_runtime_field_json_int<uint16_t>(reader, pFieldData, size, ok);
1042 }
1043 case serialization_type_id::s32: {
1044 return read_runtime_field_json_int<int32_t>(reader, pFieldData, size, ok);
1045 }
1046 case serialization_type_id::u32: {
1047 return read_runtime_field_json_int<uint32_t>(reader, pFieldData, size, ok);
1048 }
1049 case serialization_type_id::s64: {
1050 return read_runtime_field_json_int<int64_t>(reader, pFieldData, size, ok);
1051 }
1052 case serialization_type_id::u64: {
1053 return read_runtime_field_json_int<uint64_t>(reader, pFieldData, size, ok);
1054 }
1055 case serialization_type_id::f32: {
1056 return read_runtime_field_json_float<float>(reader, pFieldData, size, ok);
1057 }
1058 case serialization_type_id::f64: {
1059 return read_runtime_field_json_float<double>(reader, pFieldData, size, ok);
1060 }
1061 case serialization_type_id::b: {
1062 bool v = false;
1063 if (!reader.parse_bool(v))
1064 return false;
1065 copy_field_bytes(pFieldData, size, v);
1066 return true;
1067 }
1068 case serialization_type_id::c8: {
1069 json_str_view str;
1070 if (!reader.parse_string_view(str))
1071 return false;
1072 if (size == 0) {
1073 ok = false;
1074 return true;
1075 }
1076
1077 memset(pFieldData, 0, size);
1078 const auto maxLen = size > 0 ? size - 1 : 0;
1079 const auto strLen = (uint32_t)str.size();
1080 const auto copyLen = strLen < maxLen ? strLen : maxLen;
1081 if (strLen > maxLen)
1082 ok = false;
1083 if (copyLen > 0)
1084 memcpy(pFieldData, str.data(), copyLen);
1085 return true;
1086 }
1087 default:
1088 ok = false;
1089 return reader.skip_value();
1090 }
1091 }
1092
1093 template <typename TByteSink>
1094 inline bool parse_json_byte_array(ser_json& reader, TByteSink& out) {
1095 if (!reader.expect('['))
1096 return false;
1097
1098 reader.ws();
1099 if (reader.consume(']'))
1100 return true;
1101
1102 while (true) {
1103 double d = 0.0;
1104 if (!reader.parse_number(d))
1105 return false;
1106 if (d < 0.0 || d > 255.0)
1107 return false;
1108
1109 const auto v = (uint32_t)d;
1110 if ((double)v != d)
1111 return false;
1112
1113 const uint8_t byte = (uint8_t)v;
1114 out.save_raw(&byte, 1, serialization_type_id::u8);
1115
1116 reader.ws();
1117 if (reader.consume(','))
1118 continue;
1119 if (reader.consume(']'))
1120 return true;
1121 return false;
1122 }
1123 }
1125 } // namespace detail
1126 } // namespace ser
1127} // namespace gaia
1128
1129#endif