2#include "gaia/config/config.h"
13#include "gaia/cnt/darray.h"
14#include "gaia/core/utility.h"
15#include "gaia/ser/ser_buffer_binary.h"
16#include "gaia/util/str.h"
20 using json_str_view = util::str_view;
21 using json_str = util::str;
23 enum JsonSaveFlags : uint32_t {
25 BinarySnapshot = 1u << 0,
26 RawFallback = 1u << 1,
27 Default = BinarySnapshot | RawFallback
30 enum class JsonDiagSeverity : uint8_t { Info, Warning, Error };
31 enum class JsonDiagReason : uint8_t {
38 MissingSchemaOrRawPayload,
41 TagComponentUnsupported,
43 MissingComponentStorage,
44 MissingArchetypesSection,
46 UnsupportedFormatVersion,
51 JsonDiagSeverity severity = JsonDiagSeverity::Warning;
52 JsonDiagReason reason = JsonDiagReason::None;
58 static constexpr uint32_t MaxDiagPathLength = 1024;
59 static constexpr uint32_t MaxDiagMessageLength = 2048;
62 bool hasWarnings =
false;
63 bool hasErrors =
false;
67 diag.severity = severity;
70 diag.message.
assign(message);
71 items.push_back(diag);
73 if (severity == JsonDiagSeverity::Warning)
75 else if (severity == JsonDiagSeverity::Error)
78 void add(JsonDiagSeverity severity, JsonDiagReason reason,
json_str_view path,
const char* message) {
79 add(severity, reason, path,
json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
81 void add(JsonDiagSeverity severity, JsonDiagReason reason,
const char* path,
const char* message) {
82 add(severity, reason,
json_str_view(path, (uint32_t)GAIA_STRLEN(path, MaxDiagPathLength)),
83 json_str_view(message, (uint32_t)GAIA_STRLEN(message, MaxDiagMessageLength)));
86 GAIA_NODISCARD
bool has_issues()
const {
87 return hasWarnings || hasErrors;
102 static constexpr uint32_t MaxImplicitKeyLength = 384;
103 static constexpr uint32_t MaxImplicitStringLength = 16u * 1024u * 1024u;
104 static constexpr uint32_t MaxLiteralLength = 256u;
106 enum class CtxType : uint8_t { Object, Array };
109 CtxType type = CtxType::Object;
111 bool needsValue =
false;
117 const char* m_it =
nullptr;
118 const char* m_end =
nullptr;
120 static void add_escaped(
json_str& out,
const char*
str, uint32_t len) {
122 const char ch =
str[i];
146 void before_value() {
150 auto& ctx = m_ctx.back();
151 if (ctx.type == CtxType::Array) {
156 GAIA_ASSERT(ctx.needsValue);
157 ctx.needsValue =
false;
163 ser_json(
const char* json, uint32_t len) {
167 explicit ser_json(
const char (&json)[N]) {
168 static_assert(N > 0);
174 if (json ==
nullptr) {
185 static_assert(N > 0);
192 m_parseScratch.
clear();
202 GAIA_NODISCARD
bool eof()
const {
203 return m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end;
207 GAIA_NODISCARD
char peek()
const {
208 GAIA_ASSERT(m_it !=
nullptr && m_it < m_end);
214 if (m_it ==
nullptr || m_end ==
nullptr)
216 while (m_it < m_end && std::isspace((
unsigned char)*m_it))
220 GAIA_NODISCARD
const char* pos()
const {
224 GAIA_NODISCARD
const char* end()
const {
228 void begin_object() {
231 m_ctx.push_back({CtxType::Object,
true,
false});
235 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
243 m_ctx.push_back({CtxType::Array,
true,
false});
247 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Array);
252 void key(
const char* name, uint32_t len = 0) {
253 GAIA_ASSERT(name !=
nullptr);
254 GAIA_ASSERT(!m_ctx.empty() && m_ctx.back().type == CtxType::Object);
255 auto& ctx = m_ctx.back();
256 GAIA_ASSERT(!ctx.needsValue);
261 ctx.needsValue =
true;
263 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, MaxImplicitKeyLength) : len;
265 add_escaped(m_out, name, l);
274 void value_bool(
bool v) {
282 template <
typename TInt,
typename = std::enable_if_t<std::is_
integral_v<TInt> && !std::is_same_v<TInt,
bool>>>
283 void value_int(TInt v) {
287 if constexpr (std::is_signed_v<TInt>) {
288 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%lld", (
long long)v);
290 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%llu", (
unsigned long long)v);
292 m_out.
append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
295 void value_float(
float v) {
298 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.9g", (double)v);
299 m_out.
append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
302 void value_float(
double v) {
305 (void)GAIA_STRFMT(buff,
sizeof(buff),
"%.17g", v);
306 m_out.
append(buff, (uint32_t)GAIA_STRLEN(buff, (
size_t)
sizeof(buff)));
309 void value_string(
const char*
str, uint32_t len = 0) {
310 GAIA_ASSERT(
str !=
nullptr);
312 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(
str, MaxImplicitStringLength) : len;
314 add_escaped(m_out,
str, l);
318 bool consume(
char ch) {
320 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *m_it != ch)
326 bool expect(
char ch) {
330 bool parse_literal(
const char* lit) {
332 if (m_it ==
nullptr || m_end ==
nullptr || lit ==
nullptr)
335 const auto litLen = (uint32_t)GAIA_STRLEN(lit, MaxLiteralLength);
336 if (litLen >= MaxLiteralLength)
338 if ((uint32_t)(m_end - m_it) < litLen)
340 if (memcmp(m_it, lit, litLen) != 0)
346 bool parse_string_view(json_str_view& out,
bool* fromScratch =
nullptr) {
348 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end || *m_it !=
'"')
352 const char* begin = m_it;
353 bool escaped =
false;
354 m_parseScratch.
clear();
355 while (m_it < m_end) {
356 const char ch = *m_it++;
363 const auto prefixLen = (size_t)((m_it - 1) - begin);
365 m_parseScratch.
append(begin, (uint32_t)prefixLen);
370 const char esc = *m_it++;
375 m_parseScratch.
append(esc);
378 m_parseScratch.
append(
'\b');
381 m_parseScratch.
append(
'\f');
384 m_parseScratch.
append(
'\n');
387 m_parseScratch.
append(
'\r');
390 m_parseScratch.
append(
'\t');
393 if ((uint32_t)(m_end - m_it) < 4)
396 const char h = m_it[i];
397 const bool isHex = (h >=
'0' && h <=
'9') || (h >=
'a' && h <=
'f') || (h >=
'A' && h <=
'F');
402 m_parseScratch.
append(
'?');
409 m_parseScratch.
append(ch);
412 if (m_it <= begin || m_it > m_end || *(m_it - 1) !=
'"')
416 if (fromScratch !=
nullptr)
418 out = m_parseScratch.
view();
420 if (fromScratch !=
nullptr)
421 *fromScratch =
false;
422 out = json_str_view(begin, (uint32_t)((m_it - 1) - begin));
427 bool parse_string(json_str& out) {
429 if (!parse_string_view(view))
435 bool parse_string_eq(
const char* literal) {
437 if (!parse_string_view(view))
439 const auto literalLen = (size_t)GAIA_STRLEN(literal, MaxLiteralLength);
440 if (literalLen >= MaxLiteralLength)
442 return view.size() == literalLen && memcmp(view.data(), literal, literalLen) == 0;
445 bool parse_number(
double& value) {
447 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
450 char* pEnd =
nullptr;
451 value = std::strtod(m_it, &pEnd);
458 bool parse_bool(
bool& value) {
459 if (parse_literal(
"true")) {
463 if (parse_literal(
"false")) {
471 return parse_literal(
"null");
476 if (m_it ==
nullptr || m_end ==
nullptr || m_it >= m_end)
487 if (!parse_string_view(key))
523 return parse_string_view(tmp);
526 if (*m_it ==
't' || *m_it ==
'f') {
528 return parse_bool(v);
535 return parse_number(v);
540 template <
typename T>
541 inline void copy_field_bytes(uint8_t* pFieldData, uint32_t size,
const T& v) {
542 memcpy(pFieldData, &v, size <
sizeof(v) ? size : (uint32_t)sizeof(v));
545 template <
typename TInt>
546 inline bool read_schema_field_json_int(ser_json& reader, uint8_t* pFieldData, uint32_t size,
bool& ok) {
548 if (!reader.parse_number(d))
551 if (!std::isfinite(d)) {
554 copy_field_bytes(pFieldData, size, v);
558 double clamped = std::trunc(d);
562 constexpr auto minVal = (double)(std::numeric_limits<TInt>::lowest)();
563 constexpr auto maxVal = (double)(std::numeric_limits<TInt>::max)();
564 if (clamped < minVal) {
567 }
else if (clamped > maxVal) {
572 const TInt v = (TInt)clamped;
573 copy_field_bytes(pFieldData, size, v);
577 template <
typename TFloat>
578 inline bool read_schema_field_json_float(ser_json& reader, uint8_t* pFieldData, uint32_t size,
bool& ok) {
580 if (!reader.parse_number(d))
583 if (!std::isfinite(d)) {
585 const TFloat v = (TFloat)0;
586 copy_field_bytes(pFieldData, size, v);
590 const TFloat v = (TFloat)d;
591 copy_field_bytes(pFieldData, size, v);
596 write_schema_field_json(ser_json& writer,
const uint8_t* pFieldData, serialization_type_id type, uint32_t size) {
598 case serialization_type_id::s8: {
600 memcpy(&v, pFieldData,
sizeof(v));
604 case serialization_type_id::u8: {
606 memcpy(&v, pFieldData,
sizeof(v));
610 case serialization_type_id::s16: {
612 memcpy(&v, pFieldData,
sizeof(v));
616 case serialization_type_id::u16: {
618 memcpy(&v, pFieldData,
sizeof(v));
622 case serialization_type_id::s32: {
624 memcpy(&v, pFieldData,
sizeof(v));
628 case serialization_type_id::u32: {
630 memcpy(&v, pFieldData,
sizeof(v));
634 case serialization_type_id::s64: {
636 memcpy(&v, pFieldData,
sizeof(v));
640 case serialization_type_id::u64: {
642 memcpy(&v, pFieldData,
sizeof(v));
646 case serialization_type_id::b: {
648 memcpy(&v, pFieldData,
sizeof(v));
649 writer.value_bool(v);
652 case serialization_type_id::f32: {
654 memcpy(&v, pFieldData,
sizeof(v));
655 writer.value_float(v);
658 case serialization_type_id::f64: {
660 memcpy(&v, pFieldData,
sizeof(v));
661 writer.value_float(v);
664 case serialization_type_id::c8: {
665 const auto len = (uint32_t)GAIA_STRLEN((
const char*)pFieldData, size);
666 writer.value_string((
const char*)pFieldData, len);
675 inline bool read_schema_field_json(
676 ser_json& reader, uint8_t* pFieldData, serialization_type_id type, uint32_t size,
bool& ok) {
677 if (reader.parse_null()) {
683 case serialization_type_id::s8: {
684 return read_schema_field_json_int<int8_t>(reader, pFieldData, size, ok);
686 case serialization_type_id::u8: {
687 return read_schema_field_json_int<uint8_t>(reader, pFieldData, size, ok);
689 case serialization_type_id::s16: {
690 return read_schema_field_json_int<int16_t>(reader, pFieldData, size, ok);
692 case serialization_type_id::u16: {
693 return read_schema_field_json_int<uint16_t>(reader, pFieldData, size, ok);
695 case serialization_type_id::s32: {
696 return read_schema_field_json_int<int32_t>(reader, pFieldData, size, ok);
698 case serialization_type_id::u32: {
699 return read_schema_field_json_int<uint32_t>(reader, pFieldData, size, ok);
701 case serialization_type_id::s64: {
702 return read_schema_field_json_int<int64_t>(reader, pFieldData, size, ok);
704 case serialization_type_id::u64: {
705 return read_schema_field_json_int<uint64_t>(reader, pFieldData, size, ok);
707 case serialization_type_id::f32: {
708 return read_schema_field_json_float<float>(reader, pFieldData, size, ok);
710 case serialization_type_id::f64: {
711 return read_schema_field_json_float<double>(reader, pFieldData, size, ok);
713 case serialization_type_id::b: {
715 if (!reader.parse_bool(v))
717 copy_field_bytes(pFieldData, size, v);
720 case serialization_type_id::c8: {
722 if (!reader.parse_string_view(str))
729 memset(pFieldData, 0, size);
730 const auto maxLen = size > 0 ? size - 1 : 0;
731 const auto strLen = (uint32_t)str.size();
732 const auto copyLen = strLen < maxLen ? strLen : maxLen;
736 memcpy(pFieldData, str.data(), copyLen);
741 return reader.skip_value();
745 template <
typename TByteSink>
746 inline bool parse_json_byte_array(ser_json& reader, TByteSink& out) {
747 if (!reader.expect(
'['))
751 if (reader.consume(
']'))
756 if (!reader.parse_number(d))
758 if (d < 0.0 || d > 255.0)
761 const auto v = (uint32_t)d;
765 const uint8_t
byte = (uint8_t)v;
766 out.save_raw(&
byte, 1, serialization_type_id::u8);
769 if (reader.consume(
','))
771 if (reader.consume(
']'))
Array with variable size of elements of type.
Definition darray_impl.h:25
Lightweight JSON serializer/deserializer.
Definition ser_json.h:101
GAIA_NODISCARD const json_str & str() const
Returns currently emitted output text.
Definition ser_json.h:197
void ws()
Skips whitespace in parser input.
Definition ser_json.h:213
GAIA_NODISCARD bool eof() const
Returns true if parser has reached end of input.
Definition ser_json.h:202
void clear()
Clears output JSON text and writer context.
Definition ser_json.h:190
void reset_input(const char *json, uint32_t len)
Sets an input JSON buffer for parsing.
Definition ser_json.h:173
GAIA_NODISCARD char peek() const
Returns next non-consumed character.
Definition ser_json.h:207
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Lightweight non-owning string view over a character sequence.
Definition str.h:13
Lightweight owning string container with explicit length semantics (no implicit null terminator).
Definition str.h:331
void append(const char *data, uint32_t size)
Appends size characters from data.
Definition str.h:389
void clear()
Removes all characters from the string.
Definition str.h:352
GAIA_NODISCARD str_view view() const
Returns a non-owning view over the current contents.
Definition str.h:443
void assign(const char *data, uint32_t size)
Replaces contents with size characters from data.
Definition str.h:365