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;
49 enum class JsonDiagSeverity : uint8_t {
58 enum class JsonDiagReason : uint8_t {
72 MissingRuntimeFieldsOrRawPayload,
78 TagComponentUnsupported,
82 MissingComponentStorage,
84 MissingArchetypesSection,
88 UnsupportedFormatVersion,
92 UnknownRuntimeConstant,
94 InvalidRuntimeConstant,
102 UnsupportedPatchValue,
110 struct JsonDiagnostic {
112 JsonDiagSeverity severity = JsonDiagSeverity::Warning;
114 JsonDiagReason reason = JsonDiagReason::None;
122 struct JsonDiagnostics {
124 static constexpr uint32_t MaxDiagPathLength = 1024;
126 static constexpr uint32_t MaxDiagMessageLength = 2048;
129 cnt::darray<JsonDiagnostic> items;
131 bool hasWarnings =
false;
133 bool hasErrors =
false;
140 void add(JsonDiagSeverity severity, JsonDiagReason reason, json_str_view path, json_str_view message) {
142 diag.severity = severity;
143 diag.reason = reason;
144 diag.path.assign(path);
145 diag.message.assign(message);
146 items.push_back(diag);
148 if (severity == JsonDiagSeverity::Warning)
150 else if (severity == JsonDiagSeverity::Error)
158 void add(JsonDiagSeverity severity, JsonDiagReason reason, json_str_view path,
const char* message) {
159 add(severity, reason, path, json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
166 void add(JsonDiagSeverity severity, JsonDiagReason reason,
const char* path,
const char* message) {
167 add(severity, reason, json_str_view(path, (uint32_t)GAIA_STRLEN(path, MaxDiagPathLength)),
168 json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
173 GAIA_NODISCARD
bool has_issues()
const {
174 return hasWarnings || hasErrors;
189 static constexpr uint32_t MaxImplicitKeyLength = 384;
190 static constexpr uint32_t MaxImplicitStringLength = 16u * 1024u * 1024u;
191 static constexpr uint32_t MaxLiteralLength = 256u;
193 enum class CtxType : uint8_t { Object, Array };
196 CtxType type = CtxType::Object;
198 bool needsValue =
false;
202 json_str m_parseScratch;
203 cnt::darray<Ctx> m_ctx;
204 const char* m_it =
nullptr;
205 const char* m_end =
nullptr;
207 GAIA_NODISCARD
static bool parse_hex_quad(
const char* str, uint32_t& value) {
210 const char ch = str[i];
212 if (ch >=
'0' && ch <=
'9')
213 digit = (uint32_t)(ch -
'0');
214 else if (ch >=
'a' && ch <=
'f')
215 digit = (uint32_t)(ch -
'a') + 10;
216 else if (ch >=
'A' && ch <=
'F')
217 digit = (uint32_t)(ch -
'A') + 10;
220 value = (value << 4U) | digit;
225 static void append_utf8(json_str& out, uint32_t codePoint) {
226 if (codePoint <= 0x7fU) {
227 out.append((
char)codePoint);
228 }
else if (codePoint <= 0x7ffU) {
229 out.append((
char)(0xc0U | (codePoint >> 6U)));
230 out.append((
char)(0x80U | (codePoint & 0x3fU)));
231 }
else if (codePoint <= 0xffffU) {
232 out.append((
char)(0xe0U | (codePoint >> 12U)));
233 out.append((
char)(0x80U | ((codePoint >> 6U) & 0x3fU)));
234 out.append((
char)(0x80U | (codePoint & 0x3fU)));
236 out.append((
char)(0xf0U | (codePoint >> 18U)));
237 out.append((
char)(0x80U | ((codePoint >> 12U) & 0x3fU)));
238 out.append((
char)(0x80U | ((codePoint >> 6U) & 0x3fU)));
239 out.append((
char)(0x80U | (codePoint & 0x3fU)));
243 static void add_escaped(json_str& out,
const char* str, uint32_t len) {
245 const char ch = str[i];
269 void before_value() {
273 auto& ctx = m_ctx.back();
274 if (ctx.type == CtxType::Array) {
279 GAIA_ASSERT(ctx.needsValue);
280 ctx.needsValue =
false;
285 ser_json() =
default;
289 ser_json(
const char* json, uint32_t len) {
290 reset_input(json, len);
296 explicit ser_json(
const char (&json)[N]) {
297 static_assert(N > 0);
298 reset_input(json, (uint32_t)(N - 1));
304 void reset_input(
const char* json, uint32_t len) {
305 if (json ==
nullptr) {
318 void reset_input(
const char (&json)[N]) {
319 static_assert(N > 0);
320 reset_input(json, (uint32_t)(N - 1));
326 m_parseScratch.clear();
332 GAIA_NODISCARD
const json_str& str()
const {
338 GAIA_NODISCARD
bool eof()
const {
339 return m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end;
344 GAIA_NODISCARD
char peek()
const {
345 GAIA_ASSERT(m_it !=
nullptr && m_it < m_end);
351 if (m_it ==
nullptr || m_end ==
nullptr)
353 while (m_it < m_end && std::isspace((
unsigned char)*m_it))
357 GAIA_NODISCARD
const char* pos()
const {
361 GAIA_NODISCARD
const char* end()
const {
365 void begin_object() {
368 m_ctx.push_back({CtxType::Object,
true,
false});
372 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
380 m_ctx.push_back({CtxType::Array,
true,
false});
384 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Array);
389 void key(
const char* name, uint32_t len = 0) {
390 GAIA_ASSERT(name !=
nullptr);
391 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
392 auto& ctx = m_ctx.back();
393 GAIA_ASSERT(!ctx.needsValue);
398 ctx.needsValue =
true;
400 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, MaxImplicitKeyLength) : len;
402 add_escaped(m_out, name, l);
408 m_out.append(
"null");
411 void value_bool(
bool v) {
414 m_out.append(
"true");
416 m_out.append(
"false");
419 template <
typename TInt,
typename = std::enable_if_t<std::is_
integral_v<TInt> && !std::is_same_v<TInt,
bool>>>
420 void value_int(TInt v) {
424 if constexpr (std::is_signed_v<TInt>) {
425 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%lld", (
long long)v);
427 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%llu", (
unsigned long long)v);
429 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
432 void value_float(
float v) {
435 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.9g", (double)v);
436 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
439 void value_float(
double v) {
442 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.17g", v);
443 m_out.append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
446 void value_string(
const char* str, uint32_t len = 0) {
447 GAIA_ASSERT(str !=
nullptr);
449 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(str, MaxImplicitStringLength) : len;
451 add_escaped(m_out, str, l);
455 bool consume(
char ch) {
457 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *m_it != ch)
463 bool expect(
char ch) {
467 bool parse_literal(
const char* lit) {
469 if (m_it ==
nullptr || m_end ==
nullptr || lit ==
nullptr)
472 const auto litLen = (uint32_t)GAIA_STRLEN(lit, MaxLiteralLength);
473 if (litLen >= MaxLiteralLength)
475 if ((uint32_t)(m_end - m_it) < litLen)
477 if (memcmp(m_it, lit, litLen) != 0)
483 bool parse_string_view(json_str_view& out,
bool* fromScratch =
nullptr) {
485 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *m_it !=
'"')
489 const char* begin = m_it;
490 bool escaped =
false;
491 m_parseScratch.clear();
492 while (m_it < m_end) {
493 const char ch = *m_it++;
500 const auto prefixLen = (size_t)((m_it - 1) - begin);
502 m_parseScratch.append(begin, (uint32_t)prefixLen);
507 const char esc = *m_it++;
512 m_parseScratch.append(esc);
515 m_parseScratch.append(
'\b');
518 m_parseScratch.append(
'\f');
521 m_parseScratch.append(
'\n');
524 m_parseScratch.append(
'\r');
527 m_parseScratch.append(
'\t');
530 if ((uint32_t)(m_end - m_it) < 4)
532 uint32_t codePoint = 0;
533 if (!parse_hex_quad(m_it, codePoint))
536 if (codePoint >= 0xd800U && codePoint <= 0xdbffU) {
537 if ((uint32_t)(m_end - m_it) < 6 || m_it[0] !=
'\\' || m_it[1] !=
'u')
540 if (!parse_hex_quad(m_it + 2, low) || low < 0xdc00U || low > 0xdfffU)
543 codePoint = 0x10000U + ((codePoint - 0xd800U) << 10U) + (low - 0xdc00U);
544 }
else if (codePoint >= 0xdc00U && codePoint <= 0xdfffU) {
547 append_utf8(m_parseScratch, codePoint);
554 m_parseScratch.append(ch);
557 if (m_it <= begin || m_it > m_end || *(m_it - 1) !=
'"')
561 if (fromScratch !=
nullptr)
563 out = m_parseScratch.view();
565 if (fromScratch !=
nullptr)
566 *fromScratch =
false;
567 out = json_str_view(begin, (uint32_t)((m_it - 1) - begin));
572 bool parse_string(json_str& out) {
574 if (!parse_string_view(view))
580 bool parse_string_eq(
const char* literal) {
582 if (!parse_string_view(view))
584 const auto literalLen = (size_t)GAIA_STRLEN(literal, MaxLiteralLength);
585 if (literalLen >= MaxLiteralLength)
587 return view.size() == literalLen && memcmp(view.data(), literal, literalLen) == 0;
600 parse_integer_token(
bool& negative, uint64_t& magnitude,
bool& overflow,
bool& adjusted,
bool& integerToken) {
606 integerToken =
false;
607 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
610 const char* p = m_it;
615 if (p >= m_end || *p <
'0' || *p >
'9')
617 const char* digitsBegin = p;
618 if (*p ==
'0' && p + 1 < m_end && p[1] >=
'0' && p[1] <=
'9')
621 constexpr uint64_t MaxValue = (std::numeric_limits<uint64_t>::max)();
622 while (p < m_end && *p >=
'0' && *p <=
'9') {
623 const auto digit = (uint64_t)(*p -
'0');
624 if (magnitude > (MaxValue - digit) / 10)
627 magnitude = magnitude * 10 + digit;
630 const char* digitsEnd = p;
632 if (p < m_end && *p ==
'.') {
633 const char* decimalIt = p + 1;
634 if (decimalIt >= m_end || *decimalIt <
'0' || *decimalIt >
'9')
636 while (decimalIt < m_end && *decimalIt >=
'0' && *decimalIt <=
'9')
638 if (decimalIt < m_end && (*decimalIt ==
'e' || *decimalIt ==
'E')) {
640 if (decimalIt < m_end && (*decimalIt ==
'+' || *decimalIt ==
'-'))
642 if (decimalIt >= m_end || *decimalIt <
'0' || *decimalIt >
'9')
647 if (p < m_end && (*p ==
'e' || *p ==
'E')) {
648 const char* exponentIt = p + 1;
649 bool negativeExponent =
false;
650 if (exponentIt < m_end && (*exponentIt ==
'+' || *exponentIt ==
'-')) {
651 negativeExponent = *exponentIt ==
'-';
654 if (exponentIt >= m_end || *exponentIt <
'0' || *exponentIt >
'9')
657 uint32_t exponent = 0;
658 bool exponentOverflow =
false;
659 while (exponentIt < m_end && *exponentIt >=
'0' && *exponentIt <=
'9') {
660 const auto digit = (uint32_t)(*exponentIt -
'0');
661 if (exponent > (UINT32_MAX - digit) / 10)
662 exponentOverflow =
true;
663 else if (!exponentOverflow)
664 exponent = exponent * 10 + digit;
668 if (negativeExponent && (exponentOverflow || exponent != 0)) {
669 const auto digitCount = (uint32_t)(digitsEnd - digitsBegin);
670 const auto keptCount = exponentOverflow || exponent >= digitCount ? 0U : digitCount - exponent;
673 for (uint32_t i = 0; i < keptCount; ++i) {
674 const auto digit = (uint64_t)(digitsBegin[i] -
'0');
675 if (magnitude > (MaxValue - digit) / 10)
678 magnitude = magnitude * 10 + digit;
680 for (uint32_t i = keptCount; i < digitCount; ++i)
681 adjusted = adjusted || digitsBegin[i] !=
'0';
682 }
else if (exponentOverflow || exponent > 19) {
683 overflow = magnitude != 0;
685 while (exponent-- > 0) {
686 if (magnitude > MaxValue / 10) {
697 magnitude = MaxValue;
704 bool parse_number(
double& value) {
706 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
709 char* pEnd =
nullptr;
710 value = std::strtod(m_it, &pEnd);
717 bool parse_bool(
bool& value) {
718 if (parse_literal(
"true")) {
722 if (parse_literal(
"false")) {
730 return parse_literal(
"null");
735 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
746 if (!parse_string_view(key))
782 return parse_string_view(tmp);
785 if (*m_it ==
't' || *m_it ==
'f') {
787 return parse_bool(v);
794 return parse_number(v);
800 template <
typename T>
801 inline void copy_field_bytes(uint8_t* pFieldData, uint32_t size,
const T& v) {
802 memcpy(pFieldData, &v, size <
sizeof(v) ? size : (uint32_t)sizeof(v));
805 template <
typename TInt>
806 inline bool read_runtime_field_json_int(ser_json& reader, uint8_t* pFieldData, uint32_t size,
bool& ok) {
807 bool negative =
false;
808 uint64_t magnitude = 0;
809 bool overflow =
false;
810 bool adjusted =
false;
811 bool integerToken =
false;
812 if (!reader.parse_integer_token(negative, magnitude, overflow, adjusted, integerToken))
819 if constexpr (std::is_signed_v<TInt>) {
820 constexpr auto MaxPositive = (uint64_t)(std::numeric_limits<TInt>::max)();
821 constexpr auto MaxNegative = MaxPositive + 1;
823 if (overflow || magnitude > MaxNegative) {
824 value = (std::numeric_limits<TInt>::lowest)();
826 }
else if (magnitude == MaxNegative) {
827 value = (std::numeric_limits<TInt>::lowest)();
829 value = (TInt) - (int64_t)magnitude;
831 }
else if (overflow || magnitude > MaxPositive) {
832 value = (std::numeric_limits<TInt>::max)();
835 value = (TInt)magnitude;
838 constexpr auto MaxValue = (uint64_t)(std::numeric_limits<TInt>::max)();
840 if (overflow || magnitude != 0)
842 }
else if (overflow || magnitude > MaxValue) {
843 value = (std::numeric_limits<TInt>::max)();
846 value = (TInt)magnitude;
850 copy_field_bytes(pFieldData, size, value);
855 if (!reader.parse_number(d))
858 if (!std::isfinite(d)) {
861 copy_field_bytes(pFieldData, size, v);
865 double clamped = std::trunc(d);
869 constexpr auto minVal = (double)(std::numeric_limits<TInt>::lowest)();
870 constexpr auto maxVal = (double)(std::numeric_limits<TInt>::max)();
871 if (clamped < minVal) {
874 }
else if constexpr (
sizeof(TInt) ==
sizeof(uint64_t)) {
875 if (clamped >= maxVal) {
876 const TInt v = (std::numeric_limits<TInt>::max)();
877 copy_field_bytes(pFieldData, size, v);
881 }
else if (clamped > maxVal) {
886 const TInt v = (TInt)clamped;
887 copy_field_bytes(pFieldData, size, v);
891 template <
typename TFloat>
892 inline bool read_runtime_field_json_float(ser_json& reader, uint8_t* pFieldData, uint32_t size,
bool& ok) {
894 if (!reader.parse_number(d))
897 if (!std::isfinite(d)) {
899 const TFloat v = (TFloat)0;
900 copy_field_bytes(pFieldData, size, v);
904 const TFloat v = (TFloat)d;
905 copy_field_bytes(pFieldData, size, v);
910 write_runtime_field_json(ser_json& writer,
const uint8_t* pFieldData, serialization_type_id type, uint32_t size) {
912 case serialization_type_id::s8: {
914 memcpy(&v, pFieldData,
sizeof(v));
918 case serialization_type_id::u8: {
920 memcpy(&v, pFieldData,
sizeof(v));
924 case serialization_type_id::s16: {
926 memcpy(&v, pFieldData,
sizeof(v));
930 case serialization_type_id::u16: {
932 memcpy(&v, pFieldData,
sizeof(v));
936 case serialization_type_id::s32: {
938 memcpy(&v, pFieldData,
sizeof(v));
942 case serialization_type_id::u32: {
944 memcpy(&v, pFieldData,
sizeof(v));
948 case serialization_type_id::s64: {
950 memcpy(&v, pFieldData,
sizeof(v));
954 case serialization_type_id::u64: {
956 memcpy(&v, pFieldData,
sizeof(v));
960 case serialization_type_id::b: {
962 memcpy(&v, pFieldData,
sizeof(v));
963 writer.value_bool(v);
966 case serialization_type_id::f32: {
968 memcpy(&v, pFieldData,
sizeof(v));
969 writer.value_float(v);
972 case serialization_type_id::f64: {
974 memcpy(&v, pFieldData,
sizeof(v));
975 writer.value_float(v);
978 case serialization_type_id::c8: {
979 const auto len = (uint32_t)GAIA_STRLEN((
const char*)pFieldData, size);
980 writer.value_string((
const char*)pFieldData, len);
989 inline bool read_runtime_field_json(
990 ser_json& reader, uint8_t* pFieldData, serialization_type_id type, uint32_t size,
bool& ok) {
991 if (reader.parse_null()) {
997 case serialization_type_id::s8: {
998 return read_runtime_field_json_int<int8_t>(reader, pFieldData, size, ok);
1000 case serialization_type_id::u8: {
1001 return read_runtime_field_json_int<uint8_t>(reader, pFieldData, size, ok);
1003 case serialization_type_id::s16: {
1004 return read_runtime_field_json_int<int16_t>(reader, pFieldData, size, ok);
1006 case serialization_type_id::u16: {
1007 return read_runtime_field_json_int<uint16_t>(reader, pFieldData, size, ok);
1009 case serialization_type_id::s32: {
1010 return read_runtime_field_json_int<int32_t>(reader, pFieldData, size, ok);
1012 case serialization_type_id::u32: {
1013 return read_runtime_field_json_int<uint32_t>(reader, pFieldData, size, ok);
1015 case serialization_type_id::s64: {
1016 return read_runtime_field_json_int<int64_t>(reader, pFieldData, size, ok);
1018 case serialization_type_id::u64: {
1019 return read_runtime_field_json_int<uint64_t>(reader, pFieldData, size, ok);
1021 case serialization_type_id::f32: {
1022 return read_runtime_field_json_float<float>(reader, pFieldData, size, ok);
1024 case serialization_type_id::f64: {
1025 return read_runtime_field_json_float<double>(reader, pFieldData, size, ok);
1027 case serialization_type_id::b: {
1029 if (!reader.parse_bool(v))
1031 copy_field_bytes(pFieldData, size, v);
1034 case serialization_type_id::c8: {
1036 if (!reader.parse_string_view(str))
1043 memset(pFieldData, 0, size);
1044 const auto maxLen = size > 0 ? size - 1 : 0;
1045 const auto strLen = (uint32_t)str.size();
1046 const auto copyLen = strLen < maxLen ? strLen : maxLen;
1047 if (strLen > maxLen)
1050 memcpy(pFieldData, str.data(), copyLen);
1055 return reader.skip_value();
1059 template <
typename TByteSink>
1060 inline bool parse_json_byte_array(ser_json& reader, TByteSink& out) {
1061 if (!reader.expect(
'['))
1065 if (reader.consume(
']'))
1070 if (!reader.parse_number(d))
1072 if (d < 0.0 || d > 255.0)
1075 const auto v = (uint32_t)d;
1079 const uint8_t
byte = (uint8_t)v;
1080 out.save_raw(&
byte, 1, serialization_type_id::u8);
1083 if (reader.consume(
','))
1085 if (reader.consume(
']'))