2#include "gaia/config/config.h"
13 #include <type_traits>
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"
23 using json_str_view = util::str_view;
25 using json_str = util::str;
28 enum JsonSaveFlags : uint32_t {
32 BinarySnapshot = 1u << 0,
34 RawFallback = 1u << 1,
36 Default = BinarySnapshot | RawFallback
41 struct RuntimeJsonPolicy final {
43 bool symbolicEnums =
false;
45 bool symbolicBitmasks =
false;
48 bool requireAllFields =
false;
51 bool enforceRanges =
false;
55 enum class JsonDiagSeverity : uint8_t {
64 enum class JsonDiagReason : uint8_t {
78 MissingRuntimeFieldsOrRawPayload,
84 TagComponentUnsupported,
88 MissingComponentStorage,
90 MissingArchetypesSection,
94 UnsupportedFormatVersion,
98 UnknownRuntimeConstant,
100 InvalidRuntimeConstant,
108 UnsupportedPatchValue,
118 struct JsonDiagnostic {
120 JsonDiagSeverity severity = JsonDiagSeverity::Warning;
122 JsonDiagReason reason = JsonDiagReason::None;
130 struct JsonDiagnostics {
132 static constexpr uint32_t MaxDiagPathLength = 1024;
134 static constexpr uint32_t MaxDiagMessageLength = 2048;
137 cnt::darray<JsonDiagnostic> items;
139 bool hasWarnings =
false;
141 bool hasErrors =
false;
148 void add(JsonDiagSeverity severity, JsonDiagReason reason, json_str_view path, json_str_view message) {
150 diag.severity = severity;
151 diag.reason = reason;
152 diag.path.assign(path);
153 diag.message.assign(message);
154 items.push_back(diag);
156 if (severity == JsonDiagSeverity::Warning)
158 else if (severity == JsonDiagSeverity::Error)
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)));
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)));
181 GAIA_NODISCARD
bool has_issues()
const {
182 return hasWarnings || hasErrors;
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;
203 enum class CtxType : uint8_t { Object, Array };
206 CtxType type = CtxType::Object;
208 bool needsValue =
false;
212 json_str m_parseScratch;
213 cnt::darray<Ctx> m_ctx;
214 const char* m_it =
nullptr;
215 const char* m_end =
nullptr;
217 GAIA_NODISCARD
static bool parse_hex_quad(
const char* str, uint32_t& value) {
220 const char ch = str[i];
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;
230 value = (value << 4U) | digit;
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)));
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)));
253 static void add_escaped(json_str& out,
const char* str, uint32_t len) {
255 const char ch = str[i];
279 void before_value() {
283 auto& ctx = m_ctx.back();
284 if (ctx.type == CtxType::Array) {
289 GAIA_ASSERT(ctx.needsValue);
290 ctx.needsValue =
false;
295 ser_json() =
default;
299 ser_json(
const char* json, uint32_t len) {
300 reset_input(json, len);
306 explicit ser_json(
const char (&json)[N]) {
307 static_assert(N > 0);
308 reset_input(json, (uint32_t)(N - 1));
314 void reset_input(
const char* json, uint32_t len) {
315 if (json ==
nullptr) {
328 void reset_input(
const char (&json)[N]) {
329 static_assert(N > 0);
330 reset_input(json, (uint32_t)(N - 1));
336 m_parseScratch.clear();
342 GAIA_NODISCARD
const json_str& str()
const {
348 GAIA_NODISCARD
bool eof()
const {
349 return m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end;
354 GAIA_NODISCARD
char peek()
const {
355 GAIA_ASSERT(m_it !=
nullptr && m_it < m_end);
361 if (m_it ==
nullptr || m_end ==
nullptr)
363 while (m_it < m_end && std::isspace((
unsigned char)*m_it))
367 GAIA_NODISCARD
const char* pos()
const {
371 GAIA_NODISCARD
const char* end()
const {
375 void begin_object() {
378 m_ctx.push_back({CtxType::Object,
true,
false});
382 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
390 m_ctx.push_back({CtxType::Array,
true,
false});
394 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Array);
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);
408 ctx.needsValue =
true;
410 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, MaxImplicitKeyLength) : len;
412 add_escaped(m_out, name, l);
418 m_out.append(
"null");
421 void value_bool(
bool v) {
424 m_out.append(
"true");
426 m_out.append(
"false");
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) {
434 if constexpr (std::is_signed_v<TInt>) {
435 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%lld", (
long long)v);
437 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%llu", (
unsigned long long)v);
439 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
442 void value_float(
float v) {
445 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.9g", (double)v);
446 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
449 void value_float(
double v) {
452 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.17g", v);
453 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
456 void value_string(
const char* str, uint32_t len = 0) {
457 GAIA_ASSERT(str !=
nullptr);
459 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(str, MaxImplicitStringLength) : len;
461 add_escaped(m_out, str, l);
465 bool consume(
char ch) {
467 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *m_it != ch)
473 bool expect(
char ch) {
477 bool parse_literal(
const char* lit) {
479 if (m_it ==
nullptr || m_end ==
nullptr || lit ==
nullptr)
482 const auto litLen = (uint32_t)GAIA_STRLEN(lit, MaxLiteralLength);
483 if (litLen >= MaxLiteralLength)
485 if ((uint32_t)(m_end - m_it) < litLen)
487 if (memcmp(m_it, lit, litLen) != 0)
493 bool parse_string_view(json_str_view& out,
bool* fromScratch =
nullptr) {
495 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *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++;
510 const auto prefixLen = (size_t)((m_it - 1) - begin);
512 m_parseScratch.append(begin, (uint32_t)prefixLen);
517 const char esc = *m_it++;
522 m_parseScratch.append(esc);
525 m_parseScratch.append(
'\b');
528 m_parseScratch.append(
'\f');
531 m_parseScratch.append(
'\n');
534 m_parseScratch.append(
'\r');
537 m_parseScratch.append(
'\t');
540 if ((uint32_t)(m_end - m_it) < 4)
542 uint32_t codePoint = 0;
543 if (!parse_hex_quad(m_it, codePoint))
546 if (codePoint >= 0xd800U && codePoint <= 0xdbffU) {
547 if ((uint32_t)(m_end - m_it) < 6 || m_it[0] !=
'\\' || m_it[1] !=
'u')
550 if (!parse_hex_quad(m_it + 2, low) || low < 0xdc00U || low > 0xdfffU)
553 codePoint = 0x10000U + ((codePoint - 0xd800U) << 10U) + (low - 0xdc00U);
554 }
else if (codePoint >= 0xdc00U && codePoint <= 0xdfffU) {
557 append_utf8(m_parseScratch, codePoint);
564 m_parseScratch.append(ch);
567 if (m_it <= begin || m_it > m_end || *(m_it - 1) !=
'"')
571 if (fromScratch !=
nullptr)
573 out = m_parseScratch.view();
575 if (fromScratch !=
nullptr)
576 *fromScratch =
false;
577 out = json_str_view(begin, (uint32_t)((m_it - 1) - begin));
582 bool parse_string(json_str& out) {
584 if (!parse_string_view(view))
590 bool parse_string_eq(
const char* literal) {
592 if (!parse_string_view(view))
594 const auto literalLen = (size_t)GAIA_STRLEN(literal, MaxLiteralLength);
595 if (literalLen >= MaxLiteralLength)
597 return view.size() == literalLen && memcmp(view.data(), literal, literalLen) == 0;
610 parse_integer_token(
bool& negative, uint64_t& magnitude,
bool& overflow,
bool& adjusted,
bool& integerToken) {
616 integerToken =
false;
617 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
620 const char* p = m_it;
625 if (p >= m_end || *p <
'0' || *p >
'9')
627 const char* digitsBegin = p;
628 if (*p ==
'0' && p + 1 < m_end && p[1] >=
'0' && p[1] <=
'9')
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)
637 magnitude = magnitude * 10 + digit;
640 const char* digitsEnd = p;
642 if (p < m_end && *p ==
'.') {
643 const char* decimalIt = p + 1;
644 if (decimalIt >= m_end || *decimalIt <
'0' || *decimalIt >
'9')
646 while (decimalIt < m_end && *decimalIt >=
'0' && *decimalIt <=
'9')
648 if (decimalIt < m_end && (*decimalIt ==
'e' || *decimalIt ==
'E')) {
650 if (decimalIt < m_end && (*decimalIt ==
'+' || *decimalIt ==
'-'))
652 if (decimalIt >= m_end || *decimalIt <
'0' || *decimalIt >
'9')
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 ==
'-';
664 if (exponentIt >= m_end || *exponentIt <
'0' || *exponentIt >
'9')
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;
678 if (negativeExponent && (exponentOverflow || exponent != 0)) {
679 const auto digitCount = (uint32_t)(digitsEnd - digitsBegin);
680 const auto keptCount = exponentOverflow || exponent >= digitCount ? 0U : digitCount - exponent;
683 for (uint32_t i = 0; i < keptCount; ++i) {
684 const auto digit = (uint64_t)(digitsBegin[i] -
'0');
685 if (magnitude > (MaxValue - digit) / 10)
688 magnitude = magnitude * 10 + digit;
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;
695 while (exponent-- > 0) {
696 if (magnitude > MaxValue / 10) {
707 magnitude = MaxValue;
719 bool parse_number(
double& value) {
721 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
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 ==
'}')
731 if (++len >= MaxNumberLength)
737 char token[MaxNumberLength];
738 memcpy(token, m_it, len);
741 char* pEnd =
nullptr;
742 const double parsed = std::strtod(token, &pEnd);
743 if (pEnd != token + len)
751 bool parse_bool(
bool& value) {
752 if (parse_literal(
"true")) {
756 if (parse_literal(
"false")) {
764 return parse_literal(
"null");
769 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
780 if (!parse_string_view(key))
816 return parse_string_view(tmp);
819 if (*m_it ==
't' || *m_it ==
'f') {
821 return parse_bool(v);
828 return parse_number(v);
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));
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))
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;
857 if (overflow || magnitude > MaxNegative) {
858 value = (std::numeric_limits<TInt>::lowest)();
860 }
else if (magnitude == MaxNegative) {
861 value = (std::numeric_limits<TInt>::lowest)();
863 value = (TInt) - (int64_t)magnitude;
865 }
else if (overflow || magnitude > MaxPositive) {
866 value = (std::numeric_limits<TInt>::max)();
869 value = (TInt)magnitude;
872 constexpr auto MaxValue = (uint64_t)(std::numeric_limits<TInt>::max)();
874 if (overflow || magnitude != 0)
876 }
else if (overflow || magnitude > MaxValue) {
877 value = (std::numeric_limits<TInt>::max)();
880 value = (TInt)magnitude;
884 copy_field_bytes(pFieldData, size, value);
889 if (!reader.parse_number(d))
892 if (!std::isfinite(d)) {
895 copy_field_bytes(pFieldData, size, v);
899 double clamped = std::trunc(d);
903 constexpr auto minVal = (double)(std::numeric_limits<TInt>::lowest)();
904 constexpr auto maxVal = (double)(std::numeric_limits<TInt>::max)();
905 if (clamped < minVal) {
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);
915 }
else if (clamped > maxVal) {
920 const TInt v = (TInt)clamped;
921 copy_field_bytes(pFieldData, size, v);
925 template <
typename TFloat>
926 inline bool read_runtime_field_json_float(ser_json& reader, uint8_t* pFieldData, uint32_t size,
bool& ok) {
928 if (!reader.parse_number(d))
931 if (!std::isfinite(d)) {
933 const TFloat v = (TFloat)0;
934 copy_field_bytes(pFieldData, size, v);
938 const TFloat v = (TFloat)d;
939 copy_field_bytes(pFieldData, size, v);
944 write_runtime_field_json(ser_json& writer,
const uint8_t* pFieldData, serialization_type_id type, uint32_t size) {
946 case serialization_type_id::s8: {
948 memcpy(&v, pFieldData,
sizeof(v));
952 case serialization_type_id::u8: {
954 memcpy(&v, pFieldData,
sizeof(v));
958 case serialization_type_id::s16: {
960 memcpy(&v, pFieldData,
sizeof(v));
964 case serialization_type_id::u16: {
966 memcpy(&v, pFieldData,
sizeof(v));
970 case serialization_type_id::s32: {
972 memcpy(&v, pFieldData,
sizeof(v));
976 case serialization_type_id::u32: {
978 memcpy(&v, pFieldData,
sizeof(v));
982 case serialization_type_id::s64: {
984 memcpy(&v, pFieldData,
sizeof(v));
988 case serialization_type_id::u64: {
990 memcpy(&v, pFieldData,
sizeof(v));
994 case serialization_type_id::b: {
996 memcpy(&v, pFieldData,
sizeof(v));
997 writer.value_bool(v);
1000 case serialization_type_id::f32: {
1002 memcpy(&v, pFieldData,
sizeof(v));
1003 writer.value_float(v);
1006 case serialization_type_id::f64: {
1008 memcpy(&v, pFieldData,
sizeof(v));
1009 writer.value_float(v);
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);
1018 writer.value_null();
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()) {
1031 case serialization_type_id::s8: {
1032 return read_runtime_field_json_int<int8_t>(reader, pFieldData, size, ok);
1034 case serialization_type_id::u8: {
1035 return read_runtime_field_json_int<uint8_t>(reader, pFieldData, size, ok);
1037 case serialization_type_id::s16: {
1038 return read_runtime_field_json_int<int16_t>(reader, pFieldData, size, ok);
1040 case serialization_type_id::u16: {
1041 return read_runtime_field_json_int<uint16_t>(reader, pFieldData, size, ok);
1043 case serialization_type_id::s32: {
1044 return read_runtime_field_json_int<int32_t>(reader, pFieldData, size, ok);
1046 case serialization_type_id::u32: {
1047 return read_runtime_field_json_int<uint32_t>(reader, pFieldData, size, ok);
1049 case serialization_type_id::s64: {
1050 return read_runtime_field_json_int<int64_t>(reader, pFieldData, size, ok);
1052 case serialization_type_id::u64: {
1053 return read_runtime_field_json_int<uint64_t>(reader, pFieldData, size, ok);
1055 case serialization_type_id::f32: {
1056 return read_runtime_field_json_float<float>(reader, pFieldData, size, ok);
1058 case serialization_type_id::f64: {
1059 return read_runtime_field_json_float<double>(reader, pFieldData, size, ok);
1061 case serialization_type_id::b: {
1063 if (!reader.parse_bool(v))
1065 copy_field_bytes(pFieldData, size, v);
1068 case serialization_type_id::c8: {
1070 if (!reader.parse_string_view(str))
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)
1084 memcpy(pFieldData, str.data(), copyLen);
1089 return reader.skip_value();
1093 template <
typename TByteSink>
1094 inline bool parse_json_byte_array(ser_json& reader, TByteSink& out) {
1095 if (!reader.expect(
'['))
1099 if (reader.consume(
']'))
1104 if (!reader.parse_number(d))
1106 if (d < 0.0 || d > 255.0)
1109 const auto v = (uint32_t)d;
1113 const uint8_t
byte = (uint8_t)v;
1114 out.save_raw(&
byte, 1, serialization_type_id::u8);
1117 if (reader.consume(
','))
1119 if (reader.consume(
']'))