From c58f2c7dbdde28eeda5fcbeb92e61ceacd65c5c2 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Tue, 31 Dec 2024 21:31:52 -0600 Subject: [PATCH] Add nlohmann json replacement --- src/core/CMakeLists.txt | 2 + src/core/json.cpp | 1809 +++++++++++++++++++++++++++++++++++++++ src/core/json.hpp | 533 ++++++++++++ src/core/json_macro.inl | 185 ++++ 4 files changed, 2529 insertions(+) create mode 100644 src/core/json.cpp create mode 100644 src/core/json.hpp create mode 100644 src/core/json_macro.inl diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 20cfd67ee..5c91e1d66 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,6 +2,8 @@ target_sources(SRB2SDL2 PRIVATE hash_map.hpp hash_set.cpp hash_set.hpp + json.cpp + json.hpp memory.cpp memory.h spmc_queue.hpp diff --git a/src/core/json.cpp b/src/core/json.cpp new file mode 100644 index 000000000..d123637e3 --- /dev/null +++ b/src/core/json.cpp @@ -0,0 +1,1809 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Ronald "Eidolon" Kinard +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include "json.hpp" + +#include +#include +#include +#include + +#include + +using namespace srb2; + +JsonError::JsonError(const std::string& what) : std::runtime_error(what) {} +JsonError::JsonError(const char* what) : std::runtime_error(what) {} +JsonError::JsonError(const JsonError&) noexcept = default; +JsonParseError::JsonParseError(const std::string& what) : JsonError(what) {} +JsonParseError::JsonParseError(const char* what) : JsonError(what) {} +JsonParseError::JsonParseError(const JsonParseError&) noexcept = default; + +JsonValue::JsonValue() + : type_(Type::kNull) +{} + +JsonValue::JsonValue(const JsonValue& r) + : type_(Type::kNull) +{ + *this = r; +} + +JsonValue::JsonValue(JsonValue&& r) noexcept + : type_(Type::kNull) +{ + *this = std::move(r); +} + +JsonValue& JsonValue::operator=(const JsonValue& r) +{ + switch (type_) + { + case Type::kString: + string_.~String(); + break; + case Type::kArray: + array_.~JsonArray(); + break; + case Type::kObject: + object_.~JsonObject(); + break; + default: + break; + } + type_ = r.type_; + switch (type_) + { + case Type::kNull: + break; + case Type::kBoolean: + boolean_ = r.boolean_; + break; + case Type::kNumber: + number_ = r.number_; + break; + case Type::kString: + new (&string_) String(r.string_); + break; + case Type::kArray: + new (&array_) JsonArray(r.array_); + break; + case Type::kObject: + new (&object_) JsonObject(r.object_); + break; + } + return *this; +} + +JsonValue& JsonValue::operator=(JsonValue&& r) noexcept +{ + switch (type_) + { + case Type::kString: + string_.~String(); + break; + case Type::kArray: + array_.~JsonArray(); + break; + case Type::kObject: + object_.~JsonObject(); + break; + default: + break; + } + type_ = r.type_; + switch (type_) + { + case Type::kBoolean: + boolean_ = r.boolean_; + break; + case Type::kNumber: + number_ = r.number_; + break; + case Type::kString: + new (&string_) String(std::move(r.string_)); + break; + case Type::kArray: + new (&array_) JsonArray(std::move(r.array_)); + break; + case Type::kObject: + new (&object_) JsonObject(std::move(r.object_)); + break; + default: + break; + } + switch (r.type_) + { + case Type::kString: + r.string_.~String(); + break; + case Type::kArray: + r.array_.~JsonArray(); + break; + case Type::kObject: + r.object_.~JsonObject(); + default: + break; + } + r.type_ = Type::kNull; + return *this; +} + +JsonValue::~JsonValue() +{ + switch (type_) + { + case Type::kString: + string_.~String(); + break; + case Type::kArray: + array_.~JsonArray(); + break; + case Type::kObject: + object_.~JsonObject(); + break; + default: + break; + } + type_ = Type::kNull; +} + +template <> bool JsonValue::get() const { return type_ == Type::kBoolean ? boolean_ : false; } +template <> int8_t JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> int16_t JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> int32_t JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> int64_t JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> uint8_t JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> uint16_t JsonValue::get() const { return type_ == Type::kNumber ? (uint16_t)number_ : 0; } +template <> uint32_t JsonValue::get() const { return type_ == Type::kNumber ? (uint32_t)number_ : 0; } +template <> uint64_t JsonValue::get() const { return type_ == Type::kNumber ? (uint64_t)number_ : 0; } +template <> float JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> double JsonValue::get() const { return type_ == Type::kNumber ? number_ : 0; } +template <> String JsonValue::get() const { return type_ == Type::kString ? string_ : String(); } +template <> std::string JsonValue::get() const { return type_ == Type::kString ? static_cast(string_) : std::string(); } +template <> std::string_view JsonValue::get() const { return type_ == Type::kString ? static_cast(string_) : std::string_view(); } +template <> JsonArray JsonValue::get() const { return type_ == Type::kArray ? array_ : JsonArray(); } +template <> JsonObject JsonValue::get() const { return type_ == Type::kObject ? object_ : JsonObject(); } +template <> JsonValue JsonValue::get() const { return *this; } + +JsonArray& JsonValue::as_array() +{ + if (!is_array()) throw JsonError("accessing non-array as array"); + return array_; +} + +const JsonArray& JsonValue::as_array() const +{ + if (!is_array()) throw JsonError("accessing non-array as array"); + return array_; +} + +JsonObject& JsonValue::as_object() +{ + if (!is_object()) throw JsonError("accessing non-object as object"); + return object_; +} + +const JsonObject& JsonValue::as_object() const +{ + if (!is_object()) throw JsonError("accessing non-array as object"); + return object_; +} + +JsonValue& JsonValue::at(uint32_t i) +{ + JsonArray& arr = as_array(); + return arr.at(i); +} + +const JsonValue& JsonValue::at(uint32_t i) const +{ + const JsonArray& arr = as_array(); + return arr.at(i); +} + +JsonValue& JsonValue::at(const char* key) +{ + JsonObject& obj = as_object(); + return obj.at(key); +} + +JsonValue& JsonValue::at(const String& key) +{ + JsonObject& obj = as_object(); + return obj.at(key); +} + +const JsonValue& JsonValue::at(const char* key) const +{ + const JsonObject& obj = as_object(); + return obj.at(key); +} + +const JsonValue& JsonValue::at(const String& key) const +{ + const JsonObject& obj = as_object(); + return obj.at(key); +} + +JsonValue& JsonValue::operator[](uint32_t i) +{ + if (is_null() && !is_array()) + { + *this = JsonArray(); + } + else if (!is_array()) + { + throw JsonError("indexing non-array as array"); + } + JsonArray& arr = as_array(); + return arr[i]; +} + +JsonValue& JsonValue::operator[](const char* key) +{ + if (is_null() && !is_object()) + { + *this = JsonObject(); + } + else if (!is_object()) + { + throw JsonError("indexing non-object as object"); + } + JsonObject& obj = as_object(); + return obj[key]; +} + +JsonValue& JsonValue::operator[](const String& key) +{ + if (is_null() && !is_object()) + { + *this = JsonObject(); + } + else if (!is_object()) + { + throw JsonError("indexing non-object as object"); + } + JsonObject& obj = as_object(); + return obj[key]; +} + +bool JsonValue::contains(const String& key) const +{ + return contains(key.c_str()); +} + +bool JsonValue::contains(const char* key) const +{ + if (!is_object()) + { + return false; + } + const JsonObject& obj = as_object(); + return obj.find(key) != obj.end(); +} + +void JsonValue::to_json(JsonValue& to) const +{ + to = *this; +} + +void JsonValue::from_json(const JsonValue& from) +{ + *this = from; +} + +String JsonValue::to_json_string() const +{ + String ret; + + switch (type_) + { + case Type::kNull: + ret = "null"; + break; + case Type::kBoolean: + ret = boolean_ ? "true" : "false"; + break; + case Type::kNumber: + if (std::isnan(number_) || std::isinf(number_)) + { + ret = "null"; + break; + } + ret = fmt::format("{}", number_); + break; + case Type::kString: + ret = "\""; + for (auto c : string_) + { + switch (c) + { + case '"': ret.append("\\\""); break; + case '\\': ret.append("\\\\"); break; + case '\b': ret.append("\\b"); break; + case '\f': ret.append("\\f"); break; + case '\n': ret.append("\\n"); break; + case '\r': ret.append("\\r"); break; + case '\t': ret.append("\\t"); break; + default: + if (c >= 0x00 && c <= 0x1f) + { + ret.append(fmt::format("\\u{:04x}", c)); + } + else + { + ret.push_back(c); + } + } + } + ret.append("\""); + break; + case Type::kArray: + { + ret = "["; + for (auto itr = array_.begin(); itr != array_.end(); itr++) + { + ret.append(JsonValue(*itr).to_json_string()); + if (std::next(itr) != array_.end()) + { + ret.append(","); + } + } + ret.append("]"); + break; + } + case Type::kObject: + { + ret = "{"; + for (auto itr = object_.begin(); itr != object_.end(); itr++) + { + ret.append(JsonValue(itr->first).to_json_string()); + ret.append(":"); + ret.append(JsonValue(itr->second).to_json_string()); + auto next = itr; + ++next; + if (next != object_.end()) + { + ret.append(","); + } + } + ret.append("}"); + return ret; + } + } + return ret; +} + +srb2::Vector JsonValue::to_ubjson() const +{ + srb2::Vector out; + + do_to_ubjson(out); + + return out; +} + +void JsonValue::value_to_ubjson(srb2::Vector& out) +{ + out.push_back(std::byte { 'Z' }); +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, bool value) +{ + out.push_back(value ? std::byte { 'T' } : std::byte { 'F' }); +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, double value) +{ + if (std::isnan(value) || std::isinf(value)) + { + out.push_back(std::byte { 'Z' }); + return; + } + + uint64_t num_as_int; + std::byte buf[8]; + out.push_back(std::byte { 'D' }); + std::memcpy(&num_as_int, &value, sizeof(double)); + buf[0] = (std::byte)((num_as_int & 0xFF00000000000000) >> 56); + buf[1] = (std::byte)((num_as_int & 0x00FF000000000000) >> 48); + buf[2] = (std::byte)((num_as_int & 0x0000FF0000000000) >> 40); + buf[3] = (std::byte)((num_as_int & 0x000000FF00000000) >> 32); + buf[4] = (std::byte)((num_as_int & 0x00000000FF000000) >> 24); + buf[5] = (std::byte)((num_as_int & 0x0000000000FF0000) >> 16); + buf[6] = (std::byte)((num_as_int & 0x000000000000FF00) >> 8); + buf[7] = (std::byte)((num_as_int & 0x00000000000000FF)); + for (int i = 0; i < 8; i++) + { + out.push_back(buf[i]); + } +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, uint64_t value) +{ + + std::byte buf[8]; + int64_t string_len_i64; + int32_t string_len_32; + int16_t string_len_16; + uint8_t string_len_8; + if (value < std::numeric_limits().max()) + { + string_len_8 = value; + out.push_back(std::byte { 'U' }); + out.push_back((std::byte)(string_len_8)); + } + else if (value < std::numeric_limits().max()) + { + string_len_16 = value; + buf[0] = (std::byte)((string_len_16 & 0xFF00) >> 8); + buf[1] = (std::byte)((string_len_16 & 0x00FF) >> 0); + out.push_back(std::byte { 'I' }); + out.push_back(buf[0]); + out.push_back(buf[1]); + } + else if (value < std::numeric_limits().max()) + { + string_len_32 = value; + buf[0] = (std::byte)((string_len_32 & 0xFF000000) >> 24); + buf[1] = (std::byte)((string_len_32 & 0x00FF0000) >> 16); + buf[2] = (std::byte)((string_len_32 & 0x0000FF00) >> 8); + buf[3] = (std::byte)((string_len_32 & 0x000000FF)); + out.push_back(std::byte { 'l' }); + out.push_back(buf[0]); + out.push_back(buf[1]); + out.push_back(buf[2]); + out.push_back(buf[3]); + } + else if (value < std::numeric_limits().max()) + { + string_len_i64 = value; + buf[0] = (std::byte)((string_len_i64 & 0xFF00000000000000) >> 56); + buf[1] = (std::byte)((string_len_i64 & 0x00FF000000000000) >> 48); + buf[2] = (std::byte)((string_len_i64 & 0x0000FF0000000000) >> 40); + buf[3] = (std::byte)((string_len_i64 & 0x000000FF00000000) >> 32); + buf[4] = (std::byte)((string_len_i64 & 0x00000000FF000000) >> 24); + buf[5] = (std::byte)((string_len_i64 & 0x0000000000FF0000) >> 16); + buf[6] = (std::byte)((string_len_i64 & 0x000000000000FF00) >> 8); + buf[7] = (std::byte)((string_len_i64 & 0x00000000000000FF)); + out.push_back(std::byte { 'L' }); + out.push_back(buf[0]); + out.push_back(buf[1]); + out.push_back(buf[2]); + out.push_back(buf[3]); + out.push_back(buf[4]); + out.push_back(buf[5]); + out.push_back(buf[6]); + out.push_back(buf[7]); + } + else + { + throw JsonParseError("inexpressible integer"); + } +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, const String& value, bool include_type) +{ + if (include_type) + { + out.push_back(std::byte { 'S' }); + } + + value_to_ubjson(out, static_cast(value.size())); + for (auto c : value) + { + out.push_back((std::byte)(c)); + } +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, const JsonArray& value) +{ + out.push_back(std::byte { '[' }); + if (value.empty()) + { + out.push_back(std::byte { ']' }); + return; + } + + out.push_back(std::byte { '#' }); + value_to_ubjson(out, (uint64_t)value.size()); + for (auto& v : value) + { + v.do_to_ubjson(out); + } + + // Don't emit end because we included size prefix +} + +void JsonValue::value_to_ubjson(srb2::Vector& out, const JsonObject& value) +{ + out.push_back(std::byte { '{' }); + if (value.empty()) + { + out.push_back(std::byte { '}' }); + return; + } + + out.push_back(std::byte { '#' }); + value_to_ubjson(out, static_cast(value.size())); + for (auto& v : value) + { + value_to_ubjson(out, v.first, false); + v.second.do_to_ubjson(out); + } + + // Don't emit end because we included size prefix +} + +void JsonValue::do_to_ubjson(srb2::Vector& out) const +{ + switch (type_) + { + case Type::kNull: + value_to_ubjson(out); + break; + case Type::kBoolean: + value_to_ubjson(out, boolean_); + break; + case Type::kNumber: + value_to_ubjson(out, number_); + break; + case Type::kString: + value_to_ubjson(out, string_, true); + break; + case Type::kArray: + value_to_ubjson(out, array_); + break; + case Type::kObject: + value_to_ubjson(out, object_); + break; + default: + break; + } +} + +static uint8_t u8_from_ubjson(tcb::span& ubjson) +{ + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + uint8_t ret = std::to_integer(ubjson[0]); + ubjson = ubjson.subspan(1); + return ret; +} + +static int8_t i8_from_ubjson(tcb::span& ubjson) +{ + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + int8_t ret = std::to_integer(ubjson[0]); + ubjson = ubjson.subspan(1); + return ret; +} + +static int16_t i16_from_ubjson(tcb::span& ubjson) +{ + uint8_t b[2]; + uint16_t native; + int16_t ret; + if (ubjson.size() < 2) throw JsonParseError("insufficient data"); + b[0] = std::to_integer(ubjson[0]); + b[1] = std::to_integer(ubjson[1]); + native = b[0] << 8 | b[1]; + std::memcpy(&ret, &native, 2); + ubjson = ubjson.subspan(2); + return ret; +} + +static int32_t i32_from_ubjson(tcb::span& ubjson) +{ + uint8_t b[4]; + uint32_t native; + int32_t ret; + if (ubjson.size() < 4) throw JsonParseError("insufficient data"); + b[0] = std::to_integer(ubjson[0]); + b[1] = std::to_integer(ubjson[1]); + b[2] = std::to_integer(ubjson[2]); + b[3] = std::to_integer(ubjson[3]); + native = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + std::memcpy(&ret, &native, 4); + ubjson = ubjson.subspan(4); + return ret; +} + +static int64_t i64_from_ubjson(tcb::span& ubjson) +{ + uint64_t b[8]; + uint64_t native; + int64_t ret; + if (ubjson.size() < 8) throw JsonParseError("insufficient data"); + b[0] = std::to_integer(ubjson[0]); + b[1] = std::to_integer(ubjson[1]); + b[2] = std::to_integer(ubjson[2]); + b[3] = std::to_integer(ubjson[3]); + b[4] = std::to_integer(ubjson[4]); + b[5] = std::to_integer(ubjson[5]); + b[6] = std::to_integer(ubjson[6]); + b[7] = std::to_integer(ubjson[7]); + native = b[0] << 56 | b[1] << 48 | b[2] << 40 | b[3] << 32 | b[4] << 24 | b[5] << 16 | b[6] << 8 | b[7]; + std::memcpy(&ret, &native, 8); + ubjson = ubjson.subspan(8); + return ret; +} + +static float f32_from_ubjson(tcb::span& ubjson) +{ + uint8_t b[8]; + uint32_t native; + float ret; + if (ubjson.size() < 4) throw JsonParseError("insufficient data"); + b[0] = std::to_integer(ubjson[0]); + b[1] = std::to_integer(ubjson[1]); + b[2] = std::to_integer(ubjson[2]); + b[3] = std::to_integer(ubjson[3]); + native = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + std::memcpy(&ret, &native, 4); + ubjson = ubjson.subspan(4); + return ret; +} + +static double f64_from_ubjson(tcb::span& ubjson) +{ + uint64_t b[8]; + uint64_t native; + double ret; + if (ubjson.size() < 8) throw JsonParseError("insufficient data"); + b[0] = std::to_integer(ubjson[0]); + b[1] = std::to_integer(ubjson[1]); + b[2] = std::to_integer(ubjson[2]); + b[3] = std::to_integer(ubjson[3]); + b[4] = std::to_integer(ubjson[4]); + b[5] = std::to_integer(ubjson[5]); + b[6] = std::to_integer(ubjson[6]); + b[7] = std::to_integer(ubjson[7]); + native = b[0] << 56 | b[1] << 48 | b[2] << 40 | b[3] << 32 | b[4] << 24 | b[5] << 16 | b[6] << 8 | b[7]; + std::memcpy(&ret, &native, 8); + ubjson = ubjson.subspan(8); + return ret; +} + +static uint64_t length_from_ubjson(tcb::span& ubjson) +{ + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + uint64_t size = 0; + switch ((char)(ubjson[0])) + { + case 'i': + ubjson = ubjson.subspan(1); + size = i8_from_ubjson(ubjson); + break; + case 'U': + ubjson = ubjson.subspan(1); + size = u8_from_ubjson(ubjson); + break; + case 'I': + ubjson = ubjson.subspan(1); + size = i16_from_ubjson(ubjson); + break; + case 'l': + ubjson = ubjson.subspan(1); + size = i32_from_ubjson(ubjson); + break; + case 'L': + ubjson = ubjson.subspan(1); + size = i64_from_ubjson(ubjson); + break; + default: + throw JsonParseError("illegal data length type"); + } + return size; +} + +static String string_from_ubjson(tcb::span& ubjson) +{ + uint64_t len = length_from_ubjson(ubjson); + if (len > std::numeric_limits().max()) + { + throw JsonParseError("unloadable string length"); + } + String ret; + for (size_t i = 0; i < len; i++) + { + + } + auto strdata = ubjson.subspan(0, (size_t)len); + ubjson = ubjson.subspan((size_t)len); + for (auto itr = strdata.begin(); itr != strdata.end(); itr++) + { + ret.push_back((char)(*itr)); + } + return ret; +} + +template +static void read_ubjson_array_elements(F f, JsonArray& arr, tcb::span& ubjson, uint64_t len) +{ + ubjson = ubjson.subspan(1); + for (uint64_t i = 0; i < len; i++) + { + arr.push_back((f)(ubjson)); + } +} + +template +static void read_ubjson_array_elements(F f, JsonArray& arr, tcb::span& ubjson, uint64_t len, int depth) +{ + ubjson = ubjson.subspan(1); + for (uint64_t i = 0; i < len; i++) + { + arr.push_back((f)(ubjson, depth)); + } +} + +static JsonValue do_from_ubjson(tcb::span& ubjson, int depth); +static JsonObject object_from_ubjson(tcb::span& ubjson, int depth); + +static JsonArray array_from_ubjson(tcb::span& ubjson, int depth) +{ + char typecode = 0; + if ((char)(ubjson[0]) == '$') + { + if (ubjson.size() < 2) throw JsonParseError("insufficient data"); + typecode = (char)(ubjson[1]); + ubjson = ubjson.subspan(2); + } + bool has_len = false; + uint64_t len = 0; + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + if ((char)(ubjson[0]) == '#') + { + ubjson = ubjson.subspan(1); + has_len = true; + len = length_from_ubjson(ubjson); + } + JsonArray ret; + if (has_len) + { + ret.reserve(len); + } + if (typecode != 0) + { + switch (typecode) + { + case 'i': + read_ubjson_array_elements(i8_from_ubjson, ret, ubjson, len); + break; + case 'U': + read_ubjson_array_elements(u8_from_ubjson, ret, ubjson, len); + break; + case 'I': + read_ubjson_array_elements(i16_from_ubjson, ret, ubjson, len); + break; + case 'l': + read_ubjson_array_elements(i32_from_ubjson, ret, ubjson, len); + break; + case 'L': + read_ubjson_array_elements(i64_from_ubjson, ret, ubjson, len); + break; + case 'd': + read_ubjson_array_elements(f32_from_ubjson, ret, ubjson, len); + break; + case 'D': + read_ubjson_array_elements(f64_from_ubjson, ret, ubjson, len); + break; + case 'S': + read_ubjson_array_elements(string_from_ubjson, ret, ubjson, len); + break; + case '[': + read_ubjson_array_elements(array_from_ubjson, ret, ubjson, len, depth + 1); + break; + case '{': + read_ubjson_array_elements(object_from_ubjson, ret, ubjson, len, depth + 1); + break; + default: + throw JsonParseError("invalid typecode for array"); + } + } + else + { + if (has_len) + { + for (uint64_t i = 0; i < len; i++) + { + JsonValue v = do_from_ubjson(ubjson, depth); + ret.push_back(std::move(v)); + } + } + else + { + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + while ((char)(ubjson[0]) != ']') + { + JsonValue v = do_from_ubjson(ubjson, depth); + ret.push_back(std::move(v)); + } + ubjson = ubjson.subspan(1); + } + } + return ret; +} + +template +static void read_ubjson_object_elements(F f, JsonObject& obj, tcb::span& ubjson, uint64_t len) +{ + ubjson = ubjson.subspan(1); + for (uint64_t i = 0; i < len; i++) + { + String key = string_from_ubjson(ubjson); + JsonValue value = (f)(ubjson); + obj[std::move(key)] = std::move(value); + } +} + +template +static void read_ubjson_object_elements(F f, JsonObject& obj, tcb::span& ubjson, uint64_t len, int depth) +{ + ubjson = ubjson.subspan(1); + for (uint64_t i = 0; i < len; i++) + { + String key = string_from_ubjson(ubjson); + JsonValue value = (f)(ubjson, depth); + obj[std::move(key)] = std::move(value); + } +} + +static JsonObject object_from_ubjson(tcb::span& ubjson, int depth) +{ + char typecode = 0; + if ((char)(ubjson[0]) == '$') + { + if (ubjson.size() < 2) throw JsonParseError("insufficient data"); + typecode = (char)(ubjson[1]); + ubjson = ubjson.subspan(2); + } + bool has_len = false; + uint64_t len = 0; + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + if ((char)(ubjson[0]) == '#') + { + ubjson = ubjson.subspan(1); + has_len = true; + len = length_from_ubjson(ubjson); + } + JsonObject ret; + if (has_len) + { + ret.rehash(len); + } + if (typecode != 0) + { + switch (typecode) + { + case 'i': + read_ubjson_object_elements(i8_from_ubjson, ret, ubjson, len); + break; + case 'U': + read_ubjson_object_elements(u8_from_ubjson, ret, ubjson, len); + break; + case 'I': + read_ubjson_object_elements(i16_from_ubjson, ret, ubjson, len); + break; + case 'l': + read_ubjson_object_elements(i32_from_ubjson, ret, ubjson, len); + break; + case 'L': + read_ubjson_object_elements(i64_from_ubjson, ret, ubjson, len); + break; + case 'd': + read_ubjson_object_elements(f32_from_ubjson, ret, ubjson, len); + break; + case 'D': + read_ubjson_object_elements(f64_from_ubjson, ret, ubjson, len); + break; + case 'S': + read_ubjson_object_elements(string_from_ubjson, ret, ubjson, len); + break; + case '[': + read_ubjson_object_elements(array_from_ubjson, ret, ubjson, len, depth + 1); + break; + case '{': + read_ubjson_object_elements(object_from_ubjson, ret, ubjson, len, depth + 1); + break; + default: + throw JsonParseError("invalid typecode for array"); + } + } + else + { + if (has_len) + { + for (uint64_t i = 0; i < len; i++) + { + String key = string_from_ubjson(ubjson); + JsonValue value = do_from_ubjson(ubjson, depth); + ret[std::move(key)] = std::move(value); + } + } + else + { + if (ubjson.size() < 1) throw JsonParseError("insufficient data"); + while ((char)(ubjson[0]) != '}') + { + String key = string_from_ubjson(ubjson); + JsonValue value = do_from_ubjson(ubjson, depth); + ret[std::move(key)] = std::move(value); + } + ubjson = ubjson.subspan(1); + } + } + return ret; +} + +static JsonValue do_from_ubjson(tcb::span& ubjson, int depth) +{ + if (depth > 1000) + { + throw JsonParseError("ubjson depth limit exceeded"); + } + + if (ubjson.empty()) + { + throw JsonParseError("empty ubjson payload"); + } + + char typecode = (char)(ubjson[0]); + switch (typecode) + { + case 'Z': + ubjson = ubjson.subspan(1); + return JsonValue(); + case 'F': + ubjson = ubjson.subspan(1); + return JsonValue(false); + case 'T': + ubjson = ubjson.subspan(1); + return JsonValue(true); + case 'i': + ubjson = ubjson.subspan(1); + return i8_from_ubjson(ubjson); + case 'U': + ubjson = ubjson.subspan(1); + return u8_from_ubjson(ubjson); + case 'I': + ubjson = ubjson.subspan(1); + return i16_from_ubjson(ubjson); + case 'l': + ubjson = ubjson.subspan(1); + return i32_from_ubjson(ubjson); + case 'L': + ubjson = ubjson.subspan(1); + return i64_from_ubjson(ubjson); + case 'd': + ubjson = ubjson.subspan(1); + return f32_from_ubjson(ubjson); + case 'D': + ubjson = ubjson.subspan(1); + return f64_from_ubjson(ubjson); + case 'S': + ubjson = ubjson.subspan(1); + return string_from_ubjson(ubjson); + case '[': + ubjson = ubjson.subspan(1); + return array_from_ubjson(ubjson, depth); + case '{': + ubjson = ubjson.subspan(1); + return object_from_ubjson(ubjson, depth); + default: + throw JsonParseError(fmt::format("unrecognized ubjson typecode 0x{:02x}", typecode)); + } +} + +JsonValue JsonValue::from_ubjson(tcb::span ubjson) +{ + return do_from_ubjson(ubjson, 0); +} + +namespace +{ +struct Token +{ + enum class Type + { + kEof, + kOpenCurly, + kCloseCurly, + kColon, + kOpenSquare, + kCloseSquare, + kComma, + kBoolean, + kString, + kNumber, + kNull + }; + Type type = Type::kEof; + std::string_view slice; +}; + +class Tokenizer +{ + std::string_view in_; + + void consume(size_t len); +public: + Tokenizer(const std::string_view& in) : in_(in) {} + Token peek(); + Token next(); +}; + +Token Tokenizer::peek() +{ + Token ret; + while (!in_.empty() && ret.type == Token::Type::kEof) + { + unsigned char next = in_[0]; + switch (next) + { + case ' ': + case '\n': + case '\r': + case '\t': + in_.remove_prefix(1); + break; + case 'n': + if (in_.size() < 4) throw JsonParseError("reached end of buffer parsing null"); + ret = Token { Token::Type::kNull, in_.substr(0, 4) }; + if (ret.slice != "null") throw JsonParseError("invalid null token"); + break; + case 'f': + if (in_.size() < 5) throw JsonParseError("reached end of buffer parsing false"); + ret = Token { Token::Type::kBoolean, in_.substr(0, 5) }; + if (ret.slice != "false") throw JsonParseError("invalid boolean token"); + break; + case 't': + if (in_.size() < 4) throw JsonParseError("reached end of buffer parsing true"); + ret = Token { Token::Type::kBoolean, in_.substr(0, 4) }; + if (ret.slice != "true") throw JsonParseError("invalid boolean token"); + break; + case '{': + ret = Token { Token::Type::kOpenCurly, in_.substr(0, 1) }; + break; + case '}': + ret = Token { Token::Type::kCloseCurly, in_.substr(0, 1) }; + break; + case '[': + ret = Token { Token::Type::kOpenSquare, in_.substr(0, 1) }; + break; + case ']': + ret = Token { Token::Type::kCloseSquare, in_.substr(0, 1) }; + break; + case ':': + ret = Token { Token::Type::kColon, in_.substr(0, 1) }; + break; + case ',': + ret = Token { Token::Type::kComma, in_.substr(0, 1) }; + break; + case '"': + { + bool skipnextquote = false; + size_t len; + for (len = 1; len < in_.size(); len++) + { + char c = in_[len]; + bool shouldbreak = false; + switch (c) + { + case '\r': + throw JsonParseError("illegal carriage return in string literal"); + case '\n': + throw JsonParseError("illegal line feed in string literal"); + case '\\': + skipnextquote = true; + break; + case '"': + if (skipnextquote) skipnextquote = false; + else shouldbreak = true; + break; + default: + if (skipnextquote) skipnextquote = false; + } + if (shouldbreak) break; + } + if (in_[len] != '"') throw JsonParseError("found unterminated string"); + ret = Token { Token::Type::kString, in_.substr(0, len + 1) }; + break; + } + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + size_t len; + bool firstiszero = next == '0'; + bool zeroseen = next == '0'; + bool integerseen = next >= '0' && next <= '9'; + bool periodseen = false; + bool periodlast = false; + bool exponentseen = false; + for (len = 1; len < in_.size(); len++) + { + char c = in_[len]; + bool shouldbreak = false; + switch (c) + { + default: + shouldbreak = true; + break; + case '.': + if (periodseen || exponentseen || (!periodseen && !zeroseen && !integerseen)) + throw JsonParseError("unexpected period in number token"); + periodseen = true; + periodlast = true; + break; + case '0': + if (firstiszero && len == 1) throw JsonParseError("more than 1 preceding 0"); + if ((next == '-' || next == '+') && len == 1) firstiszero = true; + zeroseen = true; + integerseen = true; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (firstiszero && !periodseen && !exponentseen) throw JsonParseError("nonzero integral part of number preceded by 0"); + periodlast = false; + integerseen = true; + break; + case 'e': + case 'E': + if (exponentseen) throw JsonParseError("multiple exponent"); + exponentseen = true; + break; + case '+': + case '-': + if (!exponentseen && len != 0) throw JsonParseError("sign after number started"); + break; + } + if (shouldbreak) break; + } + if (periodlast) throw JsonParseError("real number without fractional part"); + ret = Token { Token::Type::kNumber, in_.substr(0, len) }; + break; + } + default: + throw JsonParseError(fmt::format("unexpected character {:c}", next)); + } + } + std::string copy { ret.slice }; + return ret; +} + +void Tokenizer::consume(size_t len) +{ + in_.remove_prefix(len); +} + +Token Tokenizer::next() +{ + Token peeked = peek(); + consume(peeked.slice.size()); + std::string p { peeked.slice }; + return peeked; +} + +} // namespace + +static JsonValue parse_value(Tokenizer& tokenizer, int depth); + +static JsonValue parse_number(const Token& token) +{ + std::string_view s { token.slice }; + if (s.empty()) throw JsonParseError("empty number token"); + while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n' || s[0] == '\r') + { + s.remove_prefix(1); + } + + bool negative = false; + if (s[0] == '-') + { + negative = true; + s.remove_prefix(1); + } + else if (s[0] == '+') + { + negative = false; + s.remove_prefix(1); + } + + if (s.empty()) + { + throw JsonParseError("only sign present on number"); + } + + const char* integral_start = s.begin(); + const char* integral_end; + const char* decimal = nullptr; + while (!s.empty()) + { + if (s[0] == '.') + { + decimal = s.begin(); + integral_end = s.begin(); + s.remove_prefix(1); + break; + } + else if (s[0] < '0' || s[0] > '9') + { + integral_end = s.begin() - 1; + break; + } + integral_end = s.begin() + 1; + s.remove_prefix(1); + } + + const char* decimal_start = s.end(); + const char* decimal_end = s.end(); + const char* exponent_start = s.end(); + const char* exponent_end = s.end(); + bool should_have_exponent = false; + if (decimal != nullptr && (decimal + 1) < s.end()) + { + decimal_start = decimal + 1; + } + while (!s.empty()) + { + // ingest decimal + if (s[0] == 'E' || s[0] == 'e') + { + if (decimal_start != s.end()) decimal_end = s.begin(); + exponent_start = s.begin() + 1; + should_have_exponent = true; + s.remove_prefix(1); + break; + } + else if ((s[0] < '0' || s[0] > '9') && s[0] != '+' && s[0] != '-') + { + throw JsonParseError("invalid character after decimal"); + } + decimal_end = s.begin() + 1; + s.remove_prefix(1); + } + + bool exponent_negative = false; + + if (should_have_exponent) + { + if (s.empty()) + { + throw JsonParseError("exponent started but not specified"); + } + bool exponent_was_signed = false; + while (!s.empty()) + { + if (s[0] == '-') + { + if (exponent_was_signed) throw JsonParseError("multiple signs on exponent"); + exponent_negative = true; + exponent_start++; + exponent_was_signed = true; + s.remove_prefix(1); + continue; + } + else if (s[0] == '+') + { + if (exponent_was_signed) throw JsonParseError("multiple signs on exponent"); + exponent_start++; + exponent_was_signed = true; + s.remove_prefix(1); + continue; + } + + if (s[0] < '0' || s[0] > '9') + { + throw JsonParseError("invalid character after exponent"); + } + exponent_end = s.begin() + 1; + s.remove_prefix(1); + } + if ((exponent_end - exponent_start) == 0) + { + throw JsonParseError("exponent started but not specified"); + } + } + + std::string_view integral_view { integral_start, (size_t)(integral_end - integral_start) }; + std::string_view decimal_view { decimal_start, (size_t)(decimal_end - decimal_start) }; + std::string_view exponent_view { exponent_start, (size_t)(exponent_end - exponent_start) }; + + if (should_have_exponent && decimal_start != s.end() && decimal_view.empty()) + { + throw JsonParseError("exponent after decimal but no decimal value"); + } + if (should_have_exponent && exponent_view.empty()) + { + throw JsonParseError("no exponent despite e/E +/-"); + } + // if (!exponent_negative && exponent_view == "1") + // { + // throw JsonParseError("exponent of 1 not allowed"); + // } + + double number = 0.0; + uint64_t integral_int = 0; + for (auto i : integral_view) + { + integral_int = 10 * integral_int + (i - '0'); + } + double decimal_value = 0.0; + for (auto i : decimal_view) + { + decimal_value = (decimal_value / 10) + ((double)(i - '0') / 10); + } + uint64_t exponent_int = 0; + for (auto i : exponent_view) + { + exponent_int = 10 * exponent_int + (i - '0'); + } + if (negative) + { + integral_int *= -1; + } + if (exponent_negative) + { + exponent_int *= -1; + } + number = std::pow(10, exponent_int) * (double)integral_int + decimal_value; + + return JsonValue(number); +} + +static char hexconv(char c) +{ + switch (c) + { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + c += 32; + break; + default: + break; + } + switch (c) + { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + case 'a': return 10; + case 'b': return 11; + case 'c': return 12; + case 'd': return 13; + case 'e': return 14; + case 'f': return 15; + default: throw JsonParseError("illegal unicode escape sequence character"); + } +} + +static constexpr bool is_surrogate(uint16_t code) +{ + return (0xf800 & code) == 0xd800; +} + +static constexpr bool is_high_surrogate(uint16_t code) +{ + return (code & 0xfc00) == 0xd800; +} + +static constexpr bool is_low_surrogate(uint16_t code) +{ + return (code & 0xfc00) == 0xdc00; +} + +static constexpr uint32_t merge_surrogate_pair(uint16_t low, uint16_t high) +{ + return ((high - 0xd800) << 10) + ((low - 0xdc00) + 0x10000); +} + +static JsonValue parse_string(const Token& token) +{ + std::string_view read { token.slice.substr(1, token.slice.size() - 2) }; + String conv; + for (auto itr = read.begin(); itr != read.end();) + { + int c = *itr & 0xFF; + switch (c) + { + case '\\': + { + // Reverse solidus indicates escape sequence + if (std::next(itr) == read.end()) throw JsonParseError("unterminated string escape sequence"); + char control = *++itr; + switch (control) + { + case '"': + case '\\': + case '/': + conv.push_back(control); + ++itr; + break; + case 'b': + conv.push_back('\b'); + ++itr; + break; + case 'f': + conv.push_back('\f'); + ++itr; + break; + case 'n': + conv.push_back('\n'); + ++itr; + break; + case 'r': + conv.push_back('\r'); + ++itr; + break; + case 't': + conv.push_back('\t'); + ++itr; + break; + case 'x': + { + if (std::distance(itr, read.end()) < 3) throw JsonParseError("unterminated hex char sequence"); + char hex[2]; + hex[0] = *++itr; + hex[1] = *++itr; + char byte = (hexconv(hex[0]) << 4) | hexconv(hex[1]); + if (byte < 0x20) throw JsonParseError("bad escaped control code"); + conv.push_back(byte); + ++itr; + break; + } + case 'u': + { + if (std::distance(itr, read.end()) < 5) throw JsonParseError("unterminated utf16 code sequence"); + // Next 4 characters are a hex sequence representing a UTF-16 surrogate + // We have to do silly things to make this work correctly + char hex[4]; + hex[0] = *++itr; + hex[1] = *++itr; + hex[2] = *++itr; + hex[3] = *++itr; + if (hex[0] == -1 || hex[1] == -1 || hex[2] == -1 || hex[3] == -1) + throw JsonParseError("invalid unicode escape"); + char byte[2]; + byte[0] = (hexconv(hex[0]) << 4) | hexconv(hex[1]); + byte[1] = (hexconv(hex[2]) << 4) | hexconv(hex[3]); + uint16_t utf16 = hexconv(hex[0]) << 12 | hexconv(hex[1]) << 8 | hexconv(hex[2]) << 4 | hexconv(hex[3]); + bool valid_codepoint = false; + uint32_t codepoint = 0; + if (is_low_surrogate(utf16)) + { + // invalid surrogate pair -- high must precede low + conv.push_back('\\'); + conv.push_back('u'); + conv.push_back(hex[0]); + conv.push_back(hex[1]); + conv.push_back(hex[2]); + conv.push_back(hex[3]); + } + else if (is_high_surrogate(utf16)) + { + if (std::distance(itr, read.end()) < 6) + { + // invalid surrogate pair -- high must precede low + conv += "\\u"; + } + else + { + // potentially valid... + if ( + *itr == '\\' && + *(itr + 1) == 'u' && + (hex[0] = *(itr + 2)) != -1 && + (hex[1] = *(itr + 3)) != -1 && + (hex[2] = *(itr + 4)) != -1 && + (hex[3] = *(itr + 5)) != -1 + ) + { + uint16_t utf16_2 = hexconv(hex[0]) << 12 | hexconv(hex[1]) << 8 | hexconv(hex[2]) << 4 | hexconv(hex[3]); + if (is_low_surrogate(utf16_2)) + { + itr += 6; + codepoint = merge_surrogate_pair(utf16_2, utf16); + valid_codepoint = true; + } + else + { + itr += 2; + conv += "\\u"; + } + } + else + { + conv += "\\u"; + } + } + } + else + { + // non-surrogate represents unicode codepoint + codepoint = utf16; + valid_codepoint = true; + } + + if (valid_codepoint) + { + char encoded[4]; + int len; + // encode codepoint as UTF-8 + if (codepoint <= 0x7f) + { + encoded[0] = codepoint; + len = 1; + } + else if (codepoint <= 0x7ff) + { + encoded[0] = 0300 | (codepoint >> 6); + encoded[1] = 0200 | (codepoint & 077); + len = 2; + } + else if (codepoint <= 0xffff) + { + if (is_surrogate(codepoint)) + { + codepoint = 0xfffd; + } + encoded[0] = 0340 | (codepoint >> 2); + encoded[1] = 0200 | ((codepoint >> 6) & 077); + encoded[2] = 0200 | (codepoint & 077); + len = 3; + } + else if (~(codepoint >> 18) & 007) + { + encoded[0] = 0360 | (codepoint >> 18); + encoded[1] = 0200 | ((codepoint >> 12) & 077); + encoded[2] = 0200 | ((c >> 6) & 077); + encoded[3] = 0200 | (c & 077); + len = 4; + } + else + { + encoded[0] = 0xef; + encoded[1] = 0xbf; + encoded[2] = 0xbd; + len = 3; + } + conv.append(encoded, len); + } + + ++itr; + break; + } + default: + throw JsonParseError("invalid string escape control code"); + } + break; + } + default: + if (c < 0x20) throw JsonParseError("unescaped control code"); + conv.push_back(c); + ++itr; + break; + } + } + return JsonValue(conv); +} + +static JsonValue parse_object(Tokenizer& tokenizer, int depth) +{ + JsonObject obj; + bool done = false; + if (tokenizer.peek().type == Token::Type::kCloseCurly) + { + tokenizer.next(); + return obj; + } + while (!done) + { + Token key_token = tokenizer.next(); + if (key_token.type != Token::Type::kString) throw JsonParseError("unexpected token; expected string (for key)"); + String key_string = parse_string(key_token).get(); + Token colon = tokenizer.next(); + if (colon.type != Token::Type::kColon) throw JsonParseError("unexpected token; expected colon (after key)"); + JsonValue value = parse_value(tokenizer, depth + 1); + Token last = tokenizer.next(); + if (last.type == Token::Type::kCloseCurly) done = true; + else if (last.type != Token::Type::kComma) throw JsonParseError("unexpected token; expected comma (after value)"); + + obj.insert_or_assign(std::move(key_string), std::move(value)); + } + return obj; +} + +static JsonArray parse_array(Tokenizer& tokenizer, int depth) +{ + JsonArray arr; + bool done = false; + if (tokenizer.peek().type == Token::Type::kCloseSquare) + { + tokenizer.next(); + return arr; + } + while (!done) + { + JsonValue value = parse_value(tokenizer, depth + 1); + Token last = tokenizer.next(); + if (last.type == Token::Type::kCloseSquare) done = true; + else if (last.type != Token::Type::kComma) throw JsonParseError("unexpected token; expected comma (after value)"); + arr.push_back(value); + } + return arr; +} + +constexpr const int kMaxDepth = 1000; + +static JsonValue parse_value(Tokenizer& tokenizer, int depth) +{ + using Type = Token::Type; + JsonValue ret; + Token token = tokenizer.next(); + + if (depth >= kMaxDepth) + { + throw JsonParseError("parse depth limit exceeded"); + } + + switch (token.type) + { + case Type::kNull: + ret = JsonValue(); + break; + case Type::kBoolean: + if (token.slice == "true") ret = JsonValue(true); + else if (token.slice == "false") ret = JsonValue(false); + else throw JsonParseError("illegal boolean token"); + break; + case Type::kNumber: + ret = parse_number(token); + break; + case Type::kString: + ret = parse_string(token); + break; + case Type::kOpenCurly: + ret = parse_object(tokenizer, depth); + break; + case Type::kOpenSquare: + ret = parse_array(tokenizer, depth); + break; + case Type::kEof: + throw JsonParseError("reached EOF before parsing value"); + default: + throw JsonParseError("unexpected token"); + } + + return ret; +} + +JsonValue JsonValue::from_json_string(const String& str) +{ + JsonValue ret; + Tokenizer tokenizer { str }; + ret = parse_value(tokenizer, 0); + + Token peek = tokenizer.peek(); + if (peek.type != Token::Type::kEof) throw JsonParseError("unexpected token after expression"); + return ret; +} + +JsonValue::JsonValue(bool value) : type_(Type::kBoolean), boolean_(value) {} +JsonValue::JsonValue(float value) : type_(Type::kNumber), number_((float)value) {} +JsonValue::JsonValue(double value) : type_(Type::kNumber), number_((double)value) {} +JsonValue::JsonValue(int8_t value) : type_(Type::kNumber), number_((int8_t)value) {} +JsonValue::JsonValue(int16_t value) : type_(Type::kNumber), number_((int16_t)value) {} +JsonValue::JsonValue(int32_t value) : type_(Type::kNumber), number_((int32_t)value) {} +JsonValue::JsonValue(int64_t value) : type_(Type::kNumber), number_((int64_t)value) {} +JsonValue::JsonValue(uint8_t value) : type_(Type::kNumber), number_((uint8_t)value) {} +JsonValue::JsonValue(uint16_t value) : type_(Type::kNumber), number_((double)value) {} +JsonValue::JsonValue(uint32_t value) : type_(Type::kNumber), number_((double)value) {} +JsonValue::JsonValue(uint64_t value) : type_(Type::kNumber), number_((double)value) {} +JsonValue::JsonValue(const String& value) : type_(Type::kString), string_(value) {} +JsonValue::JsonValue(String&& value) : type_(Type::kString), string_(std::move(value)) {} +JsonValue::JsonValue(const JsonArray& value) : type_(Type::kArray), array_(value) {} +JsonValue::JsonValue(JsonArray&& value) : type_(Type::kArray), array_(std::move(value)) {} +JsonValue::JsonValue(const JsonObject& value) : type_(Type::kObject), object_(value) {} +JsonValue::JsonValue(JsonObject&& value) : type_(Type::kObject), object_(std::move(value)) {} + +bool JsonValue::operator==(const JsonValue& rhs) const +{ + if (type_ != rhs.type_) + { + return false; + } + switch (type_) + { + case Type::kNull: + return true; + case Type::kBoolean: + return boolean_ == rhs.boolean_; + case Type::kNumber: + return number_ == rhs.number_; + case Type::kString: + return string_ == rhs.string_; + case Type::kArray: + return array_ == rhs.array_; + case Type::kObject: + return object_ == rhs.object_; + } + return false; +} + +void srb2::to_json(JsonValue& to, bool value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, int8_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, int16_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, int32_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, int64_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, uint8_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, uint16_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, uint32_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, uint64_t value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, float value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, double value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, const String& value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, const JsonArray& value) { to = JsonValue(value); } +void srb2::to_json(JsonValue& to, const JsonObject& value) { to = JsonValue(value); } + +void srb2::to_json(JsonValue& to, const std::string& v) +{ + to = JsonValue(static_cast(v)); +} + +void srb2::from_json(const JsonValue& from, bool& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, int8_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, int16_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, int32_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, int64_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, uint8_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, uint16_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, uint32_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, uint64_t& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, float& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, double& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, String& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, JsonArray& value) { value = from.get(); } +void srb2::from_json(const JsonValue& from, JsonObject& value) { value = from.get(); } + +void srb2::from_json(const JsonValue& from, std::string& to) +{ + to = static_cast(from.get()); +} + +template class srb2::Vector; +template class srb2::HashMap; diff --git a/src/core/json.hpp b/src/core/json.hpp new file mode 100644 index 000000000..6492c50bb --- /dev/null +++ b/src/core/json.hpp @@ -0,0 +1,533 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Ronald "Eidolon" Kinard +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef SRB2_CORE_JSON_HPP +#define SRB2_CORE_JSON_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hash_map.hpp" +#include "string.h" +#include "vector.hpp" + +#include "json_macro.inl" + +namespace srb2 +{ + +class JsonValue; + +using JsonArray = Vector; +using JsonObject = HashMap; + +class JsonError : public std::runtime_error +{ +public: + JsonError(const std::string& what); + JsonError(const char* what); + JsonError(const JsonError&) noexcept; +}; + +class JsonParseError : public JsonError +{ +public: + JsonParseError(const std::string& what); + JsonParseError(const char* what); + JsonParseError(const JsonParseError&) noexcept; +}; + +class JsonValue +{ +public: + enum class Type : int + { + kNull, + kBoolean, + kNumber, + kString, + kArray, + kObject + }; + +private: + Type type_; + union { + struct {} dummy; + bool boolean_; + double number_; + String string_; + JsonArray array_; + JsonObject object_; + }; + + static void value_to_ubjson(srb2::Vector& out); + static void value_to_ubjson(srb2::Vector& out, bool value); + static void value_to_ubjson(srb2::Vector& out, double value); + static void value_to_ubjson(srb2::Vector& out, uint64_t value); + static void value_to_ubjson(srb2::Vector& out, const String& value, bool include_type); + static void value_to_ubjson(srb2::Vector& out, const JsonArray& value); + static void value_to_ubjson(srb2::Vector& out, const JsonObject& value); + + void do_to_ubjson(srb2::Vector& out) const; + +public: + JsonValue(); + JsonValue(const JsonValue&); + JsonValue(JsonValue&&) noexcept; + ~JsonValue(); + JsonValue& operator=(const JsonValue&); + JsonValue& operator=(JsonValue&&) noexcept; + + JsonValue(const String& value); + JsonValue(String&& value); + JsonValue(const JsonArray& value); + JsonValue(JsonArray&& value); + JsonValue(const JsonObject& value); + JsonValue(JsonObject&& value); + JsonValue(float value); + JsonValue(double value); + JsonValue(int8_t value); + JsonValue(int16_t value); + JsonValue(int32_t value); + JsonValue(int64_t value); + JsonValue(uint8_t value); + JsonValue(uint16_t value); + JsonValue(uint32_t value); + JsonValue(uint64_t value); + JsonValue(bool value); + + static JsonValue array() { return JsonValue(JsonArray()); } + static JsonValue object() { return JsonValue(JsonObject()); } + + bool operator==(const JsonValue& rhs) const; + bool operator!=(const JsonValue& rhs) const { return !(*this == rhs); } + + void to_json(JsonValue& to) const; + void from_json(const JsonValue& from); + String to_json_string() const; + static JsonValue from_json_string(const String& str); + srb2::Vector to_ubjson() const; + static JsonValue from_ubjson(tcb::span ubjson); + constexpr Type type() const noexcept { return type_; } + + template V get() const; + + constexpr bool is_null() const noexcept { return type_ == Type::kNull; } + constexpr bool is_boolean() const noexcept { return type_ == Type::kBoolean; } + constexpr bool is_number() const noexcept { return type_ == Type::kNumber; } + constexpr bool is_string() const noexcept { return type_ == Type::kString; } + constexpr bool is_array() const noexcept { return type_ == Type::kArray; } + constexpr bool is_object() const noexcept { return type_ == Type::kObject; } + JsonArray& as_array(); + JsonObject& as_object(); + const JsonArray& as_array() const; + const JsonObject& as_object() const; + + JsonValue& at(uint32_t i); + const JsonValue& at(uint32_t i) const; + JsonValue& at(const char* key); + JsonValue& at(const String& key); + const JsonValue& at(const char* key) const; + const JsonValue& at(const String& key) const; + + template + V value(const char* key, const V& def) const; + template + V value(const String& key, const V& def) const; + template + V value(const char* key, V&& def) const; + template + V value(const String& key, V&& def) const; + + bool contains(const char* key) const; + bool contains(const String& key) const; + + JsonValue& operator[](uint32_t i); + JsonValue& operator[](const char* key); + JsonValue& operator[](const String& key); +}; + +void to_json(JsonValue& to, bool value); +void to_json(JsonValue& to, int8_t value); +void to_json(JsonValue& to, int16_t value); +void to_json(JsonValue& to, int32_t value); +void to_json(JsonValue& to, int64_t value); +void to_json(JsonValue& to, uint8_t value); +void to_json(JsonValue& to, uint16_t value); +void to_json(JsonValue& to, uint32_t value); +void to_json(JsonValue& to, uint64_t value); +void to_json(JsonValue& to, float value); +void to_json(JsonValue& to, double value); +void to_json(JsonValue& to, const String& value); +void to_json(JsonValue& to, const JsonArray& value); +void to_json(JsonValue& to, const JsonObject& value); + +void from_json(const JsonValue& to, bool& value); +void from_json(const JsonValue& to, int8_t& value); +void from_json(const JsonValue& to, int16_t& value); +void from_json(const JsonValue& to, int32_t& value); +void from_json(const JsonValue& to, int64_t& value); +void from_json(const JsonValue& to, uint8_t& value); +void from_json(const JsonValue& to, uint16_t& value); +void from_json(const JsonValue& to, uint32_t& value); +void from_json(const JsonValue& to, uint64_t& value); +void from_json(const JsonValue& to, float& value); +void from_json(const JsonValue& to, double& value); +void from_json(const JsonValue& to, String& value); +void from_json(const JsonValue& to, JsonArray& value); +void from_json(const JsonValue& to, JsonObject& value); + +template +inline void to_json(JsonValue& to, const std::array& v) +{ + JsonArray arr; + for (auto itr = v.begin(); itr != v.end(); itr++) + { + JsonValue conv; + to_json(conv, *itr); + arr.push_back(conv); + } + to = JsonValue(std::move(arr)); +} + +template +inline void from_json(const JsonValue& to, std::array& v) +{ + if (!to.is_array()) + { + throw JsonError("json value must be an array"); + } + const JsonArray& arr = to.as_array(); + size_t si = 0; + for (auto& i : arr) + { + if (si >= v.size()) + { + break; + } + + T conv; + from_json(i, conv); + v[si] = std::move(conv); + si++; + } +} + +template +inline void to_json(JsonValue& to, const std::vector& v) +{ + JsonArray arr; + for (auto itr = v.begin(); itr != v.end(); itr++) + { + JsonValue conv; + to_json(conv, *itr); + arr.push_back(conv); + } + to = JsonValue(std::move(arr)); +} + +template +inline void from_json(const JsonValue& to, std::vector& v) +{ + if (!to.is_array()) + { + throw JsonError("json value must be an array"); + } + v.clear(); + const JsonArray& arr = to.as_array(); + for (auto& i : arr) + { + T conv; + from_json(i, conv); + v.push_back(std::move(conv)); + } +} + +template +inline void to_json(JsonValue& to, const srb2::Vector& v) +{ + JsonArray arr; + for (auto itr = v.begin(); itr != v.end(); itr++) + { + JsonValue conv; + to_json(conv, *itr); + arr.push_back(conv); + } + to = JsonValue(std::move(arr)); +} + +template +inline void from_json(const JsonValue& to, srb2::Vector& v) +{ + if (!to.is_array()) + { + throw JsonError("json value must be an array"); + } + v.clear(); + const JsonArray& arr = to.as_array(); + for (auto& i : arr) + { + T conv; + from_json(i, conv); + v.push_back(std::move(conv)); + } +} + +template +inline void to_json(JsonValue& to, const std::unordered_map& v) +{ + JsonObject obj; + for (auto itr = v.begin(); itr != v.end(); itr++) + { + to_json(obj[itr->first], itr->second); + } + to = JsonValue(std::move(obj)); +} + +template +inline void from_json(const JsonValue& to, std::unordered_map& v) +{ + if (!to.is_object()) + { + throw JsonError("json value must be an object"); + } + v.clear(); + const JsonObject& obj = to.as_object(); + for (auto itr = obj.begin(); itr != obj.end(); itr++) + { + V conv; + from_json(itr->second, conv); + v[itr->first] = std::move(conv); + } +} + +template +inline void to_json(JsonValue& to, const srb2::HashMap& v) +{ + JsonObject obj; + for (auto itr = v.begin(); itr != v.end(); itr++) + { + to_json(obj[itr->first], itr->second); + } + to = JsonValue(std::move(obj)); +} + +template +inline void from_json(const JsonValue& to, srb2::HashMap& v) +{ + if (!to.is_object()) + { + throw JsonError("json value must be an object"); + } + v.clear(); + const JsonObject& obj = to.as_object(); + for (auto itr = obj.begin(); itr != obj.end(); itr++) + { + V conv; + from_json(itr->second, conv); + v[itr->first] = std::move(conv); + } +} + +void from_json(const JsonValue& to, std::string& v); +void to_json(JsonValue& to, const std::string& v); + +// template +// inline void to_json(JsonValue& to, const srb2::OAHashMap& v) +// { +// JsonObject obj; +// for (auto itr = v.begin(); itr != v.end(); itr++) +// { +// to_json(obj[itr->first], itr->second); +// } +// to = JsonValue(std::move(obj)); +// } + +// template +// inline void from_json(const JsonValue& to, srb2::OAHashMap& v) +// { +// if (!to.is_object()) +// { +// throw JsonError("json value must be an object"); +// } +// v.clear(); +// const JsonObject& obj = to.as_object(); +// for (auto itr = obj.begin(); itr != obj.end(); itr++) +// { +// V conv; +// from_json(itr->second, conv); +// v[itr->first] = std::move(conv); +// } +// } +// + +template +inline V JsonValue::get() const +{ + V v; + from_json(*this, v); + return v; +} + +template +inline V JsonValue::value(const char* key, const V& def) const +{ + V copy { def }; + return value(key, std::move(copy)); +} + +template +inline V JsonValue::value(const String& key, const V& def) const +{ + return value(key.c_str(), def); +} + +template +inline V JsonValue::value(const char* key, V&& def) const +{ + const JsonObject& obj = as_object(); + auto itr = obj.find(key); + if (itr != obj.end()) + { + return itr->second.get(); + } + return def; +} + +template <> bool JsonValue::get() const; +template <> int8_t JsonValue::get() const; +template <> int16_t JsonValue::get() const; +template <> int32_t JsonValue::get() const; +template <> int64_t JsonValue::get() const; +template <> uint8_t JsonValue::get() const; +template <> uint16_t JsonValue::get() const; +template <> uint32_t JsonValue::get() const; +template <> uint64_t JsonValue::get() const; +template <> float JsonValue::get() const; +template <> double JsonValue::get() const; +template <> String JsonValue::get() const; +template <> std::string JsonValue::get() const; +template <> std::string_view JsonValue::get() const; +template <> JsonArray JsonValue::get() const; +template <> JsonObject JsonValue::get() const; +template <> JsonValue JsonValue::get() const; + +inline bool operator==(const JsonValue& lhs, bool rhs) +{ + if (!lhs.is_boolean()) + { + return false; + } + return lhs.get() == rhs; +} + +inline bool operator==(const JsonValue& lhs, int64_t rhs) +{ + if (!lhs.is_number()) + { + return false; + } + return lhs.get() == rhs; +} + +inline bool operator==(const JsonValue& lhs, uint64_t rhs) +{ + if (!lhs.is_number()) + { + return false; + } + return lhs.get() == rhs; +} + +inline bool operator==(const JsonValue& lhs, double rhs) +{ + if (!lhs.is_number()) + { + return false; + } + return lhs.get() == rhs; +} + +inline bool operator==(const JsonValue& lhs, std::string_view rhs) +{ + if (!lhs.is_string()) + { + return false; + } + return lhs.get() == rhs; +} + +inline bool operator==(const JsonValue& lhs, const JsonArray& rhs) +{ + if (!lhs.is_array()) + { + return false; + } + return lhs.as_array() == rhs; +} + +inline bool operator==(const JsonValue& lhs, const JsonObject& rhs) +{ + if (!lhs.is_object()) + { + return false; + } + return lhs.as_object() == rhs; +} + +inline bool operator!=(const JsonValue& lhs, bool rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, int64_t rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, uint64_t rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, double rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, std::string_view rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, const JsonArray& rhs) +{ + return !(lhs == rhs); +} + +inline bool operator!=(const JsonValue& lhs, const JsonObject& rhs) +{ + return !(lhs == rhs); +} + +extern template class Vector; +extern template class HashMap; + +} // namespace srb2 + +#endif // SRB2_CORE_JSON_HPP diff --git a/src/core/json_macro.inl b/src/core/json_macro.inl new file mode 100644 index 000000000..ce8b01db5 --- /dev/null +++ b/src/core/json_macro.inl @@ -0,0 +1,185 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.11.3 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann +// SPDX-FileCopyrightText: 2016-2021 Evan Nemerson +// SPDX-License-Identifier: MIT + +// file: macro_scope.hpp + +// This file is derived from nlohmman json's codegen macros and thus is provided under its license. + +#define SRB2_JSON_EXPAND( x ) x +#define SRB2_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define SRB2_JSON_PASTE(...) SRB2_JSON_EXPAND(SRB2_JSON_GET_MACRO(__VA_ARGS__, \ + SRB2_JSON_PASTE64, \ + SRB2_JSON_PASTE63, \ + SRB2_JSON_PASTE62, \ + SRB2_JSON_PASTE61, \ + SRB2_JSON_PASTE60, \ + SRB2_JSON_PASTE59, \ + SRB2_JSON_PASTE58, \ + SRB2_JSON_PASTE57, \ + SRB2_JSON_PASTE56, \ + SRB2_JSON_PASTE55, \ + SRB2_JSON_PASTE54, \ + SRB2_JSON_PASTE53, \ + SRB2_JSON_PASTE52, \ + SRB2_JSON_PASTE51, \ + SRB2_JSON_PASTE50, \ + SRB2_JSON_PASTE49, \ + SRB2_JSON_PASTE48, \ + SRB2_JSON_PASTE47, \ + SRB2_JSON_PASTE46, \ + SRB2_JSON_PASTE45, \ + SRB2_JSON_PASTE44, \ + SRB2_JSON_PASTE43, \ + SRB2_JSON_PASTE42, \ + SRB2_JSON_PASTE41, \ + SRB2_JSON_PASTE40, \ + SRB2_JSON_PASTE39, \ + SRB2_JSON_PASTE38, \ + SRB2_JSON_PASTE37, \ + SRB2_JSON_PASTE36, \ + SRB2_JSON_PASTE35, \ + SRB2_JSON_PASTE34, \ + SRB2_JSON_PASTE33, \ + SRB2_JSON_PASTE32, \ + SRB2_JSON_PASTE31, \ + SRB2_JSON_PASTE30, \ + SRB2_JSON_PASTE29, \ + SRB2_JSON_PASTE28, \ + SRB2_JSON_PASTE27, \ + SRB2_JSON_PASTE26, \ + SRB2_JSON_PASTE25, \ + SRB2_JSON_PASTE24, \ + SRB2_JSON_PASTE23, \ + SRB2_JSON_PASTE22, \ + SRB2_JSON_PASTE21, \ + SRB2_JSON_PASTE20, \ + SRB2_JSON_PASTE19, \ + SRB2_JSON_PASTE18, \ + SRB2_JSON_PASTE17, \ + SRB2_JSON_PASTE16, \ + SRB2_JSON_PASTE15, \ + SRB2_JSON_PASTE14, \ + SRB2_JSON_PASTE13, \ + SRB2_JSON_PASTE12, \ + SRB2_JSON_PASTE11, \ + SRB2_JSON_PASTE10, \ + SRB2_JSON_PASTE9, \ + SRB2_JSON_PASTE8, \ + SRB2_JSON_PASTE7, \ + SRB2_JSON_PASTE6, \ + SRB2_JSON_PASTE5, \ + SRB2_JSON_PASTE4, \ + SRB2_JSON_PASTE3, \ + SRB2_JSON_PASTE2, \ + SRB2_JSON_PASTE1)(__VA_ARGS__)) +#define SRB2_JSON_PASTE2(func, v1) func(v1) +#define SRB2_JSON_PASTE3(func, v1, v2) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE2(func, v2) +#define SRB2_JSON_PASTE4(func, v1, v2, v3) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE3(func, v2, v3) +#define SRB2_JSON_PASTE5(func, v1, v2, v3, v4) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE4(func, v2, v3, v4) +#define SRB2_JSON_PASTE6(func, v1, v2, v3, v4, v5) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE5(func, v2, v3, v4, v5) +#define SRB2_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define SRB2_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define SRB2_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define SRB2_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define SRB2_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define SRB2_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define SRB2_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define SRB2_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define SRB2_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define SRB2_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define SRB2_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define SRB2_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define SRB2_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define SRB2_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define SRB2_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define SRB2_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define SRB2_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define SRB2_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define SRB2_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define SRB2_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define SRB2_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define SRB2_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define SRB2_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define SRB2_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define SRB2_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define SRB2_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define SRB2_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define SRB2_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define SRB2_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define SRB2_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define SRB2_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define SRB2_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define SRB2_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define SRB2_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define SRB2_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define SRB2_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define SRB2_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define SRB2_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define SRB2_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define SRB2_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define SRB2_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define SRB2_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define SRB2_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define SRB2_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define SRB2_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define SRB2_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define SRB2_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define SRB2_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define SRB2_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define SRB2_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define SRB2_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define SRB2_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define SRB2_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define SRB2_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define SRB2_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define SRB2_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define SRB2_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define SRB2_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) SRB2_JSON_PASTE2(func, v1) SRB2_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define SRB2_JSON_TO(v1) to_json(srb2_json_j.as_object()[#v1], srb2_json_t.v1); +#define SRB2_JSON_FROM(v1) srb2_json_t.v1 = srb2_json_j.as_object().at(#v1); +#define SRB2_JSON_FROM_WITH_DEFAULT(v1) if (srb2_json_j.as_object().find(#v1) != srb2_json_j.as_object().end()) \ + { \ + from_json(srb2_json_j.as_object().find(#v1)->second, srb2_json_t.v1); \ + } \ + else \ + { \ + srb2_json_t.v1 = srb2_json_default_obj.v1; \ + } + +/*! +@brief macro +@def SRB2_JSON_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define SRB2_JSON_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { srb2_json_j = srb2::JsonObject(); SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM, __VA_ARGS__)) } + +#define SRB2_JSON_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + friend void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { srb2_json_j = srb2::JsonObject(); SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { const Type srb2_json_default_obj{}; SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } + +/*! +@brief macro +@def SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT +@since version 3.11.0 +*/ +#define SRB2_JSON_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ + inline void to_json(srb2::JsonValue& srb2_json_j, const Type& srb2_json_t) { SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const srb2::JsonValue& srb2_json_j, Type& srb2_json_t) { const Type srb2_json_default_obj{}; SRB2_JSON_EXPAND(SRB2_JSON_PASTE(SRB2_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }