[upstream_utils] Add jart/json.cpp

This commit is contained in:
Peter Johnson
2026-03-29 15:37:32 -07:00
parent bfea2b7e1f
commit de3e211fdb
34 changed files with 6397 additions and 6 deletions

View File

@@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sun, 29 Mar 2026 16:46:09 -0700
Subject: [PATCH 01/25] Move to wpi::util namespace
---
json.cpp | 4 ++--
json.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/json.cpp b/json.cpp
index 0b3e3730406ecf3bf283a56d93b7cc8c3052d61d..645266767781cb3ebdb700bf6761e6928806806e 100644
--- a/json.cpp
+++ b/json.cpp
@@ -83,7 +83,7 @@
#define ON_LOGIC_ERROR(s) abort()
#endif
-namespace jt {
+namespace wpi::util {
static const char kJsonStr[256] = {
1, 1, 1, 1, 1, 1, 1, 1, // 0000 ascii (0)
@@ -1313,4 +1313,4 @@ Json::StatusToString(Json::Status status)
}
}
-} // namespace jt
+} // namespace wpi::util
diff --git a/json.h b/json.h
index 6bb9087f452ae18dfad7c52b95de27c39a886341..c676e651d7c2591a0fb07d8d8b28738cbd1defab 100644
--- a/json.h
+++ b/json.h
@@ -20,7 +20,7 @@
#include <string>
#include <vector>
-namespace jt {
+namespace wpi::util {
class Json
{
@@ -221,4 +221,4 @@ class Json
static Status parse(Json&, const char*&, const char*, int, int);
};
-} // namespace jt
+} // namespace wpi::util

View File

