RingRacers/src/core/json.cpp
2025-03-16 18:44:13 -05:00

1809 lines
45 KiB
C++

// 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 <cmath>
#include <limits>
#include <stdexcept>
#include <string>
#include <fmt/format.h>
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<std::string>(string_) : std::string(); }
template <> std::string_view JsonValue::get() const { return type_ == Type::kString ? static_cast<std::string_view>(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<std::byte> JsonValue::to_ubjson() const
{
srb2::Vector<std::byte> out;
do_to_ubjson(out);
return out;
}
void JsonValue::value_to_ubjson(srb2::Vector<std::byte>& out)
{
out.push_back(std::byte { 'Z' });
}
void JsonValue::value_to_ubjson(srb2::Vector<std::byte>& out, bool value)
{
out.push_back(value ? std::byte { 'T' } : std::byte { 'F' });
}
void JsonValue::value_to_ubjson(srb2::Vector<std::byte>& 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<std::byte>& 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<uint8_t>().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<int16_t>().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<int32_t>().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<int64_t>().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<std::byte>& out, const String& value, bool include_type)
{
if (include_type)
{
out.push_back(std::byte { 'S' });
}
value_to_ubjson(out, static_cast<uint64_t>(value.size()));
for (auto c : value)
{
out.push_back((std::byte)(c));
}
}
void JsonValue::value_to_ubjson(srb2::Vector<std::byte>& 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<std::byte>& 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<uint64_t>(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<std::byte>& 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<const std::byte>& ubjson)
{
if (ubjson.size() < 1) throw JsonParseError("insufficient data");
uint8_t ret = std::to_integer<uint8_t>(ubjson[0]);
ubjson = ubjson.subspan(1);
return ret;
}
static int8_t i8_from_ubjson(tcb::span<const std::byte>& ubjson)
{
if (ubjson.size() < 1) throw JsonParseError("insufficient data");
int8_t ret = std::to_integer<int8_t>(ubjson[0]);
ubjson = ubjson.subspan(1);
return ret;
}
static int16_t i16_from_ubjson(tcb::span<const std::byte>& ubjson)
{
uint8_t b[2];
uint16_t native;
int16_t ret;
if (ubjson.size() < 2) throw JsonParseError("insufficient data");
b[0] = std::to_integer<uint8_t>(ubjson[0]);
b[1] = std::to_integer<uint8_t>(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<const std::byte>& ubjson)
{
uint8_t b[4];
uint32_t native;
int32_t ret;
if (ubjson.size() < 4) throw JsonParseError("insufficient data");
b[0] = std::to_integer<uint8_t>(ubjson[0]);
b[1] = std::to_integer<uint8_t>(ubjson[1]);
b[2] = std::to_integer<uint8_t>(ubjson[2]);
b[3] = std::to_integer<uint8_t>(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<const std::byte>& ubjson)
{
uint64_t b[8];
uint64_t native;
int64_t ret;
if (ubjson.size() < 8) throw JsonParseError("insufficient data");
b[0] = std::to_integer<uint64_t>(ubjson[0]);
b[1] = std::to_integer<uint64_t>(ubjson[1]);
b[2] = std::to_integer<uint64_t>(ubjson[2]);
b[3] = std::to_integer<uint64_t>(ubjson[3]);
b[4] = std::to_integer<uint64_t>(ubjson[4]);
b[5] = std::to_integer<uint64_t>(ubjson[5]);
b[6] = std::to_integer<uint64_t>(ubjson[6]);
b[7] = std::to_integer<uint64_t>(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<const std::byte>& ubjson)
{
uint8_t b[8];
uint32_t native;
float ret;
if (ubjson.size() < 4) throw JsonParseError("insufficient data");
b[0] = std::to_integer<uint8_t>(ubjson[0]);
b[1] = std::to_integer<uint8_t>(ubjson[1]);
b[2] = std::to_integer<uint8_t>(ubjson[2]);
b[3] = std::to_integer<uint8_t>(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<const std::byte>& ubjson)
{
uint64_t b[8];
uint64_t native;
double ret;
if (ubjson.size() < 8) throw JsonParseError("insufficient data");
b[0] = std::to_integer<uint64_t>(ubjson[0]);
b[1] = std::to_integer<uint64_t>(ubjson[1]);
b[2] = std::to_integer<uint64_t>(ubjson[2]);
b[3] = std::to_integer<uint64_t>(ubjson[3]);
b[4] = std::to_integer<uint64_t>(ubjson[4]);
b[5] = std::to_integer<uint64_t>(ubjson[5]);
b[6] = std::to_integer<uint64_t>(ubjson[6]);
b[7] = std::to_integer<uint64_t>(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<const std::byte>& 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<const std::byte>& ubjson)
{
uint64_t len = length_from_ubjson(ubjson);
if (len > std::numeric_limits<size_t>().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 <typename F>
static void read_ubjson_array_elements(F f, JsonArray& arr, tcb::span<const std::byte>& ubjson, uint64_t len)
{
ubjson = ubjson.subspan(1);
for (uint64_t i = 0; i < len; i++)
{
arr.push_back((f)(ubjson));
}
}
template <typename F>
static void read_ubjson_array_elements(F f, JsonArray& arr, tcb::span<const std::byte>& 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<const std::byte>& ubjson, int depth);
static JsonObject object_from_ubjson(tcb::span<const std::byte>& ubjson, int depth);
static JsonArray array_from_ubjson(tcb::span<const std::byte>& 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 <typename F>
static void read_ubjson_object_elements(F f, JsonObject& obj, tcb::span<const std::byte>& 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 <typename F>
static void read_ubjson_object_elements(F f, JsonObject& obj, tcb::span<const std::byte>& 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<const std::byte>& 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<const std::byte>& 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<const std::byte> 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<String>();
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<String>(v));
}
void srb2::from_json(const JsonValue& from, bool& value) { value = from.get<bool>(); }
void srb2::from_json(const JsonValue& from, int8_t& value) { value = from.get<int8_t>(); }
void srb2::from_json(const JsonValue& from, int16_t& value) { value = from.get<int16_t>(); }
void srb2::from_json(const JsonValue& from, int32_t& value) { value = from.get<int32_t>(); }
void srb2::from_json(const JsonValue& from, int64_t& value) { value = from.get<int64_t>(); }
void srb2::from_json(const JsonValue& from, uint8_t& value) { value = from.get<uint8_t>(); }
void srb2::from_json(const JsonValue& from, uint16_t& value) { value = from.get<uint16_t>(); }
void srb2::from_json(const JsonValue& from, uint32_t& value) { value = from.get<uint32_t>(); }
void srb2::from_json(const JsonValue& from, uint64_t& value) { value = from.get<uint64_t>(); }
void srb2::from_json(const JsonValue& from, float& value) { value = from.get<float>(); }
void srb2::from_json(const JsonValue& from, double& value) { value = from.get<double>(); }
void srb2::from_json(const JsonValue& from, String& value) { value = from.get<String>(); }
void srb2::from_json(const JsonValue& from, JsonArray& value) { value = from.get<JsonArray>(); }
void srb2::from_json(const JsonValue& from, JsonObject& value) { value = from.get<JsonObject>(); }
void srb2::from_json(const JsonValue& from, std::string& to)
{
to = static_cast<std::string>(from.get<String>());
}
template class srb2::Vector<srb2::JsonValue>;
template class srb2::HashMap<srb2::String, srb2::JsonValue>;