@@ -0,0 +1,599 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sun, 29 Mar 2026 16:47:26 -0700
Subject: [PATCH 02/25] Rename class from Json to json
---
json.cpp | 142 +++++++++++++++++++++++++++----------------------------
json.h | 54 ++++++++++-----------
2 files changed, 98 insertions(+), 98 deletions(-)
diff --git a/json.cpp b/json.cpp
index 645266767781cb3ebdb700bf6761e6928806806e..3e59a885b55d3fddc418903112595fe5de223f34 100644
--- a/json.cpp
+++ b/json.cpp
@@ -242,7 +242,7 @@ LongToString(char* p, long long x)
return UlongToString(p, x);
}
-Json::Json(unsigned long value)
+json::json(unsigned long value)
{
if (value <= LLONG_MAX) {
type_ = Long;
@@ -253,7 +253,7 @@ Json::Json(unsigned long value)
}
}
-Json::Json(unsigned long long value)
+json::json(unsigned long long value)
{
if (value <= LLONG_MAX) {
type_ = Long;
@@ -264,7 +264,7 @@ Json::Json(unsigned long long value)
}
}
-Json::Json(const char* value)
+json::json(const char* value)
{
if (value) {
type_ = String;
@@ -274,18 +274,18 @@ Json::Json(const char* value)
}
}
-Json::Json(const std::string& value) : type_(String), string_value(value)
+json::json(const std::string& value) : type_(String), string_value(value)
{
}
-Json::~Json()
+json::~json()
{
if (type_ >= String)
clear();
}
void
-Json::clear()
+json::clear()
{
switch (type_) {
case String:
@@ -303,7 +303,7 @@ Json::clear()
type_ = Null;
}
-Json::Json(const Json& other) : type_(other.type_)
+json::json(const json& other) : type_(other.type_)
{
switch (type_) {
case Null:
@@ -324,18 +324,18 @@ Json::Json(const Json& other) : type_(other.type_)
new (&string_value) std::string(other.string_value);
break;
case Array:
- new (&array_value) std::vector<Json>(other.array_value);
+ new (&array_value) std::vector<json>(other.array_value);
break;
case Object:
- new (&object_value) std::map<std::string, Json>(other.object_value);
+ new (&object_value) std::map<std::string, json>(other.object_value);
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
}
}
-Json&
-Json::operator=(const Json& other)
+json&
+json::operator=(const json& other)
{
if (this != &other) {
if (type_ >= String)
@@ -360,11 +360,11 @@ Json::operator=(const Json& other)
new (&string_value) std::string(other.string_value);
break;
case Array:
- new (&array_value) std::vector<Json>(other.array_value);
+ new (&array_value) std::vector<json>(other.array_value);
break;
case Object:
new (&object_value)
- std::map<std::string, Json>(other.object_value);
+ std::map<std::string, json>(other.object_value);
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
@@ -373,7 +373,7 @@ Json::operator=(const Json& other)
return *this;
}
-Json::Json(Json&& other) : type_(other.type_)
+json::json(json&& other) : type_(other.type_)
{
switch (type_) {
case Null:
@@ -394,11 +394,11 @@ Json::Json(Json&& other) : type_(other.type_)
new (&string_value) std::string(std::move(other.string_value));
break;
case Array:
- new (&array_value) std::vector<Json>(std::move(other.array_value));
+ new (&array_value) std::vector<json>(std::move(other.array_value));
break;
case Object:
new (&object_value)
- std::map<std::string, Json>(std::move(other.object_value));
+ std::map<std::string, json>(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
@@ -406,8 +406,8 @@ Json::Json(Json&& other) : type_(other.type_)
other.type_ = Null;
}
-Json&
-Json::operator=(Json&& other)
+json&
+json::operator=(json&& other)
{
if (this != &other) {
if (type_ >= String)
@@ -433,11 +433,11 @@ Json::operator=(Json&& other)
break;
case Array:
new (&array_value)
- std::vector<Json>(std::move(other.array_value));
+ std::vector<json>(std::move(other.array_value));
break;
case Object:
new (&object_value)
- std::map<std::string, Json>(std::move(other.object_value));
+ std::map<std::string, json>(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");;
@@ -448,7 +448,7 @@ Json::operator=(Json&& other)
}
double
-Json::getNumber() const
+json::getNumber() const
{
switch (type_) {
case Long:
@@ -463,7 +463,7 @@ Json::getNumber() const
}
long long
-Json::getLong() const
+json::getLong() const
{
switch (type_) {
case Long:
@@ -474,7 +474,7 @@ Json::getLong() const
}
bool
-Json::getBool() const
+json::getBool() const
{
switch (type_) {
case Bool:
@@ -485,7 +485,7 @@ Json::getBool() const
}
float
-Json::getFloat() const
+json::getFloat() const
{
switch (type_) {
case Float:
@@ -498,7 +498,7 @@ Json::getFloat() const
}
double
-Json::getDouble() const
+json::getDouble() const
{
switch (type_) {
case Float:
@@ -511,7 +511,7 @@ Json::getDouble() const
}
std::string&
-Json::getString()
+json::getString()
{
switch (type_) {
case String:
@@ -521,8 +521,8 @@ Json::getString()
}
}
-std::vector<Json>&
-Json::getArray()
+std::vector<json>&
+json::getArray()
{
switch (type_) {
case Array:
@@ -532,8 +532,8 @@ Json::getArray()
}
}
-std::map<std::string, Json>&
-Json::getObject()
+std::map<std::string, json>&
+json::getObject()
{
switch (type_) {
case Object:
@@ -544,33 +544,33 @@ Json::getObject()
}
void
-Json::setArray()
+json::setArray()
{
if (type_ >= String)
clear();
type_ = Array;
- new (&array_value) std::vector<Json>();
+ new (&array_value) std::vector<json>();
}
void
-Json::setObject()
+json::setObject()
{
if (type_ >= String)
clear();
type_ = Object;
- new (&object_value) std::map<std::string, Json>();
+ new (&object_value) std::map<std::string, json>();
}
bool
-Json::contains(const std::string& key) const
+json::contains(const std::string& key) const
{
if (!isObject())
return false;
return object_value.find(key) != object_value.end();
}
-Json&
-Json::operator[](size_t index)
+json&
+json::operator[](size_t index)
{
if (!isArray())
setArray();
@@ -580,8 +580,8 @@ Json::operator[](size_t index)
return array_value[index];
}
-Json&
-Json::operator[](const std::string& key)
+json&
+json::operator[](const std::string& key)
{
if (!isObject())
setObject();
@@ -589,7 +589,7 @@ Json::operator[](const std::string& key)
}
std::string
-Json::toString() const
+json::toString() const
{
std::string b;
marshal(b, false, 0);
@@ -597,7 +597,7 @@ Json::toString() const
}
std::string
-Json::toStringPretty() const
+json::toStringPretty() const
{
std::string b;
marshal(b, true, 0);
@@ -605,7 +605,7 @@ Json::toStringPretty() const
}
void
-Json::marshal(std::string& b, bool pretty, int indent) const
+json::marshal(std::string& b, bool pretty, int indent) const
{
switch (type_) {
case Null:
@@ -692,7 +692,7 @@ Json::marshal(std::string& b, bool pretty, int indent) const
}
void
-Json::stringify(std::string& b, const std::string& s)
+json::stringify(std::string& b, const std::string& s)
{
b += '"';
serialize(b, s);
@@ -700,7 +700,7 @@ Json::stringify(std::string& b, const std::string& s)
}
void
-Json::serialize(std::string& sb, const std::string& s)
+json::serialize(std::string& sb, const std::string& s)
{
size_t i, j, m;
wint_t x, a, b;
@@ -768,8 +768,8 @@ Json::serialize(std::string& sb, const std::string& s)
}
}
-Json::Status
-Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
+json::Status
+json::parse(json& j, const char*& p, const char* e, int context, int depth)
{
char w[4];
long long x;
@@ -818,8 +818,8 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
if (context & (KEY | COLON | COMMA))
goto OnColonCommaKey;
if (p + 4 <= e && READ32LE(p) == READ32LE("alse")) {
- json.type_ = Bool;
- json.bool_value = false;
+ j.type_ = Bool;
+ j.bool_value = false;
p += 4;
return success;
} else {
@@ -830,8 +830,8 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
if (context & (KEY | COLON | COMMA))
goto OnColonCommaKey;
if (p + 3 <= e && READ32LE(p - 1) == READ32LE("true")) {
- json.type_ = Bool;
- json.bool_value = true;
+ j.type_ = Bool;
+ j.bool_value = true;
p += 3;
return success;
} else {
@@ -873,8 +873,8 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
return unexpected_octal;
}
}
- json.type_ = Long;
- json.long_value = 0;
+ j.type_ = Long;
+ j.long_value = 0;
return success;
case '1':
@@ -905,13 +905,13 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
break;
}
}
- json.type_ = Long;
- json.long_value = x;
+ j.type_ = Long;
+ j.long_value = x;
return success;
UseDubble: // number
- json.type_ = Double;
- json.double_value = StringToDouble(a, e - a, &c);
+ j.type_ = Double;
+ j.double_value = StringToDouble(a, e - a, &c);
if (c <= 0)
return bad_double;
if (a + c < e && (a[c] == 'e' || a[c] == 'E'))
@@ -922,15 +922,15 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
case '[': { // Array
if (context & (COLON | COMMA | KEY))
goto OnColonCommaKey;
- json.setArray();
- Json value;
+ j.setArray();
+ json value;
for (context = ARRAY, i = 0;;) {
Status status = parse(value, p, e, context, depth - 1);
if (status == absent_value)
return success;
if (status != success)
return status;
- json.array_value.emplace_back(std::move(value));
+ j.array_value.emplace_back(std::move(value));
context = ARRAY | COMMA;
}
}
@@ -948,9 +948,9 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
case '{': { // Object
if (context & (COLON | COMMA | KEY))
goto OnColonCommaKey;
- json.setObject();
+ j.setObject();
context = KEY | OBJECT;
- Json key, value;
+ json key, value;
for (;;) {
Status status = parse(key, p, e, context, depth - 1);
if (status == absent_value)
@@ -964,7 +964,7 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
return object_missing_value;
if (status != success)
return status;
- json.object_value.emplace(std::move(key.string_value),
+ j.object_value.emplace(std::move(key.string_value),
std::move(value));
context = KEY | COMMA | OBJECT;
key.clear();
@@ -985,8 +985,8 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
break;
case DQUOTE:
- json.type_ = String;
- new (&json.string_value) std::string(std::move(b));
+ j.type_ = String;
+ new (&j.string_value) std::string(std::move(b));
return success;
case BACKSLASH:
@@ -1221,16 +1221,16 @@ Json::parse(Json& json, const char*& p, const char* e, int context, int depth)
return unexpected_eof;
}
-std::pair<Json::Status, Json>
-Json::parse(const std::string& s)
+std::pair<json::Status, json>
+json::parse(const std::string& s)
{
- Json::Status s2;
- std::pair<Json::Status, Json> res;
+ json::Status s2;
+ std::pair<json::Status, json> res;
const char* p = s.data();
const char* e = s.data() + s.size();
res.first = parse(res.second, p, e, 0, DEPTH);
- if (res.first == Json::success) {
- Json j2;
+ if (res.first == json::success) {
+ json j2;
s2 = parse(j2, p, e, 0, DEPTH);
if (s2 != absent_value)
res.first = trailing_content;
@@ -1239,7 +1239,7 @@ Json::parse(const std::string& s)
}
const char*
-Json::StatusToString(Json::Status status)
+json::StatusToString(json::Status status)
{
switch (status) {
case success:
diff --git a/json.h b/json.h
index c676e651d7c2591a0fb07d8d8b28738cbd1defab..b1f175cf070c6b0dbebffdc60c38954c14f77745 100644
--- a/json.h
+++ b/json.h
@@ -22,7 +22,7 @@
namespace wpi::util {
-class Json
+class json
{
public:
enum Type
@@ -83,55 +83,55 @@ class Json
double double_value;
long long long_value;
std::string string_value;
- std::vector<Json> array_value;
- std::map<std::string, Json> object_value;
+ std::vector<json> array_value;
+ std::map<std::string, json> object_value;
};
public:
static const char* StatusToString(Status);
- static std::pair<Status, Json> parse(const std::string&);
+ static std::pair<Status, json> parse(const std::string&);
- Json(const Json&);
- Json(Json&&);
- Json(unsigned long);
- Json(unsigned long long);
- Json(const char*);
- Json(const std::string&);
- ~Json();
+ json(const json&);
+ json(json&&);
+ json(unsigned long);
+ json(unsigned long long);
+ json(const char*);
+ json(const std::string&);
+ ~json();
- Json(const std::nullptr_t = nullptr) : type_(Null)
+ json(const std::nullptr_t = nullptr) : type_(Null)
{
}
- Json(bool value) : type_(Bool), bool_value(value)
+ json(bool value) : type_(Bool), bool_value(value)
{
}
- Json(int value) : type_(Long), long_value(value)
+ json(int value) : type_(Long), long_value(value)
{
}
- Json(float value) : type_(Float), float_value(value)
+ json(float value) : type_(Float), float_value(value)
{
}
- Json(unsigned value) : type_(Long), long_value(value)
+ json(unsigned value) : type_(Long), long_value(value)
{
}
- Json(long value) : type_(Long), long_value(value)
+ json(long value) : type_(Long), long_value(value)
{
}
- Json(long long value) : type_(Long), long_value(value)
+ json(long long value) : type_(Long), long_value(value)
{
}
- Json(double value) : type_(Double), double_value(value)
+ json(double value) : type_(Double), double_value(value)
{
}
- Json(std::string&& value) : type_(String), string_value(std::move(value))
+ json(std::string&& value) : type_(String), string_value(std::move(value))
{
}
@@ -191,8 +191,8 @@ class Json
double getNumber() const;
long long getLong() const;
std::string& getString();
- std::vector<Json>& getArray();
- std::map<std::string, Json>& getObject();
+ std::vector<json>& getArray();
+ std::map<std::string, json>& getObject();
bool contains(const std::string&) const;
@@ -202,11 +202,11 @@ class Json
std::string toString() const;
std::string toStringPretty() const;
- Json& operator=(const Json&);
- Json& operator=(Json&&);
+ json& operator=(const json&);
+ json& operator=(json&&);
- Json& operator[](size_t);
- Json& operator[](const std::string&);
+ json& operator[](size_t);
+ json& operator[](const std::string&);
operator std::string() const
{
@@ -218,7 +218,7 @@ class Json
void marshal(std::string&, bool, int) const;
static void stringify(std::string&, const std::string&);
static void serialize(std::string&, const std::string&);
- static Status parse(Json&, const char*&, const char*, int, int);
+ static Status parse(json&, const char*&, const char*, int, int);
};
} // namespace wpi::util

View File

@@ -0,0 +1,286 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sun, 29 Mar 2026 16:50:21 -0700
Subject: [PATCH 03/25] Use snake_case names
---
json.cpp | 40 ++++++++++++++++++++--------------------
json.h | 48 ++++++++++++++++++++++++------------------------
2 files changed, 44 insertions(+), 44 deletions(-)
diff --git a/json.cpp b/json.cpp
index 3e59a885b55d3fddc418903112595fe5de223f34..939efbcf8e550e7c48d180f81be39b476c5990fe 100644
--- a/json.cpp
+++ b/json.cpp
@@ -448,7 +448,7 @@ json::operator=(json&& other)
}
double
-json::getNumber() const
+json::get_number() const
{
switch (type_) {
case Long:
@@ -463,7 +463,7 @@ json::getNumber() const
}
long long
-json::getLong() const
+json::get_long() const
{
switch (type_) {
case Long:
@@ -474,7 +474,7 @@ json::getLong() const
}
bool
-json::getBool() const
+json::get_bool() const
{
switch (type_) {
case Bool:
@@ -485,7 +485,7 @@ json::getBool() const
}
float
-json::getFloat() const
+json::get_float() const
{
switch (type_) {
case Float:
@@ -498,7 +498,7 @@ json::getFloat() const
}
double
-json::getDouble() const
+json::get_double() const
{
switch (type_) {
case Float:
@@ -511,7 +511,7 @@ json::getDouble() const
}
std::string&
-json::getString()
+json::get_string()
{
switch (type_) {
case String:
@@ -522,7 +522,7 @@ json::getString()
}
std::vector<json>&
-json::getArray()
+json::get_array()
{
switch (type_) {
case Array:
@@ -533,7 +533,7 @@ json::getArray()
}
std::map<std::string, json>&
-json::getObject()
+json::get_object()
{
switch (type_) {
case Object:
@@ -544,7 +544,7 @@ json::getObject()
}
void
-json::setArray()
+json::set_array()
{
if (type_ >= String)
clear();
@@ -553,7 +553,7 @@ json::setArray()
}
void
-json::setObject()
+json::set_object()
{
if (type_ >= String)
clear();
@@ -564,7 +564,7 @@ json::setObject()
bool
json::contains(const std::string& key) const
{
- if (!isObject())
+ if (!is_object())
return false;
return object_value.find(key) != object_value.end();
}
@@ -572,8 +572,8 @@ json::contains(const std::string& key) const
json&
json::operator[](size_t index)
{
- if (!isArray())
- setArray();
+ if (!is_array())
+ set_array();
if (index >= array_value.size()) {
array_value.resize(index + 1);
}
@@ -583,13 +583,13 @@ json::operator[](size_t index)
json&
json::operator[](const std::string& key)
{
- if (!isObject())
- setObject();
+ if (!is_object())
+ set_object();
return object_value[key];
}
std::string
-json::toString() const
+json::to_string() const
{
std::string b;
marshal(b, false, 0);
@@ -597,7 +597,7 @@ json::toString() const
}
std::string
-json::toStringPretty() const
+json::to_string_pretty() const
{
std::string b;
marshal(b, true, 0);
@@ -922,7 +922,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
case '[': { // Array
if (context & (COLON | COMMA | KEY))
goto OnColonCommaKey;
- j.setArray();
+ j.set_array();
json value;
for (context = ARRAY, i = 0;;) {
Status status = parse(value, p, e, context, depth - 1);
@@ -948,7 +948,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
case '{': { // Object
if (context & (COLON | COMMA | KEY))
goto OnColonCommaKey;
- j.setObject();
+ j.set_object();
context = KEY | OBJECT;
json key, value;
for (;;) {
@@ -957,7 +957,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
return success;
if (status != success)
return status;
- if (!key.isString())
+ if (!key.is_string())
return object_key_must_be_string;
status = parse(value, p, e, COLON, depth - 1);
if (status == absent_value)
diff --git a/json.h b/json.h
index b1f175cf070c6b0dbebffdc60c38954c14f77745..bf250d3aacb3508601e1e5d61531ec5e8248ebdb 100644
--- a/json.h
+++ b/json.h
@@ -135,72 +135,72 @@ class json
{
}
- Type getType() const
+ Type type() const
{
return type_;
}
- bool isNull() const
+ bool is_null() const
{
return type_ == Null;
}
- bool isBool() const
+ bool is_bool() const
{
return type_ == Bool;
}
- bool isNumber() const
+ bool is_number() const
{
- return isFloat() || isDouble() || isLong();
+ return is_float() || is_double() || is_long();
}
- bool isLong() const
+ bool is_long() const
{
return type_ == Long;
}
- bool isFloat() const
+ bool is_float() const
{
return type_ == Float;
}
- bool isDouble() const
+ bool is_double() const
{
return type_ == Double;
}
- bool isString() const
+ bool is_string() const
{
return type_ == String;
}
- bool isArray() const
+ bool is_array() const
{
return type_ == Array;
}
- bool isObject() const
+ bool is_object() const
{
return type_ == Object;
}
- bool getBool() const;
- float getFloat() const;
- double getDouble() const;
- double getNumber() const;
- long long getLong() const;
- std::string& getString();
- std::vector<json>& getArray();
- std::map<std::string, json>& getObject();
+ bool get_bool() const;
+ float get_float() const;
+ double get_double() const;
+ double get_number() const;
+ long long get_long() const;
+ std::string& get_string();
+ std::vector<json>& get_array();
+ std::map<std::string, json>& get_object();
bool contains(const std::string&) const;
- void setArray();
- void setObject();
+ void set_array();
+ void set_object();
- std::string toString() const;
- std::string toStringPretty() const;
+ std::string to_string() const;
+ std::string to_string_pretty() const;
json& operator=(const json&);
json& operator=(json&&);
@@ -210,7 +210,7 @@ class json
operator std::string() const
{
- return toString();
+ return to_string();
}
private:

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 07:31:40 -0700
Subject: [PATCH 04/25] Suppress pedantic warning
---
jtckdint.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/jtckdint.h b/jtckdint.h
index 4c71d9990ca233017332f3ede71b092da48b474f..ac6a52e5aab04116defe1d06b3831d3509fea8a2 100644
--- a/jtckdint.h
+++ b/jtckdint.h
@@ -57,6 +57,10 @@
#ifndef JTCKDINT_H_
#define JTCKDINT_H_
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
#ifdef __has_include
#define __ckd_has_include(x) __has_include(x)
#else

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 07:32:33 -0700
Subject: [PATCH 05/25] Suppress type limits warning
---
json.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/json.cpp b/json.cpp
index 939efbcf8e550e7c48d180f81be39b476c5990fe..8f45bb573deed5b8c7afaf3fe2b6b40fa21555c1 100644
--- a/json.cpp
+++ b/json.cpp
@@ -28,6 +28,10 @@
#include "double-conversion/double-to-string.h"
#include "double-conversion/string-to-double.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
+
#define KEY 1
#define COMMA 2
#define COLON 4

View File

@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 07:33:06 -0700
Subject: [PATCH 06/25] Update double-conversion include for wpi
---
json.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/json.cpp b/json.cpp
index 8f45bb573deed5b8c7afaf3fe2b6b40fa21555c1..87433664d2caf96b1c8f5b80eb4f4cc8d3d34f7e 100644
--- a/json.cpp
+++ b/json.cpp
@@ -25,8 +25,8 @@
#include <cstdlib>
#include <stdexcept>
-#include "double-conversion/double-to-string.h"
-#include "double-conversion/string-to-double.h"
+#include "wpi/double-conversion/double-to-string.h"
+#include "wpi/double-conversion/string-to-double.h"
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wtype-limits"

View File

@@ -0,0 +1,155 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 07:51:30 -0700
Subject: [PATCH 07/25] Use typedefs for array and object types
---
json.cpp | 32 ++++++++++++++------------------
json.h | 11 +++++++----
2 files changed, 21 insertions(+), 22 deletions(-)
diff --git a/json.cpp b/json.cpp
index 87433664d2caf96b1c8f5b80eb4f4cc8d3d34f7e..8fd64748250d9cd71fd31b8caf4281183e31b053 100644
--- a/json.cpp
+++ b/json.cpp
@@ -296,10 +296,10 @@ json::clear()
string_value.~basic_string();
break;
case Array:
- array_value.~vector();
+ array_value.~array_t();
break;
case Object:
- object_value.~map();
+ object_value.~object_t();
break;
default:
break;
@@ -328,10 +328,10 @@ json::json(const json& other) : type_(other.type_)
new (&string_value) std::string(other.string_value);
break;
case Array:
- new (&array_value) std::vector<json>(other.array_value);
+ new (&array_value) array_t(other.array_value);
break;
case Object:
- new (&object_value) std::map<std::string, json>(other.object_value);
+ new (&object_value) object_t(other.object_value);
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
@@ -364,11 +364,10 @@ json::operator=(const json& other)
new (&string_value) std::string(other.string_value);
break;
case Array:
- new (&array_value) std::vector<json>(other.array_value);
+ new (&array_value) array_t(other.array_value);
break;
case Object:
- new (&object_value)
- std::map<std::string, json>(other.object_value);
+ new (&object_value) object_t(other.object_value);
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
@@ -398,11 +397,10 @@ json::json(json&& other) : type_(other.type_)
new (&string_value) std::string(std::move(other.string_value));
break;
case Array:
- new (&array_value) std::vector<json>(std::move(other.array_value));
+ new (&array_value) array_t(std::move(other.array_value));
break;
case Object:
- new (&object_value)
- std::map<std::string, json>(std::move(other.object_value));
+ new (&object_value) object_t(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
@@ -436,12 +434,10 @@ json::operator=(json&& other)
new (&string_value) std::string(std::move(other.string_value));
break;
case Array:
- new (&array_value)
- std::vector<json>(std::move(other.array_value));
+ new (&array_value) array_t(std::move(other.array_value));
break;
case Object:
- new (&object_value)
- std::map<std::string, json>(std::move(other.object_value));
+ new (&object_value) object_t(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");;
@@ -525,7 +521,7 @@ json::get_string()
}
}
-std::vector<json>&
+json::array_t&
json::get_array()
{
switch (type_) {
@@ -536,7 +532,7 @@ json::get_array()
}
}
-std::map<std::string, json>&
+json::object_t&
json::get_object()
{
switch (type_) {
@@ -553,7 +549,7 @@ json::set_array()
if (type_ >= String)
clear();
type_ = Array;
- new (&array_value) std::vector<json>();
+ new (&array_value) array_t();
}
void
@@ -562,7 +558,7 @@ json::set_object()
if (type_ >= String)
clear();
type_ = Object;
- new (&object_value) std::map<std::string, json>();
+ new (&object_value) object_t();
}
bool
diff --git a/json.h b/json.h
index bf250d3aacb3508601e1e5d61531ec5e8248ebdb..e25116a7f7c26525028b11928e665c1883b3ba65 100644
--- a/json.h
+++ b/json.h
@@ -25,6 +25,9 @@ namespace wpi::util {
class json
{
public:
+ using array_t = std::vector<json>;
+ using object_t = std::map<std::string, json>;
+
enum Type
{
Null,
@@ -83,8 +86,8 @@ class json
double double_value;
long long long_value;
std::string string_value;
- std::vector<json> array_value;
- std::map<std::string, json> object_value;
+ array_t array_value;
+ object_t object_value;
};
public:
@@ -191,8 +194,8 @@ class json
double get_number() const;
long long get_long() const;
std::string& get_string();
- std::vector<json>& get_array();
- std::map<std::string, json>& get_object();
+ array_t& get_array();
+ object_t& get_object();
bool contains(const std::string&) const;

View File

@@ -0,0 +1,582 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 07:56:14 -0700
Subject: [PATCH 08/25] Change Type to an enum class
---
json.cpp | 156 +++++++++++++++++++++++++++----------------------------
json.h | 36 ++++++-------
2 files changed, 96 insertions(+), 96 deletions(-)
diff --git a/json.cpp b/json.cpp
index 8fd64748250d9cd71fd31b8caf4281183e31b053..88ce30f8669a02cdc54e741a2a5a0663953d4acb 100644
--- a/json.cpp
+++ b/json.cpp
@@ -249,10 +249,10 @@ LongToString(char* p, long long x)
json::json(unsigned long value)
{
if (value <= LLONG_MAX) {
- type_ = Long;
+ type_ = Type::Long;
long_value = value;
} else {
- type_ = Double;
+ type_ = Type::Double;
double_value = value;
}
}
@@ -260,10 +260,10 @@ json::json(unsigned long value)
json::json(unsigned long long value)
{
if (value <= LLONG_MAX) {
- type_ = Long;
+ type_ = Type::Long;
long_value = value;
} else {
- type_ = Double;
+ type_ = Type::Double;
double_value = value;
}
}
@@ -271,20 +271,20 @@ json::json(unsigned long long value)
json::json(const char* value)
{
if (value) {
- type_ = String;
+ type_ = Type::String;
new (&string_value) std::string(value);
} else {
- type_ = Null;
+ type_ = Type::Null;
}
}
-json::json(const std::string& value) : type_(String), string_value(value)
+json::json(const std::string& value) : type_(Type::String), string_value(value)
{
}
json::~json()
{
- if (type_ >= String)
+ if (type_ >= Type::String)
clear();
}
@@ -292,45 +292,45 @@ void
json::clear()
{
switch (type_) {
- case String:
+ case Type::String:
string_value.~basic_string();
break;
- case Array:
+ case Type::Array:
array_value.~array_t();
break;
- case Object:
+ case Type::Object:
object_value.~object_t();
break;
default:
break;
}
- type_ = Null;
+ type_ = Type::Null;
}
json::json(const json& other) : type_(other.type_)
{
switch (type_) {
- case Null:
+ case Type::Null:
break;
- case Bool:
+ case Type::Bool:
bool_value = other.bool_value;
break;
- case Long:
+ case Type::Long:
long_value = other.long_value;
break;
- case Float:
+ case Type::Float:
float_value = other.float_value;
break;
- case Double:
+ case Type::Double:
double_value = other.double_value;
break;
- case String:
+ case Type::String:
new (&string_value) std::string(other.string_value);
break;
- case Array:
+ case Type::Array:
new (&array_value) array_t(other.array_value);
break;
- case Object:
+ case Type::Object:
new (&object_value) object_t(other.object_value);
break;
default:
@@ -342,31 +342,31 @@ json&
json::operator=(const json& other)
{
if (this != &other) {
- if (type_ >= String)
+ if (type_ >= Type::String)
clear();
type_ = other.type_;
switch (type_) {
- case Null:
+ case Type::Null:
break;
- case Bool:
+ case Type::Bool:
bool_value = other.bool_value;
break;
- case Long:
+ case Type::Long:
long_value = other.long_value;
break;
- case Float:
+ case Type::Float:
float_value = other.float_value;
break;
- case Double:
+ case Type::Double:
double_value = other.double_value;
break;
- case String:
+ case Type::String:
new (&string_value) std::string(other.string_value);
break;
- case Array:
+ case Type::Array:
new (&array_value) array_t(other.array_value);
break;
- case Object:
+ case Type::Object:
new (&object_value) object_t(other.object_value);
break;
default:
@@ -379,70 +379,70 @@ json::operator=(const json& other)
json::json(json&& other) : type_(other.type_)
{
switch (type_) {
- case Null:
+ case Type::Null:
break;
- case Bool:
+ case Type::Bool:
bool_value = other.bool_value;
break;
- case Long:
+ case Type::Long:
long_value = other.long_value;
break;
- case Float:
+ case Type::Float:
float_value = other.float_value;
break;
- case Double:
+ case Type::Double:
double_value = other.double_value;
break;
- case String:
+ case Type::String:
new (&string_value) std::string(std::move(other.string_value));
break;
- case Array:
+ case Type::Array:
new (&array_value) array_t(std::move(other.array_value));
break;
- case Object:
+ case Type::Object:
new (&object_value) object_t(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
}
- other.type_ = Null;
+ other.type_ = Type::Null;
}
json&
json::operator=(json&& other)
{
if (this != &other) {
- if (type_ >= String)
+ if (type_ >= Type::String)
clear();
type_ = other.type_;
switch (type_) {
- case Null:
+ case Type::Null:
break;
- case Bool:
+ case Type::Bool:
bool_value = other.bool_value;
break;
- case Long:
+ case Type::Long:
long_value = other.long_value;
break;
- case Float:
+ case Type::Float:
float_value = other.float_value;
break;
- case Double:
+ case Type::Double:
double_value = other.double_value;
break;
- case String:
+ case Type::String:
new (&string_value) std::string(std::move(other.string_value));
break;
- case Array:
+ case Type::Array:
new (&array_value) array_t(std::move(other.array_value));
break;
- case Object:
+ case Type::Object:
new (&object_value) object_t(std::move(other.object_value));
break;
default:
ON_LOGIC_ERROR("Unhandled JSON type.");;
}
- other.type_ = Null;
+ other.type_ = Type::Null;
}
return *this;
}
@@ -451,11 +451,11 @@ double
json::get_number() const
{
switch (type_) {
- case Long:
+ case Type::Long:
return long_value;
- case Float:
+ case Type::Float:
return float_value;
- case Double:
+ case Type::Double:
return double_value;
default:
ON_LOGIC_ERROR("JSON value is not a number.");
@@ -466,7 +466,7 @@ long long
json::get_long() const
{
switch (type_) {
- case Long:
+ case Type::Long:
return long_value;
default:
ON_LOGIC_ERROR("JSON value is not a long.");
@@ -477,7 +477,7 @@ bool
json::get_bool() const
{
switch (type_) {
- case Bool:
+ case Type::Bool:
return bool_value;
default:
ON_LOGIC_ERROR("JSON value is not a bool.");
@@ -488,9 +488,9 @@ float
json::get_float() const
{
switch (type_) {
- case Float:
+ case Type::Float:
return float_value;
- case Double:
+ case Type::Double:
return double_value;
default:
ON_LOGIC_ERROR("JSON value is not a floating-point number.");
@@ -501,9 +501,9 @@ double
json::get_double() const
{
switch (type_) {
- case Float:
+ case Type::Float:
return float_value;
- case Double:
+ case Type::Double:
return double_value;
default:
ON_LOGIC_ERROR("JSON value is not a floating-point number.");
@@ -514,7 +514,7 @@ std::string&
json::get_string()
{
switch (type_) {
- case String:
+ case Type::String:
return string_value;
default:
ON_LOGIC_ERROR("JSON value is not a string.");
@@ -525,7 +525,7 @@ json::array_t&
json::get_array()
{
switch (type_) {
- case Array:
+ case Type::Array:
return array_value;
default:
ON_LOGIC_ERROR("JSON value is not an array.");
@@ -536,7 +536,7 @@ json::object_t&
json::get_object()
{
switch (type_) {
- case Object:
+ case Type::Object:
return object_value;
default:
ON_LOGIC_ERROR("JSON value is not an object.");
@@ -546,18 +546,18 @@ json::get_object()
void
json::set_array()
{
- if (type_ >= String)
+ if (type_ >= Type::String)
clear();
- type_ = Array;
+ type_ = Type::Array;
new (&array_value) array_t();
}
void
json::set_object()
{
- if (type_ >= String)
+ if (type_ >= Type::String)
clear();
- type_ = Object;
+ type_ = Type::Object;
new (&object_value) object_t();
}
@@ -608,21 +608,21 @@ void
json::marshal(std::string& b, bool pretty, int indent) const
{
switch (type_) {
- case Null:
+ case Type::Null:
b += "null";
break;
- case String:
+ case Type::String:
stringify(b, string_value);
break;
- case Bool:
+ case Type::Bool:
b += bool_value ? "true" : "false";
break;
- case Long: {
+ case Type::Long: {
char buf[64];
b.append(buf, LongToString(buf, long_value) - buf);
break;
}
- case Float: {
+ case Type::Float: {
char buf[128];
double_conversion::StringBuilder db(buf, 128);
kDoubleToJson.ToShortestSingle(float_value, &db);
@@ -630,7 +630,7 @@ json::marshal(std::string& b, bool pretty, int indent) const
b += buf;
break;
}
- case Double: {
+ case Type::Double: {
char buf[128];
double_conversion::StringBuilder db(buf, 128);
kDoubleToJson.ToShortest(double_value, &db);
@@ -638,7 +638,7 @@ json::marshal(std::string& b, bool pretty, int indent) const
b += buf;
break;
}
- case Array: {
+ case Type::Array: {
bool once = false;
b += '[';
for (auto i = array_value.begin(); i != array_value.end(); ++i) {
@@ -654,7 +654,7 @@ json::marshal(std::string& b, bool pretty, int indent) const
b += ']';
break;
}
- case Object: {
+ case Type::Object: {
bool once = false;
b += '{';
for (auto i = object_value.begin(); i != object_value.end(); ++i) {
@@ -818,7 +818,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
if (context & (KEY | COLON | COMMA))
goto OnColonCommaKey;
if (p + 4 <= e && READ32LE(p) == READ32LE("alse")) {
- j.type_ = Bool;
+ j.type_ = Type::Bool;
j.bool_value = false;
p += 4;
return success;
@@ -830,7 +830,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
if (context & (KEY | COLON | COMMA))
goto OnColonCommaKey;
if (p + 3 <= e && READ32LE(p - 1) == READ32LE("true")) {
- j.type_ = Bool;
+ j.type_ = Type::Bool;
j.bool_value = true;
p += 3;
return success;
@@ -873,7 +873,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
return unexpected_octal;
}
}
- j.type_ = Long;
+ j.type_ = Type::Long;
j.long_value = 0;
return success;
@@ -905,12 +905,12 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
break;
}
}
- j.type_ = Long;
+ j.type_ = Type::Long;
j.long_value = x;
return success;
UseDubble: // number
- j.type_ = Double;
+ j.type_ = Type::Double;
j.double_value = StringToDouble(a, e - a, &c);
if (c <= 0)
return bad_double;
@@ -985,7 +985,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
break;
case DQUOTE:
- j.type_ = String;
+ j.type_ = Type::String;
new (&j.string_value) std::string(std::move(b));
return success;
diff --git a/json.h b/json.h
index e25116a7f7c26525028b11928e665c1883b3ba65..3d2cb52a1bfeafb5f1a8a1e9c6057a2611cbb41e 100644
--- a/json.h
+++ b/json.h
@@ -28,7 +28,7 @@ class json
using array_t = std::vector<json>;
using object_t = std::map<std::string, json>;
- enum Type
+ enum class Type
{
Null,
Bool,
@@ -102,39 +102,39 @@ class json
json(const std::string&);
~json();
- json(const std::nullptr_t = nullptr) : type_(Null)
+ json(const std::nullptr_t = nullptr) : type_(Type::Null)
{
}
- json(bool value) : type_(Bool), bool_value(value)
+ json(bool value) : type_(Type::Bool), bool_value(value)
{
}
- json(int value) : type_(Long), long_value(value)
+ json(int value) : type_(Type::Long), long_value(value)
{
}
- json(float value) : type_(Float), float_value(value)
+ json(float value) : type_(Type::Float), float_value(value)
{
}
- json(unsigned value) : type_(Long), long_value(value)
+ json(unsigned value) : type_(Type::Long), long_value(value)
{
}
- json(long value) : type_(Long), long_value(value)
+ json(long value) : type_(Type::Long), long_value(value)
{
}
- json(long long value) : type_(Long), long_value(value)
+ json(long long value) : type_(Type::Long), long_value(value)
{
}
- json(double value) : type_(Double), double_value(value)
+ json(double value) : type_(Type::Double), double_value(value)
{
}
- json(std::string&& value) : type_(String), string_value(std::move(value))
+ json(std::string&& value) : type_(Type::String), string_value(std::move(value))
{
}
@@ -145,12 +145,12 @@ class json
bool is_null() const
{
- return type_ == Null;
+ return type_ == Type::Null;
}
bool is_bool() const
{
- return type_ == Bool;
+ return type_ == Type::Bool;
}
bool is_number() const
@@ -160,32 +160,32 @@ class json
bool is_long() const
{
- return type_ == Long;
+ return type_ == Type::Long;
}
bool is_float() const
{
- return type_ == Float;
+ return type_ == Type::Float;
}
bool is_double() const
{
- return type_ == Double;
+ return type_ == Type::Double;
}
bool is_string() const
{
- return type_ == String;
+ return type_ == Type::String;
}
bool is_array() const
{
- return type_ == Array;
+ return type_ == Type::Array;
}
bool is_object() const
{
- return type_ == Object;
+ return type_ == Type::Object;
}
bool get_bool() const;

View File

@@ -0,0 +1,183 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:02:21 -0700
Subject: [PATCH 09/25] Rename long to int
---
json.cpp | 24 ++++++++++++------------
json.h | 18 +++++++++---------
2 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/json.cpp b/json.cpp
index 88ce30f8669a02cdc54e741a2a5a0663953d4acb..1266aa36b43155cbab6fe8d918fed2c56596f7a8 100644
--- a/json.cpp
+++ b/json.cpp
@@ -249,7 +249,7 @@ LongToString(char* p, long long x)
json::json(unsigned long value)
{
if (value <= LLONG_MAX) {
- type_ = Type::Long;
+ type_ = Type::Int;
long_value = value;
} else {
type_ = Type::Double;
@@ -260,7 +260,7 @@ json::json(unsigned long value)
json::json(unsigned long long value)
{
if (value <= LLONG_MAX) {
- type_ = Type::Long;
+ type_ = Type::Int;
long_value = value;
} else {
type_ = Type::Double;
@@ -315,7 +315,7 @@ json::json(const json& other) : type_(other.type_)
case Type::Bool:
bool_value = other.bool_value;
break;
- case Type::Long:
+ case Type::Int:
long_value = other.long_value;
break;
case Type::Float:
@@ -351,7 +351,7 @@ json::operator=(const json& other)
case Type::Bool:
bool_value = other.bool_value;
break;
- case Type::Long:
+ case Type::Int:
long_value = other.long_value;
break;
case Type::Float:
@@ -384,7 +384,7 @@ json::json(json&& other) : type_(other.type_)
case Type::Bool:
bool_value = other.bool_value;
break;
- case Type::Long:
+ case Type::Int:
long_value = other.long_value;
break;
case Type::Float:
@@ -421,7 +421,7 @@ json::operator=(json&& other)
case Type::Bool:
bool_value = other.bool_value;
break;
- case Type::Long:
+ case Type::Int:
long_value = other.long_value;
break;
case Type::Float:
@@ -451,7 +451,7 @@ double
json::get_number() const
{
switch (type_) {
- case Type::Long:
+ case Type::Int:
return long_value;
case Type::Float:
return float_value;
@@ -463,10 +463,10 @@ json::get_number() const
}
long long
-json::get_long() const
+json::get_int() const
{
switch (type_) {
- case Type::Long:
+ case Type::Int:
return long_value;
default:
ON_LOGIC_ERROR("JSON value is not a long.");
@@ -617,7 +617,7 @@ json::marshal(std::string& b, bool pretty, int indent) const
case Type::Bool:
b += bool_value ? "true" : "false";
break;
- case Type::Long: {
+ case Type::Int: {
char buf[64];
b.append(buf, LongToString(buf, long_value) - buf);
break;
@@ -873,7 +873,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
return unexpected_octal;
}
}
- j.type_ = Type::Long;
+ j.type_ = Type::Int;
j.long_value = 0;
return success;
@@ -905,7 +905,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
break;
}
}
- j.type_ = Type::Long;
+ j.type_ = Type::Int;
j.long_value = x;
return success;
diff --git a/json.h b/json.h
index 3d2cb52a1bfeafb5f1a8a1e9c6057a2611cbb41e..d9fdfe40c5cce5b306eee42668f9944e11f6aacd 100644
--- a/json.h
+++ b/json.h
@@ -32,7 +32,7 @@ class json
{
Null,
Bool,
- Long,
+ Int,
Float,
Double,
String,
@@ -110,7 +110,7 @@ class json
{
}
- json(int value) : type_(Type::Long), long_value(value)
+ json(int value) : type_(Type::Int), long_value(value)
{
}
@@ -118,15 +118,15 @@ class json
{
}
- json(unsigned value) : type_(Type::Long), long_value(value)
+ json(unsigned value) : type_(Type::Int), long_value(value)
{
}
- json(long value) : type_(Type::Long), long_value(value)
+ json(long value) : type_(Type::Int), long_value(value)
{
}
- json(long long value) : type_(Type::Long), long_value(value)
+ json(long long value) : type_(Type::Int), long_value(value)
{
}
@@ -155,12 +155,12 @@ class json
bool is_number() const
{
- return is_float() || is_double() || is_long();
+ return is_float() || is_double() || is_int();
}
- bool is_long() const
+ bool is_int() const
{
- return type_ == Type::Long;
+ return type_ == Type::Int;
}
bool is_float() const
@@ -192,7 +192,7 @@ class json
float get_float() const;
double get_double() const;
double get_number() const;
- long long get_long() const;
+ long long get_int() const;
std::string& get_string();
array_t& get_array();
object_t& get_object();

View File

@@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:04:27 -0700
Subject: [PATCH 10/25] Make non-object functions constexpr
---
json.h | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/json.h b/json.h
index d9fdfe40c5cce5b306eee42668f9944e11f6aacd..8b09d001acd0a966328301bc89ff72616f106a4b 100644
--- a/json.h
+++ b/json.h
@@ -102,35 +102,35 @@ class json
json(const std::string&);
~json();
- json(const std::nullptr_t = nullptr) : type_(Type::Null)
+ constexpr json(const std::nullptr_t = nullptr) : type_(Type::Null)
{
}
- json(bool value) : type_(Type::Bool), bool_value(value)
+ constexpr json(bool value) : type_(Type::Bool), bool_value(value)
{
}
- json(int value) : type_(Type::Int), long_value(value)
+ constexpr json(int value) : type_(Type::Int), long_value(value)
{
}
- json(float value) : type_(Type::Float), float_value(value)
+ constexpr json(float value) : type_(Type::Float), float_value(value)
{
}
- json(unsigned value) : type_(Type::Int), long_value(value)
+ constexpr json(unsigned value) : type_(Type::Int), long_value(value)
{
}
- json(long value) : type_(Type::Int), long_value(value)
+ constexpr json(long value) : type_(Type::Int), long_value(value)
{
}
- json(long long value) : type_(Type::Int), long_value(value)
+ constexpr json(long long value) : type_(Type::Int), long_value(value)
{
}
- json(double value) : type_(Type::Double), double_value(value)
+ constexpr json(double value) : type_(Type::Double), double_value(value)
{
}
@@ -138,37 +138,37 @@ class json
{
}
- Type type() const
+ constexpr Type type() const
{
return type_;
}
- bool is_null() const
+ constexpr bool is_null() const
{
return type_ == Type::Null;
}
- bool is_bool() const
+ constexpr bool is_bool() const
{
return type_ == Type::Bool;
}
- bool is_number() const
+ constexpr bool is_number() const
{
return is_float() || is_double() || is_int();
}
- bool is_int() const
+ constexpr bool is_int() const
{
return type_ == Type::Int;
}
- bool is_float() const
+ constexpr bool is_float() const
{
return type_ == Type::Float;
}
- bool is_double() const
+ constexpr bool is_double() const
{
return type_ == Type::Double;
}

View File

@@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:13:25 -0700
Subject: [PATCH 11/25] Add array and object constructors
---
json.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/json.h b/json.h
index 8b09d001acd0a966328301bc89ff72616f106a4b..9aa5c889932e720746972d9e8ef1dd8b095e0fb1 100644
--- a/json.h
+++ b/json.h
@@ -138,6 +138,14 @@ class json
{
}
+ json(array_t&& value) : type_(Type::Array), array_value(std::move(value))
+ {
+ }
+
+ json(object_t&& value) : type_(Type::Object), object_value(std::move(value))
+ {
+ }
+
constexpr Type type() const
{
return type_;

View File

@@ -0,0 +1,84 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:15:04 -0700
Subject: [PATCH 12/25] Add const getters for string, array, and object
---
json.cpp | 33 +++++++++++++++++++++++++++++++++
json.h | 3 +++
2 files changed, 36 insertions(+)
diff --git a/json.cpp b/json.cpp
index 1266aa36b43155cbab6fe8d918fed2c56596f7a8..4aebebec76de7628792afbf176f46d61e2f67c43 100644
--- a/json.cpp
+++ b/json.cpp
@@ -521,6 +521,17 @@ json::get_string()
}
}
+const std::string&
+json::get_string() const
+{
+ switch (type_) {
+ case Type::String:
+ return string_value;
+ default:
+ ON_LOGIC_ERROR("JSON value is not a string.");
+ }
+}
+
json::array_t&
json::get_array()
{
@@ -532,6 +543,17 @@ json::get_array()
}
}
+const json::array_t&
+json::get_array() const
+{
+ switch (type_) {
+ case Type::Array:
+ return array_value;
+ default:
+ ON_LOGIC_ERROR("JSON value is not an array.");
+ }
+}
+
json::object_t&
json::get_object()
{
@@ -543,6 +565,17 @@ json::get_object()
}
}
+const json::object_t&
+json::get_object() const
+{
+ switch (type_) {
+ case Type::Object:
+ return object_value;
+ default:
+ ON_LOGIC_ERROR("JSON value is not an object.");
+ }
+}
+
void
json::set_array()
{
diff --git a/json.h b/json.h
index 9aa5c889932e720746972d9e8ef1dd8b095e0fb1..2a4c009e4b3cf7f1ccac8b09986853ccd3540f9d 100644
--- a/json.h
+++ b/json.h
@@ -202,8 +202,11 @@ class json
double get_number() const;
long long get_int() const;
std::string& get_string();
+ const std::string& get_string() const;
array_t& get_array();
+ const array_t& get_array() const;
object_t& get_object();
+ const object_t& get_object() const;
bool contains(const std::string&) const;

View File

@@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:20:16 -0700
Subject: [PATCH 13/25] Use wpi StringMap
---
json.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/json.h b/json.h
index 2a4c009e4b3cf7f1ccac8b09986853ccd3540f9d..662a3f2f68a04dc4e7faf5e4bd5a984ffd3705a9 100644
--- a/json.h
+++ b/json.h
@@ -16,17 +16,19 @@
// limitations under the License.
#pragma once
-#include <map>
+
#include <string>
#include <vector>
+#include "wpi/util/StringMap.hpp"
+
namespace wpi::util {
class json
{
public:
using array_t = std::vector<json>;
- using object_t = std::map<std::string, json>;
+ using object_t = wpi::util::StringMap<json>;
enum class Type
{

View File

@@ -0,0 +1,128 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Wed, 1 Apr 2026 08:19:39 -0700
Subject: [PATCH 14/25] Use std::string_view
---
json.cpp | 14 +++++++++-----
json.h | 12 +++++++-----
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/json.cpp b/json.cpp
index 4aebebec76de7628792afbf176f46d61e2f67c43..2b4191865e84f4c160450fa8380ad578d37e1cd1 100644
--- a/json.cpp
+++ b/json.cpp
@@ -282,6 +282,10 @@ json::json(const std::string& value) : type_(Type::String), string_value(value)
{
}
+json::json(std::string_view value) : type_(Type::String), string_value(value)
+{
+}
+
json::~json()
{
if (type_ >= Type::String)
@@ -595,7 +599,7 @@ json::set_object()
}
bool
-json::contains(const std::string& key) const
+json::contains(std::string_view key) const
{
if (!is_object())
return false;
@@ -614,7 +618,7 @@ json::operator[](size_t index)
}
json&
-json::operator[](const std::string& key)
+json::operator[](std::string_view key)
{
if (!is_object())
set_object();
@@ -725,7 +729,7 @@ json::marshal(std::string& b, bool pretty, int indent) const
}
void
-json::stringify(std::string& b, const std::string& s)
+json::stringify(std::string& b, std::string_view s)
{
b += '"';
serialize(b, s);
@@ -733,7 +737,7 @@ json::stringify(std::string& b, const std::string& s)
}
void
-json::serialize(std::string& sb, const std::string& s)
+json::serialize(std::string& sb, std::string_view s)
{
size_t i, j, m;
wint_t x, a, b;
@@ -1255,7 +1259,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
}
std::pair<json::Status, json>
-json::parse(const std::string& s)
+json::parse(std::string_view s)
{
json::Status s2;
std::pair<json::Status, json> res;
diff --git a/json.h b/json.h
index 662a3f2f68a04dc4e7faf5e4bd5a984ffd3705a9..1c0dc26a460e17dab44f13d62059cf6e69f355c9 100644
--- a/json.h
+++ b/json.h
@@ -18,6 +18,7 @@
#pragma once
#include <string>
+#include <string_view>
#include <vector>
#include "wpi/util/StringMap.hpp"
@@ -94,7 +95,7 @@ class json
public:
static const char* StatusToString(Status);
- static std::pair<Status, json> parse(const std::string&);
+ static std::pair<Status, json> parse(std::string_view);
json(const json&);
json(json&&);
@@ -102,6 +103,7 @@ class json
json(unsigned long long);
json(const char*);
json(const std::string&);
+ json(std::string_view);
~json();
constexpr json(const std::nullptr_t = nullptr) : type_(Type::Null)
@@ -210,7 +212,7 @@ class json
object_t& get_object();
const object_t& get_object() const;
- bool contains(const std::string&) const;
+ bool contains(std::string_view) const;
void set_array();
void set_object();
@@ -222,7 +224,7 @@ class json
json& operator=(json&&);
json& operator[](size_t);
- json& operator[](const std::string&);
+ json& operator[](std::string_view);
operator std::string() const
{
@@ -232,8 +234,8 @@ class json
private:
void clear();
void marshal(std::string&, bool, int) const;
- static void stringify(std::string&, const std::string&);
- static void serialize(std::string&, const std::string&);
+ static void stringify(std::string&, std::string_view);
+ static void serialize(std::string&, std::string_view);
static Status parse(json&, const char*&, const char*, int, int);
};

View File

@@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 22:39:28 -0700
Subject: [PATCH 15/25] Change parse to return expected with error string
---
json.cpp | 21 +++++++++++----------
json.h | 7 ++++---
2 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/json.cpp b/json.cpp
index 2b4191865e84f4c160450fa8380ad578d37e1cd1..baefe6317126f95ec75fe9d9a896923d0a90e720 100644
--- a/json.cpp
+++ b/json.cpp
@@ -1258,21 +1258,22 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
return unexpected_eof;
}
-std::pair<json::Status, json>
+wpi::util::expected<json, const char*>
json::parse(std::string_view s)
{
- json::Status s2;
- std::pair<json::Status, json> res;
const char* p = s.data();
const char* e = s.data() + s.size();
- res.first = parse(res.second, p, e, 0, DEPTH);
- if (res.first == json::success) {
- json j2;
- s2 = parse(j2, p, e, 0, DEPTH);
- if (s2 != absent_value)
- res.first = trailing_content;
+ json j;
+ Status result = parse(j, p, e, 0, DEPTH);
+ if (result != success) {
+ return wpi::util::unexpected(StatusToString(result));
}
- return res;
+ json j2;
+ Status s2 = parse(j2, p, e, 0, DEPTH);
+ if (s2 != absent_value) {
+ return wpi::util::unexpected(StatusToString(trailing_content));
+ }
+ return j;
}
const char*
diff --git a/json.h b/json.h
index 1c0dc26a460e17dab44f13d62059cf6e69f355c9..d2962c322678b0dd664707f34151094fec90d281 100644
--- a/json.h
+++ b/json.h
@@ -22,6 +22,7 @@
#include <vector>
#include "wpi/util/StringMap.hpp"
+#include "wpi/util/expected"
namespace wpi::util {
@@ -43,6 +44,7 @@ class json
Object
};
+ private:
enum Status
{
success,
@@ -80,7 +82,6 @@ class json
non_del_c0_control_code_in_string,
};
- private:
Type type_;
union
{
@@ -94,8 +95,7 @@ class json
};
public:
- static const char* StatusToString(Status);
- static std::pair<Status, json> parse(std::string_view);
+ static wpi::util::expected<json, const char*> parse(std::string_view);
json(const json&);
json(json&&);
@@ -232,6 +232,7 @@ class json
}
private:
+ static const char* StatusToString(Status);
void clear();
void marshal(std::string&, bool, int) const;
static void stringify(std::string&, std::string_view);

View File

@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 22:40:25 -0700
Subject: [PATCH 16/25] Add parse_or_throw
---
json.cpp | 10 ++++++++++
json.h | 1 +
2 files changed, 11 insertions(+)
diff --git a/json.cpp b/json.cpp
index baefe6317126f95ec75fe9d9a896923d0a90e720..49fbf92ea6e67e771f051f10b65ac795351c6f3d 100644
--- a/json.cpp
+++ b/json.cpp
@@ -1276,6 +1276,16 @@ json::parse(std::string_view s)
return j;
}
+json
+json::parse_or_throw(std::string_view s)
+{
+ auto result = parse(s);
+ if (!result) {
+ ON_LOGIC_ERROR(result.error());
+ }
+ return *result;
+}
+
const char*
json::StatusToString(json::Status status)
{
diff --git a/json.h b/json.h
index d2962c322678b0dd664707f34151094fec90d281..f42ba9c135cb76a1b0bc31f9e2c03b1ed7dce749 100644
--- a/json.h
+++ b/json.h
@@ -96,6 +96,7 @@ class json
public:
static wpi::util::expected<json, const char*> parse(std::string_view);
+ static json parse_or_throw(std::string_view);
json(const json&);
json(json&&);

View File

@@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 22:54:14 -0700
Subject: [PATCH 17/25] Add operator==
---
json.cpp | 26 ++++++++++++++++++++++++++
json.h | 6 ++++++
2 files changed, 32 insertions(+)
diff --git a/json.cpp b/json.cpp
index 49fbf92ea6e67e771f051f10b65ac795351c6f3d..40479bc266390f015e291b737b5751522dac76ac 100644
--- a/json.cpp
+++ b/json.cpp
@@ -1361,4 +1361,30 @@ json::StatusToString(json::Status status)
}
}
+bool
+operator==(const json& lhs, const json& rhs) {
+ if (lhs.type_ != rhs.type_)
+ return false;
+ switch (lhs.type_) {
+ case json::Type::Null:
+ return true;
+ case json::Type::String:
+ return lhs.string_value == rhs.string_value;
+ case json::Type::Bool:
+ return lhs.bool_value == rhs.bool_value;
+ case json::Type::Int:
+ return lhs.long_value == rhs.long_value;
+ case json::Type::Float:
+ return lhs.float_value == rhs.float_value;
+ case json::Type::Double:
+ return lhs.double_value == rhs.double_value;
+ case json::Type::Array:
+ return lhs.array_value == rhs.array_value;
+ case json::Type::Object:
+ return lhs.object_value == rhs.object_value;
+ default:
+ ON_LOGIC_ERROR("Unhandled JSON type during equality comparison.");
+ }
+}
+
} // namespace wpi::util
diff --git a/json.h b/json.h
index f42ba9c135cb76a1b0bc31f9e2c03b1ed7dce749..fbc3fb959f71f6acc344f26247597959807d23bb 100644
--- a/json.h
+++ b/json.h
@@ -28,6 +28,7 @@ namespace wpi::util {
class json
{
+ friend bool operator==(const json& lhs, const json& rhs);
public:
using array_t = std::vector<json>;
using object_t = wpi::util::StringMap<json>;
@@ -241,4 +242,9 @@ class json
static Status parse(json&, const char*&, const char*, int, int);
};
+bool operator==(const json& lhs, const json& rhs);
+inline bool operator!=(const json& lhs, const json& rhs) {
+ return !(lhs == rhs);
+}
+
} // namespace wpi::util

View File

@@ -0,0 +1,166 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 22:58:18 -0700
Subject: [PATCH 18/25] Add container functions
---
json.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
json.h | 18 ++++++++++
2 files changed, 120 insertions(+)
diff --git a/json.cpp b/json.cpp
index 40479bc266390f015e291b737b5751522dac76ac..2307536b15be9aae19da4ec9370a5aa17d5150d1 100644
--- a/json.cpp
+++ b/json.cpp
@@ -606,6 +606,28 @@ json::contains(std::string_view key) const
return object_value.find(key) != object_value.end();
}
+json*
+json::lookup(std::string_view key)
+{
+ if (!is_object())
+ return nullptr;
+ auto i = object_value.find(key);
+ if (i == object_value.end())
+ return nullptr;
+ return &i->second;
+}
+
+const json*
+json::lookup(std::string_view key) const
+{
+ if (!is_object())
+ return nullptr;
+ auto i = object_value.find(key);
+ if (i == object_value.end())
+ return nullptr;
+ return &i->second;
+}
+
json&
json::operator[](size_t index)
{
@@ -625,6 +647,86 @@ json::operator[](std::string_view key)
return object_value[key];
}
+json&
+json::at(size_t index) {
+ if (!is_array())
+ ON_LOGIC_ERROR("JSON value is not an array.");
+ return array_value.at(index);
+}
+
+json&
+json::at(std::string_view key) {
+ if (!is_object())
+ ON_LOGIC_ERROR("JSON value is not an object.");
+ return object_value.at(key);
+}
+
+const json&
+json::at(size_t index) const {
+ if (!is_array())
+ ON_LOGIC_ERROR("JSON value is not an array.");
+ return array_value.at(index);
+}
+
+const json&
+json::at(std::string_view key) const {
+ if (!is_object())
+ ON_LOGIC_ERROR("JSON value is not an object.");
+ return object_value.at(key);
+}
+
+json
+json::value(size_t index, json&& default_value) {
+ if (!is_array())
+ ON_LOGIC_ERROR("JSON value is not an array.");
+ if (index < array_value.size())
+ return array_value[index];
+ return default_value;
+}
+
+json
+json::value(std::string_view key, json&& default_value)
+{
+ if (!is_object())
+ ON_LOGIC_ERROR("JSON value is not an object.");
+ auto i = object_value.find(key);
+ if (i != object_value.end())
+ return i->second;
+ return default_value;
+}
+
+void
+json::erase(std::string_view key)
+{
+ if (!is_object())
+ ON_LOGIC_ERROR("JSON value is not an object.");
+ object_value.erase(key);
+}
+
+json&
+json::emplace_back(json&& value)
+{
+ if (!is_array())
+ set_array();
+ array_value.emplace_back(std::move(value));
+ return array_value.back();
+}
+
+bool
+json::empty() const
+{
+ switch (type_) {
+ case Type::Null:
+ return true;
+ case Type::Array:
+ return array_value.empty();
+ case Type::Object:
+ return object_value.empty();
+ default:
+ return false;
+ }
+}
+
std::string
json::to_string() const
{
diff --git a/json.h b/json.h
index fbc3fb959f71f6acc344f26247597959807d23bb..62af49cb18b713304ad95e89c52f5cc5bbc4b626 100644
--- a/json.h
+++ b/json.h
@@ -216,6 +216,9 @@ class json
bool contains(std::string_view) const;
+ json* lookup(std::string_view);
+ const json* lookup(std::string_view) const;
+
void set_array();
void set_object();
@@ -228,6 +231,21 @@ class json
json& operator[](size_t);
json& operator[](std::string_view);
+ json& at(size_t);
+ json& at(std::string_view);
+
+ const json& at(size_t) const;
+ const json& at(std::string_view) const;
+
+ json value(size_t, json&&);
+ json value(std::string_view, json&&);
+
+ void erase(std::string_view);
+
+ json& emplace_back(json&& value);
+
+ bool empty() const;
+
operator std::string() const
{
return to_string();

View File

@@ -0,0 +1,106 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 23:01:45 -0700
Subject: [PATCH 19/25] Add to_json, from_json, and json_serializer struct
---
json.h | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/json.h b/json.h
index 62af49cb18b713304ad95e89c52f5cc5bbc4b626..e52435a79ee7537928ba59fb146f635fc8511bfc 100644
--- a/json.h
+++ b/json.h
@@ -26,6 +26,35 @@
namespace wpi::util {
+class json;
+
+template <typename T>
+struct json_serializer;
+
+namespace detail {
+template <typename T>
+concept HasToJson = requires(json& j, const T& val) {
+ { to_json(j, val) };
+};
+
+template <typename T>
+concept HasFromJson = requires(const json& j, T& val) {
+ { from_json(j, val) };
+};
+
+template <typename T>
+concept HasJsonSerializer = requires (json& j, const json& cj, const T& val) {
+ typename json_serializer<typename std::remove_cvref_t<T>>;
+ { json_serializer<typename std::remove_cvref_t<T>>::to(j, val) };
+};
+
+template <typename T>
+concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) {
+ typename json_serializer<typename std::remove_cvref_t<T>>;
+ { json_serializer<typename std::remove_cvref_t<T>>::from(cj) } -> std::convertible_to<typename std::remove_cvref_t<T>>;
+};
+} // namespace detail
+
class json
{
friend bool operator==(const json& lhs, const json& rhs);
@@ -152,6 +181,18 @@ class json
{
}
+ template <detail::HasToJson T>
+ json(const T& value) : type_(Type::Null)
+ {
+ to_json(*this, value);
+ }
+
+ template <detail::HasJsonSerializer T>
+ json(const T& value) : type_(Type::Null)
+ {
+ json_serializer<T>::to(*this, value);
+ }
+
constexpr Type type() const
{
return type_;
@@ -214,6 +255,18 @@ class json
object_t& get_object();
const object_t& get_object() const;
+ template <detail::HasFromJson T>
+ T get() const {
+ T value;
+ from_json(*this, value);
+ return value;
+ }
+
+ template <detail::HasJsonDeserializer T>
+ T get() const {
+ return json_serializer<T>::from(*this);
+ }
+
bool contains(std::string_view) const;
json* lookup(std::string_view);
@@ -228,6 +281,18 @@ class json
json& operator=(const json&);
json& operator=(json&&);
+ template <detail::HasToJson T>
+ json& operator=(const T& value) {
+ to_json(*this, value);
+ return *this;
+ }
+
+ template <detail::HasJsonSerializer T>
+ json& operator=(const T& value) {
+ json_serializer<T>::to(*this, value);
+ return *this;
+ }
+
json& operator[](size_t);
json& operator[](std::string_view);

View File

@@ -0,0 +1,64 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 23:05:58 -0700
Subject: [PATCH 20/25] Add array and object factory functions
---
json.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/json.h b/json.h
index e52435a79ee7537928ba59fb146f635fc8511bfc..6a52615ab3e1799ebcf51a3b5ca445b1e2e64b97 100644
--- a/json.h
+++ b/json.h
@@ -19,6 +19,8 @@
#include <string>
#include <string_view>
+#include <tuple>
+#include <utility>
#include <vector>
#include "wpi/util/StringMap.hpp"
@@ -53,6 +55,13 @@ concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) {
typename json_serializer<typename std::remove_cvref_t<T>>;
{ json_serializer<typename std::remove_cvref_t<T>>::from(cj) } -> std::convertible_to<typename std::remove_cvref_t<T>>;
};
+
+template <typename F, typename Tuple, size_t... I>
+void apply_pairs_helper(F&& f, Tuple&& t, std::index_sequence<I...>) {
+ // This fold expression creates a sequence of calls to f for each pair
+ (f(std::get<I * 2>(std::forward<Tuple>(t)),
+ std::get<I * 2 + 1>(std::forward<Tuple>(t))), ...);
+}
} // namespace detail
class json
@@ -275,6 +284,27 @@ class json
void set_array();
void set_object();
+ template <typename... Args>
+ static json array(Args&&... args) {
+ json j;
+ j.set_array();
+ (j.array_value.emplace_back(std::forward<Args>(args)), ...);
+ return j;
+ }
+
+ template <typename... Args>
+ static json object(Args&&... args) {
+ json j;
+ j.set_object();
+ detail::apply_pairs_helper(
+ [&](auto&& key, auto&& value) {
+ j.object_value[std::forward<decltype(key)>(key)] = std::forward<decltype(value)>(value);
+ },
+ std::forward_as_tuple(args...),
+ std::make_index_sequence<sizeof...(Args) / 2>{});
+ return j;
+ }
+
std::string to_string() const;
std::string to_string_pretty() const;

View File

@@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Fri, 3 Apr 2026 23:07:20 -0700
Subject: [PATCH 21/25] Add array constructor
---
json.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/json.h b/json.h
index 6a52615ab3e1799ebcf51a3b5ca445b1e2e64b97..ef3ea13b7cb147f0b383ff1ead496d495cfece32 100644
--- a/json.h
+++ b/json.h
@@ -17,6 +17,8 @@
#pragma once
+#include <concepts>
+#include <ranges>
#include <string>
#include <string_view>
#include <tuple>
@@ -56,6 +58,19 @@ concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) {
{ json_serializer<typename std::remove_cvref_t<T>>::from(cj) } -> std::convertible_to<typename std::remove_cvref_t<T>>;
};
+template <typename T>
+concept JsonConvertible =
+ HasToJson<T> ||
+ HasJsonSerializer<T> ||
+ std::is_same_v<T, std::nullptr_t> ||
+ std::is_same_v<T, bool> ||
+ std::is_floating_point_v<T> ||
+ std::is_integral_v<T> ||
+ std::convertible_to<T, std::string> ||
+ std::convertible_to<T, std::string_view> ||
+ std::is_same_v<T, std::vector<json>> ||
+ std::is_same_v<T, wpi::util::StringMap<json>>;
+
template <typename F, typename Tuple, size_t... I>
void apply_pairs_helper(F&& f, Tuple&& t, std::index_sequence<I...>) {
// This fold expression creates a sequence of calls to f for each pair
@@ -190,6 +205,12 @@ class json
{
}
+ template <std::ranges::input_range R>
+ requires detail::JsonConvertible<std::ranges::range_value_t<R>>
+ json(const R& range) : type_(Type::Array), array_value(std::ranges::cbegin(range), std::ranges::cend(range))
+ {
+ }
+
template <detail::HasToJson T>
json(const T& value) : type_(Type::Null)
{

View File

@@ -0,0 +1,329 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sat, 4 Apr 2026 12:09:43 -0700
Subject: [PATCH 22/25] Improve stringize and marshal and use raw_ostream
---
json.cpp | 202 ++++++++++++++++++++++++++++++++-----------------------
json.h | 16 ++++-
2 files changed, 131 insertions(+), 87 deletions(-)
diff --git a/json.cpp b/json.cpp
index 2307536b15be9aae19da4ec9370a5aa17d5150d1..1beec7cc30e90ab2e31bc488ee15cedf9a9f32e5 100644
--- a/json.cpp
+++ b/json.cpp
@@ -27,6 +27,7 @@
#include "wpi/double-conversion/double-to-string.h"
#include "wpi/double-conversion/string-to-double.h"
+#include "wpi/util/raw_ostream.hpp"
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wtype-limits"
@@ -731,7 +732,8 @@ std::string
json::to_string() const
{
std::string b;
- marshal(b, false, 0);
+ wpi::util::raw_string_ostream os(b);
+ marshal(os, false, 0);
return b;
}
@@ -739,107 +741,139 @@ std::string
json::to_string_pretty() const
{
std::string b;
- marshal(b, true, 0);
+ wpi::util::raw_string_ostream os(b);
+ marshal(os, true, 0);
return b;
}
void
-json::marshal(std::string& b, bool pretty, int indent) const
+json::marshal(wpi::util::raw_ostream& os, bool pretty, int indent) const
{
switch (type_) {
case Type::Null:
- b += "null";
+ stringify_null(os);
break;
case Type::String:
- stringify(b, string_value);
+ stringify_string(os, string_value);
break;
case Type::Bool:
- b += bool_value ? "true" : "false";
+ stringify_bool(os, bool_value);
break;
- case Type::Int: {
- char buf[64];
- b.append(buf, LongToString(buf, long_value) - buf);
+ case Type::Int:
+ stringify_int(os, long_value);
break;
- }
- case Type::Float: {
- char buf[128];
- double_conversion::StringBuilder db(buf, 128);
- kDoubleToJson.ToShortestSingle(float_value, &db);
- db.Finalize();
- b += buf;
+ case Type::Float:
+ stringify_float(os, float_value);
break;
- }
- case Type::Double: {
- char buf[128];
- double_conversion::StringBuilder db(buf, 128);
- kDoubleToJson.ToShortest(double_value, &db);
- db.Finalize();
- b += buf;
+ case Type::Double:
+ stringify_double(os, double_value);
break;
- }
- case Type::Array: {
- bool once = false;
- b += '[';
- for (auto i = array_value.begin(); i != array_value.end(); ++i) {
- if (once) {
- b += ',';
- if (pretty)
- b += ' ';
- } else {
- once = true;
- }
- i->marshal(b, pretty, indent);
- }
- b += ']';
+ case Type::Array:
+ stringify_array(os, array_value, pretty, indent);
break;
- }
- case Type::Object: {
- bool once = false;
- b += '{';
- for (auto i = object_value.begin(); i != object_value.end(); ++i) {
- if (once) {
- b += ',';
- } else {
- once = true;
- }
- if (pretty && object_value.size() > 1) {
- b += '\n';
- ++indent;
- for (int j = 0; j < indent; ++j)
- b += " ";
- }
- stringify(b, i->first);
- b += ':';
- if (pretty)
- b += ' ';
- i->second.marshal(b, pretty, indent);
- if (pretty && object_value.size() > 1)
- --indent;
- }
- if (pretty && object_value.size() > 1) {
- b += '\n';
- for (int j = 0; j < indent; ++j)
- b += " ";
- ++indent;
- }
- b += '}';
+ case Type::Object:
+ stringify_object(os, object_value, pretty, indent);
break;
- }
default:
ON_LOGIC_ERROR("Unhandled JSON type.");
}
}
void
-json::stringify(std::string& b, std::string_view s)
+json::stringify_null(wpi::util::raw_ostream& os)
{
- b += '"';
- serialize(b, s);
- b += '"';
+ os << "null";
+}
+
+void
+json::stringify_string(wpi::util::raw_ostream& os, std::string_view s)
+{
+ os << '"';
+ serialize(os, s);
+ os << '"';
+}
+
+void
+json::stringify_bool(wpi::util::raw_ostream& os, bool value) {
+ os << (value ? "true" : "false");
+}
+
+void
+json::stringify_float(wpi::util::raw_ostream& os, float value) {
+ char buf[128];
+ double_conversion::StringBuilder db(buf, 128);
+ kDoubleToJson.ToShortestSingle(value, &db);
+ db.Finalize();
+ os << buf;
+}
+
+void
+json::stringify_double(wpi::util::raw_ostream& os, double value) {
+ char buf[128];
+ double_conversion::StringBuilder db(buf, 128);
+ kDoubleToJson.ToShortest(value, &db);
+ db.Finalize();
+ os << buf;
+}
+
+void
+json::stringify_int(wpi::util::raw_ostream& os, long long value) {
+ char buf[64];
+ os.write(buf, LongToString(buf, value) - buf);
+}
+
+void
+json::stringify_array(wpi::util::raw_ostream& os, const array_t& value, bool pretty, int indent) {
+ bool once = false;
+ os << '[';
+ for (auto i = value.begin(); i != value.end(); ++i) {
+ if (once) {
+ os << ',';
+ if (pretty)
+ os << ' ';
+ } else {
+ once = true;
+ }
+ i->marshal(os, pretty, indent);
+ }
+ os << ']';
+}
+
+void
+json::stringify_object(wpi::util::raw_ostream& os, const object_t& value, bool pretty, int indent) {
+ bool once = false;
+ os << '{';
+ for (auto i = value.begin(); i != value.end(); ++i) {
+ if (once) {
+ os << ',';
+ } else {
+ once = true;
+ }
+ if (pretty && value.size() > 1) {
+ os << '\n';
+ ++indent;
+ for (int j = 0; j < indent; ++j)
+ os << " ";
+ }
+ stringify_string(os, i->first);
+ os << ':';
+ if (pretty)
+ os << ' ';
+ i->second.marshal(os, pretty, indent);
+ if (pretty && value.size() > 1)
+ --indent;
+ }
+ if (pretty && value.size() > 1) {
+ os << '\n';
+ for (int j = 0; j < indent; ++j)
+ os << " ";
+ ++indent;
+ }
+ os << '}';
}
void
-json::serialize(std::string& sb, std::string_view s)
+json::serialize(wpi::util::raw_ostream& os, std::string_view s)
{
size_t i, j, m;
wint_t x, a, b;
@@ -865,28 +899,28 @@ json::serialize(std::string& sb, std::string_view s)
}
switch (0 <= x && x <= 127 ? kEscapeLiteral[x] : 9) {
case 0:
- sb += x;
+ os << static_cast<char>(x);
break;
case 1:
- sb += "\\t";
+ os << "\\t";
break;
case 2:
- sb += "\\n";
+ os << "\\n";
break;
case 3:
- sb += "\\r";
+ os << "\\r";
break;
case 4:
- sb += "\\f";
+ os << "\\f";
break;
case 5:
- sb += "\\\\";
+ os << "\\\\";
break;
case 6:
- sb += "\\/";
+ os << "\\/";
break;
case 7:
- sb += "\\\"";
+ os << "\\\"";
break;
case 9:
w = EncodeUtf16(x);
@@ -898,7 +932,7 @@ json::serialize(std::string& sb, std::string_view s)
esc[3] = "0123456789abcdef"[(w & 0x0F00) >> 010];
esc[4] = "0123456789abcdef"[(w & 0x00F0) >> 004];
esc[5] = "0123456789abcdef"[(w & 0x000F) >> 000];
- sb.append(esc, 6);
+ os.write(esc, 6);
} while ((w >>= 16));
break;
default:
diff --git a/json.h b/json.h
index ef3ea13b7cb147f0b383ff1ead496d495cfece32..e42ee96ac8697eaf17fd9d5298e877a45a4644a0 100644
--- a/json.h
+++ b/json.h
@@ -31,6 +31,7 @@
namespace wpi::util {
class json;
+class raw_ostream;
template <typename T>
struct json_serializer;
@@ -367,12 +368,21 @@ class json
return to_string();
}
+ void marshal(wpi::util::raw_ostream& os, bool pretty = false, int indent = 0) const;
+
+ static void stringify_null(wpi::util::raw_ostream&);
+ static void stringify_string(wpi::util::raw_ostream&, std::string_view);
+ static void stringify_bool(wpi::util::raw_ostream&, bool);
+ static void stringify_float(wpi::util::raw_ostream&, float);
+ static void stringify_double(wpi::util::raw_ostream&, double);
+ static void stringify_int(wpi::util::raw_ostream&, long long);
+ static void stringify_array(wpi::util::raw_ostream&, const array_t&, bool pretty = false, int indent = 0);
+ static void stringify_object(wpi::util::raw_ostream&, const object_t&, bool pretty = false, int indent = 0);
+
private:
static const char* StatusToString(Status);
void clear();
- void marshal(std::string&, bool, int) const;
- static void stringify(std::string&, std::string_view);
- static void serialize(std::string&, std::string_view);
+ static void serialize(wpi::util::raw_ostream&, std::string_view);
static Status parse(json&, const char*&, const char*, int, int);
};

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sat, 4 Apr 2026 12:24:46 -0700
Subject: [PATCH 23/25] Update include path
---
json.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/json.cpp b/json.cpp
index 1beec7cc30e90ab2e31bc488ee15cedf9a9f32e5..f8416f3723df70032d274f32cda36b4ef1c7ed90 100644
--- a/json.cpp
+++ b/json.cpp
@@ -15,7 +15,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "json.h"
+#include "wpi/util/json.hpp"
#include "jtckdint.h"
#include <cassert>

View File

@@ -0,0 +1,326 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Sun, 5 Apr 2026 17:19:33 -0700
Subject: [PATCH 24/25] Add support for unsigned long long values
Make unsigned constructors constexpr
---
json.cpp | 115 ++++++++++++++++++++++++++++++++++++++-----------------
json.h | 36 +++++++++++++++--
2 files changed, 112 insertions(+), 39 deletions(-)
diff --git a/json.cpp b/json.cpp
index f8416f3723df70032d274f32cda36b4ef1c7ed90..7f23ece0e8fa09ccb5fef8226de39c55ef70efdd 100644
--- a/json.cpp
+++ b/json.cpp
@@ -247,28 +247,6 @@ LongToString(char* p, long long x)
return UlongToString(p, x);
}
-json::json(unsigned long value)
-{
- if (value <= LLONG_MAX) {
- type_ = Type::Int;
- long_value = value;
- } else {
- type_ = Type::Double;
- double_value = value;
- }
-}
-
-json::json(unsigned long long value)
-{
- if (value <= LLONG_MAX) {
- type_ = Type::Int;
- long_value = value;
- } else {
- type_ = Type::Double;
- double_value = value;
- }
-}
-
json::json(const char* value)
{
if (value) {
@@ -323,6 +301,9 @@ json::json(const json& other) : type_(other.type_)
case Type::Int:
long_value = other.long_value;
break;
+ case Type::Uint:
+ ulong_value = other.ulong_value;
+ break;
case Type::Float:
float_value = other.float_value;
break;
@@ -359,6 +340,9 @@ json::operator=(const json& other)
case Type::Int:
long_value = other.long_value;
break;
+ case Type::Uint:
+ ulong_value = other.ulong_value;
+ break;
case Type::Float:
float_value = other.float_value;
break;
@@ -392,6 +376,9 @@ json::json(json&& other) : type_(other.type_)
case Type::Int:
long_value = other.long_value;
break;
+ case Type::Uint:
+ ulong_value = other.ulong_value;
+ break;
case Type::Float:
float_value = other.float_value;
break;
@@ -429,6 +416,9 @@ json::operator=(json&& other)
case Type::Int:
long_value = other.long_value;
break;
+ case Type::Uint:
+ ulong_value = other.ulong_value;
+ break;
case Type::Float:
float_value = other.float_value;
break;
@@ -458,6 +448,8 @@ json::get_number() const
switch (type_) {
case Type::Int:
return long_value;
+ case Type::Uint:
+ return ulong_value;
case Type::Float:
return float_value;
case Type::Double:
@@ -478,6 +470,17 @@ json::get_int() const
}
}
+unsigned long long
+json::get_uint() const
+{
+ switch (type_) {
+ case Type::Uint:
+ return ulong_value;
+ default:
+ ON_LOGIC_ERROR("JSON value is not an unsigned long.");
+ }
+}
+
bool
json::get_bool() const
{
@@ -762,6 +765,9 @@ json::marshal(wpi::util::raw_ostream& os, bool pretty, int indent) const
case Type::Int:
stringify_int(os, long_value);
break;
+ case Type::Uint:
+ stringify_uint(os, ulong_value);
+ break;
case Type::Float:
stringify_float(os, float_value);
break;
@@ -822,6 +828,12 @@ json::stringify_int(wpi::util::raw_ostream& os, long long value) {
os.write(buf, LongToString(buf, value) - buf);
}
+void
+json::stringify_uint(wpi::util::raw_ostream& os, unsigned long long value) {
+ char buf[64];
+ os.write(buf, UlongToString(buf, value) - buf);
+}
+
void
json::stringify_array(wpi::util::raw_ostream& os, const array_t& value, bool pretty, int indent) {
bool once = false;
@@ -946,6 +958,7 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
{
char w[4];
long long x;
+ unsigned long long ux;
const char* a;
int A, B, C, D, c, d, i, u;
if (!depth)
@@ -1061,25 +1074,53 @@ json::parse(json& j, const char*& p, const char* e, int context, int depth)
case '9': // integer
if (context & (COLON | COMMA | KEY))
goto OnColonCommaKey;
- for (x = (c - '0') * d; p < e; ++p) {
- c = *p & 255;
- if (isdigit(c)) {
- if (ckd_mul(&x, x, 10) ||
- ckd_add(&x, x, (c - '0') * d)) {
+ if (d > 0) {
+ for (ux = c - '0'; p < e; ++p) {
+ c = *p & 255;
+ if (isdigit(c)) {
+ if (ckd_mul(&ux, ux, 10u) ||
+ ckd_add(&ux, ux,
+ static_cast<unsigned long long>(c - '0'))) {
+ goto UseDubble;
+ }
+ } else if (c == '.') {
+ if (p + 1 == e || !isdigit(p[1]))
+ return bad_double;
+ goto UseDubble;
+ } else if (c == 'e' || c == 'E') {
goto UseDubble;
+ } else {
+ break;
}
- } else if (c == '.') {
- if (p + 1 == e || !isdigit(p[1]))
- return bad_double;
- goto UseDubble;
- } else if (c == 'e' || c == 'E') {
- goto UseDubble;
+ }
+ if (ux <= static_cast<unsigned long long>(LLONG_MAX)) {
+ j.type_ = Type::Int;
+ j.long_value = ux;
} else {
- break;
+ j.type_ = Type::Uint;
+ j.ulong_value = ux;
+ }
+ } else {
+ for (x = (c - '0') * d; p < e; ++p) {
+ c = *p & 255;
+ if (isdigit(c)) {
+ if (ckd_mul(&x, x, 10) ||
+ ckd_add(&x, x, (c - '0') * d)) {
+ goto UseDubble;
+ }
+ } else if (c == '.') {
+ if (p + 1 == e || !isdigit(p[1]))
+ return bad_double;
+ goto UseDubble;
+ } else if (c == 'e' || c == 'E') {
+ goto UseDubble;
+ } else {
+ break;
+ }
}
+ j.type_ = Type::Int;
+ j.long_value = x;
}
- j.type_ = Type::Int;
- j.long_value = x;
return success;
UseDubble: // number
@@ -1510,6 +1551,8 @@ operator==(const json& lhs, const json& rhs) {
return lhs.bool_value == rhs.bool_value;
case json::Type::Int:
return lhs.long_value == rhs.long_value;
+ case json::Type::Uint:
+ return lhs.ulong_value == rhs.ulong_value;
case json::Type::Float:
return lhs.float_value == rhs.float_value;
case json::Type::Double:
diff --git a/json.h b/json.h
index e42ee96ac8697eaf17fd9d5298e877a45a4644a0..31c5ea39b67286df158a9d6e13ab5073498126b8 100644
--- a/json.h
+++ b/json.h
@@ -17,6 +17,7 @@
#pragma once
+#include <climits>
#include <concepts>
#include <ranges>
#include <string>
@@ -92,6 +93,7 @@ class json
Null,
Bool,
Int,
+ Uint,
Float,
Double,
String,
@@ -144,6 +146,7 @@ class json
float float_value;
double double_value;
long long long_value;
+ unsigned long long ulong_value;
std::string string_value;
array_t array_value;
object_t object_value;
@@ -155,8 +158,6 @@ class json
json(const json&);
json(json&&);
- json(unsigned long);
- json(unsigned long long);
json(const char*);
json(const std::string&);
json(std::string_view);
@@ -186,10 +187,32 @@ class json
{
}
+ constexpr json(unsigned long value)
+ {
+ if (value <= LLONG_MAX) {
+ type_ = Type::Int;
+ long_value = value;
+ } else {
+ type_ = Type::Uint;
+ ulong_value = value;
+ }
+ }
+
constexpr json(long long value) : type_(Type::Int), long_value(value)
{
}
+ constexpr json(unsigned long long value)
+ {
+ if (value <= LLONG_MAX) {
+ type_ = Type::Int;
+ long_value = value;
+ } else {
+ type_ = Type::Uint;
+ ulong_value = value;
+ }
+ }
+
constexpr json(double value) : type_(Type::Double), double_value(value)
{
}
@@ -241,7 +264,7 @@ class json
constexpr bool is_number() const
{
- return is_float() || is_double() || is_int();
+ return is_float() || is_double() || is_int() || is_uint();
}
constexpr bool is_int() const
@@ -249,6 +272,11 @@ class json
return type_ == Type::Int;
}
+ constexpr bool is_uint() const
+ {
+ return type_ == Type::Uint;
+ }
+
constexpr bool is_float() const
{
return type_ == Type::Float;
@@ -279,6 +307,7 @@ class json
double get_double() const;
double get_number() const;
long long get_int() const;
+ unsigned long long get_uint() const;
std::string& get_string();
const std::string& get_string() const;
array_t& get_array();
@@ -376,6 +405,7 @@ class json
static void stringify_float(wpi::util::raw_ostream&, float);
static void stringify_double(wpi::util::raw_ostream&, double);
static void stringify_int(wpi::util::raw_ostream&, long long);
+ static void stringify_uint(wpi::util::raw_ostream&, unsigned long long);
static void stringify_array(wpi::util::raw_ostream&, const array_t&, bool pretty = false, int indent = 0);
static void stringify_object(wpi::util::raw_ostream&, const object_t&, bool pretty = false, int indent = 0);

View File

@@ -0,0 +1,22 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Peter Johnson <johnson.peter@gmail.com>
Date: Mon, 6 Apr 2026 17:49:08 -0700
Subject: [PATCH 25/25] Make bool constructor safe
---
json.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/json.h b/json.h
index 31c5ea39b67286df158a9d6e13ab5073498126b8..2746badf92a5bb4de6c38007d8e6dcfbed5b47eb 100644
--- a/json.h
+++ b/json.h
@@ -167,7 +167,7 @@ class json
{
}
- constexpr json(bool value) : type_(Type::Bool), bool_value(value)
+ constexpr json(std::same_as<bool> auto value) : type_(Type::Bool), bool_value(value)
{
}