[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

@@ -19,6 +19,7 @@ Google Test thirdparty/googletest/include
thirdparty/googletest/src
LLVM wpiutil/src/main/native/thirdparty/llvm
wpiutil/src/test/native/cpp/llvm/
JSON for Classic C++ wpiutil/src/main/native/thirdparty/json
libuv wpinet/src/main/native/thirdparty/libuv/
fmtlib wpiutil/src/main/native/thirdparty/fmtlib/
sigslot wpiutil/src/main/native/thirdparty/sigslot
@@ -368,6 +369,24 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
============================
JSON for Classic C++ License
============================
Copyright 2024 Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================
libuv License
==============================================================================

54
upstream_utils/json.py Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
import shutil
from pathlib import Path
from upstream_utils import Lib
def copy_upstream_src(wpilib_root: Path):
wpiutil = wpilib_root / "wpiutil"
# Delete old install
for d in [
"src/main/native/thirdparty/json/cpp",
"src/main/native/thirdparty/json/include",
]:
shutil.rmtree(wpiutil / d, ignore_errors=True)
# Create lists of source and destination files
src_include_files = ["json.h"]
src_src_files = ["json.cpp", "jtckdint.h"]
wpiutil_json_root = wpiutil / "src/main/native/thirdparty/json/include/wpi/util"
dest_include_files = [wpiutil_json_root / "json.hpp"]
# Copy json header files into allwpilib
for i in range(len(src_include_files)):
dest_dir = dest_include_files[i].parent
if not dest_dir.exists():
dest_dir.mkdir(parents=True)
shutil.copyfile(src_include_files[i], dest_include_files[i])
# Copy json src files into allwpilib
for i in range(len(src_src_files)):
dest_dir = wpiutil / "src/main/native/thirdparty/json/src"
if not dest_dir.exists():
dest_dir.mkdir(parents=True)
shutil.copyfile(src_src_files[i], dest_dir / src_src_files[i])
def main():
name = "json"
url = "https://github.com/jart/json.cpp.git"
tag = "6ec4e44a5bbaadbe677473378c3d2133644c58a1"
patch_options = {
"use_threeway": True,
}
json = Lib(name, url, tag, copy_upstream_src, patch_options)
json.main()
if __name__ == "__main__":
main()

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)
{
}

View File

@@ -122,6 +122,7 @@ third_party_cc_lib_helper(
third_party_cc_lib_helper(
name = "json",
include_root = "src/main/native/thirdparty/json/include",
src_root = "src/main/native/thirdparty/json/src",
visibility = ["//visibility:public"],
)
@@ -189,13 +190,13 @@ wpilib_cc_library(
third_party_header_only_libraries = [
":argparse",
":expected",
":json",
":sigslot",
],
third_party_libraries = [
":debugging",
":double-conversion",
":fmtlib",
":json",
":llvm",
":mpack",
":nanopb",

View File

@@ -114,7 +114,7 @@ file(
src/main/native/cpp/*.cpp
src/main/native/thirdparty/debugging/src/*.cpp
src/main/native/thirdparty/double-conversion/src/*.cpp
src/main/native/thirdparty/json/cpp/*.cpp
src/main/native/thirdparty/json/src/*.cpp
src/main/native/thirdparty/llvm/cpp/*.cpp
src/main/native/thirdparty/mpack/src/*.cpp
src/main/native/thirdparty/nanopb/src/*.cpp

View File

@@ -44,11 +44,11 @@ ext {
}
jsonCpp(CppSourceSet) {
source {
srcDirs 'src/main/native/thirdparty/json/cpp'
include '*.cpp'
srcDirs 'src/main/native/thirdparty/json/src'
include '**/*.cpp'
}
exportedHeaders {
srcDirs 'src/main/native/thirdparty/json/include'
srcDirs 'src/main/native/include', 'src/main/native/thirdparty/double-conversion/include', 'src/main/native/thirdparty/expected/include', 'src/main/native/thirdparty/llvm/include', 'src/main/native/thirdparty/json/include'
}
}
llvmCpp(CppSourceSet) {
@@ -210,7 +210,7 @@ cppSourcesZip {
from('src/main/native/thirdparty/fmtlib/src') {
into '/'
}
from('src/main/native/thirdparty/json/cpp') {
from('src/main/native/thirdparty/json/src') {
into '/'
}
from('src/main/native/thirdparty/llvm/cpp') {

View File

@@ -0,0 +1,424 @@
// -*- mode:c++;indent-tabs-mode:nil;c-basic-offset:4;coding:utf-8 -*-
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
//
// Copyright 2024 Mozilla Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <climits>
#include <concepts>
#include <ranges>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>
#include "wpi/util/StringMap.hpp"
#include "wpi/util/expected"
namespace wpi::util {
class json;
class raw_ostream;
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>>;
};
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
(f(std::get<I * 2>(std::forward<Tuple>(t)),
std::get<I * 2 + 1>(std::forward<Tuple>(t))), ...);
}
} // namespace detail
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>;
enum class Type
{
Null,
Bool,
Int,
Uint,
Float,
Double,
String,
Array,
Object
};
private:
enum Status
{
success,
bad_double,
absent_value,
bad_negative,
bad_exponent,
missing_comma,
missing_colon,
malformed_utf8,
depth_exceeded,
stack_overflow,
unexpected_eof,
overlong_ascii,
unexpected_comma,
unexpected_colon,
unexpected_octal,
trailing_content,
illegal_character,
invalid_hex_escape,
overlong_utf8_0x7ff,
overlong_utf8_0xffff,
object_missing_value,
illegal_utf8_character,
invalid_unicode_escape,
utf16_surrogate_in_utf8,
unexpected_end_of_array,
hex_escape_not_printable,
invalid_escape_character,
utf8_exceeds_utf16_range,
unexpected_end_of_string,
unexpected_end_of_object,
object_key_must_be_string,
c1_control_code_in_string,
non_del_c0_control_code_in_string,
};
Type type_;
union
{
bool bool_value;
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;
};
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&&);
json(const char*);
json(const std::string&);
json(std::string_view);
~json();
constexpr json(const std::nullptr_t = nullptr) : type_(Type::Null)
{
}
constexpr json(std::same_as<bool> auto value) : type_(Type::Bool), bool_value(value)
{
}
constexpr json(int value) : type_(Type::Int), long_value(value)
{
}
constexpr json(float value) : type_(Type::Float), float_value(value)
{
}
constexpr json(unsigned value) : type_(Type::Int), long_value(value)
{
}
constexpr json(long value) : type_(Type::Int), long_value(value)
{
}
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)
{
}
json(std::string&& value) : type_(Type::String), string_value(std::move(value))
{
}
json(array_t&& value) : type_(Type::Array), array_value(std::move(value))
{
}
json(object_t&& value) : type_(Type::Object), object_value(std::move(value))
{
}
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)
{
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_;
}
constexpr bool is_null() const
{
return type_ == Type::Null;
}
constexpr bool is_bool() const
{
return type_ == Type::Bool;
}
constexpr bool is_number() const
{
return is_float() || is_double() || is_int() || is_uint();
}
constexpr bool is_int() const
{
return type_ == Type::Int;
}
constexpr bool is_uint() const
{
return type_ == Type::Uint;
}
constexpr bool is_float() const
{
return type_ == Type::Float;
}
constexpr bool is_double() const
{
return type_ == Type::Double;
}
bool is_string() const
{
return type_ == Type::String;
}
bool is_array() const
{
return type_ == Type::Array;
}
bool is_object() const
{
return type_ == Type::Object;
}
bool get_bool() const;
float get_float() const;
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();
const array_t& get_array() const;
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);
const json* lookup(std::string_view) const;
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;
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);
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();
}
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_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);
private:
static const char* StatusToString(Status);
void clear();
static void serialize(wpi::util::raw_ostream&, std::string_view);
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,670 @@
/*
* Copyright 2023 Justine Alexandra Roberts Tunney
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @fileoverview C23 Checked Arithmetic
*
* This header defines three type generic functions:
*
* - `bool ckd_add(res, a, b)`
* - `bool ckd_sub(res, a, b)`
* - `bool ckd_mul(res, a, b)`
*
* Which allow integer arithmetic errors to be detected. There are many
* kinds of integer errors, e.g. overflow, truncation, etc. These funcs
* catch them all. Here's an example of how it works:
*
* uint32_t c;
* int32_t a = 0x7fffffff;
* int32_t b = 2;
* assert(!ckd_add(&c, a, b));
* assert(c == 0x80000001u);
*
* Experienced C / C++ users should find this example counter-intuitive
* because the expression `0x7fffffff + 2` not only overflows it's also
* undefined behavior. However here we see it's specified, and does not
* result in an error. That's because C23 checked arithmetic is not the
* arithmetic you're used to. The new standard changes the mathematics.
*
* C23 checked arithmetic is defined as performing the arithmetic using
* infinite precision and then checking if the resulting value will fit
* in the output type. Our example above did not result in an error due
* to `0x80000001` being a legal value for `uint32_t`.
*
* This implementation will use the GNU compiler builtins, when they're
* available, only if you don't use build flags like `-std=c11` because
* they define `__STRICT_ANSI__` and GCC extensions aren't really ANSI.
* Instead, you'll get a pretty good pure C11 and C++11 implementation.
*
* @see https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf
* @version 1.0 (2024-12-07)
*/
#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
#define __ckd_has_include(x) 0
#endif
#if __ckd_has_include(<stdckdint.h>) && !defined(__cplusplus)
#include <stdckdint.h>
#else
#define __STDC_VERSION_STDCKDINT_H__ 202311L
#if (!defined(__STRICT_ANSI__) && defined(__SIZEOF_INT128__))
#define __ckd_have_int128
#define __ckd_intmax __int128
#elif ((defined(__cplusplus) && __cplusplus >= 201103L) || \
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L))
#define __ckd_intmax long long
#else
#define __ckd_intmax long
#endif
typedef signed __ckd_intmax __ckd_intmax_t;
typedef unsigned __ckd_intmax __ckd_uintmax_t;
#ifdef __has_builtin
#define __ckd_has_builtin(x) __has_builtin(x)
#else
#define __ckd_has_builtin(x) 0
#endif
#if (!defined(__STRICT_ANSI__) && \
((defined(__GNUC__) && __GNUC__ >= 5 && !defined(__ICC)) || \
(__ckd_has_builtin(__builtin_add_overflow) && \
__ckd_has_builtin(__builtin_sub_overflow) && \
__ckd_has_builtin(__builtin_mul_overflow))))
#define ckd_add(res, x, y) __builtin_add_overflow((x), (y), (res))
#define ckd_sub(res, x, y) __builtin_sub_overflow((x), (y), (res))
#define ckd_mul(res, x, y) __builtin_mul_overflow((x), (y), (res))
#elif (defined(__cplusplus) && \
(__cplusplus >= 201103L || \
(defined(_MSC_VER) && __cplusplus >= 199711L && \
__ckd_has_include(<type_traits>) && __ckd_has_include(<limits>))))
#include <limits>
#include <type_traits>
template<typename __T, typename __U, typename __V>
inline bool
ckd_add(__T* __res, __U __a, __V __b)
{
static_assert(std::is_integral<__T>::value &&
std::is_integral<__U>::value &&
std::is_integral<__V>::value,
"non-integral types not allowed");
static_assert(!std::is_same<__T, bool>::value &&
!std::is_same<__U, bool>::value &&
!std::is_same<__V, bool>::value,
"checked booleans not supported");
static_assert(!std::is_same<__T, char>::value &&
!std::is_same<__U, char>::value &&
!std::is_same<__V, char>::value,
"unqualified char type is ambiguous");
__ckd_uintmax_t __x = __a;
__ckd_uintmax_t __y = __b;
__ckd_uintmax_t __z = __x + __y;
*__res = __z;
if (sizeof(__z) > sizeof(__U) && sizeof(__z) > sizeof(__V)) {
if (sizeof(__z) > sizeof(__T) || std::is_signed<__T>::value) {
return static_cast<__ckd_intmax_t>(__z) != static_cast<__T>(__z);
} else if (!std::is_same<__T, __ckd_uintmax_t>::value) {
return (
__z != static_cast<__T>(__z) ||
((std::is_signed<__U>::value || std::is_signed<__V>::value) &&
static_cast<__ckd_intmax_t>(__z) < 0));
}
}
bool __truncated = false;
if (sizeof(__T) < sizeof(__ckd_intmax_t)) {
__truncated =
__z != static_cast<__ckd_uintmax_t>(static_cast<__T>(__z));
}
switch (std::is_signed<__T>::value << 2 | //
std::is_signed<__U>::value << 1 | //
std::is_signed<__V>::value) {
case 0: // u = u + u
return __truncated | (__z < __x);
case 1: // u = u + s
__y ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated |
(static_cast<__ckd_intmax_t>((__z ^ __x) & (__z ^ __y)) < 0);
case 2: // u = s + u
__x ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated |
(static_cast<__ckd_intmax_t>((__z ^ __x) & (__z ^ __y)) < 0);
case 3: // u = s + s
return __truncated |
(static_cast<__ckd_intmax_t>(((__z | __x) & __y) |
((__z & __x) & ~__y)) < 0);
case 4: // s = u + u
return __truncated | (__z < __x) |
(static_cast<__ckd_intmax_t>(__z) < 0);
case 5: // s = u + s
__y ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated | (__x + __y < __y);
case 6: // s = s + u
__x ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated | (__x + __y < __x);
case 7: // s = s + s
return __truncated |
(static_cast<__ckd_intmax_t>((__z ^ __x) & (__z ^ __y)) < 0);
default:
for (;;)
(void)0;
}
}
template<typename __T, typename __U, typename __V>
inline bool
ckd_sub(__T* __res, __U __a, __V __b)
{
static_assert(std::is_integral<__T>::value &&
std::is_integral<__U>::value &&
std::is_integral<__V>::value,
"non-integral types not allowed");
static_assert(!std::is_same<__T, bool>::value &&
!std::is_same<__U, bool>::value &&
!std::is_same<__V, bool>::value,
"checked booleans not supported");
static_assert(!std::is_same<__T, char>::value &&
!std::is_same<__U, char>::value &&
!std::is_same<__V, char>::value,
"unqualified char type is ambiguous");
__ckd_uintmax_t __x = __a;
__ckd_uintmax_t __y = __b;
__ckd_uintmax_t __z = __x - __y;
*__res = __z;
bool __truncated = false;
if (sizeof(__T) < sizeof(__ckd_intmax_t)) {
__truncated =
__z != static_cast<__ckd_uintmax_t>(static_cast<__T>(__z));
}
switch (std::is_signed<__T>::value << 2 | //
std::is_signed<__U>::value << 1 | //
std::is_signed<__V>::value) {
case 0: // u = u - u
return __truncated | (__x < __y);
case 1: // u = u - s
__y ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated |
(static_cast<__ckd_intmax_t>((__x ^ __y) & (__z ^ __x)) < 0);
case 2: // u = s - u
return __truncated | (__y > __x) |
(static_cast<__ckd_intmax_t>(__x) < 0);
case 3: // u = s - s
return __truncated |
(static_cast<__ckd_intmax_t>(((__z & __x) & __y) |
((__z | __x) & ~__y)) < 0);
case 4: // s = u - u
return __truncated |
((__x < __y) ^ (static_cast<__ckd_intmax_t>(__z) < 0));
case 5: // s = u - s
__y ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated | (__x >= __y);
case 6: // s = s - u
__x ^= std::numeric_limits<__ckd_intmax_t>::min();
return __truncated | (__x < __y);
case 7: // s = s - s
return __truncated |
(static_cast<__ckd_intmax_t>((__x ^ __y) & (__z ^ __x)) < 0);
default:
for (;;)
(void)0;
}
}
template<typename __T, typename __U, typename __V>
inline bool
ckd_mul(__T* __res, __U __a, __V __b)
{
static_assert(std::is_integral<__T>::value &&
std::is_integral<__U>::value &&
std::is_integral<__V>::value,
"non-integral types not allowed");
static_assert(!std::is_same<__T, bool>::value &&
!std::is_same<__U, bool>::value &&
!std::is_same<__V, bool>::value,
"checked booleans not supported");
static_assert(!std::is_same<__T, char>::value &&
!std::is_same<__U, char>::value &&
!std::is_same<__V, char>::value,
"unqualified char type is ambiguous");
__ckd_uintmax_t __x = __a;
__ckd_uintmax_t __y = __b;
if ((sizeof(__U) * 8 - std::is_signed<__U>::value) +
(sizeof(__V) * 8 - std::is_signed<__V>::value) <=
(sizeof(__T) * 8 - std::is_signed<__T>::value)) {
if (sizeof(__ckd_uintmax_t) > sizeof(__T) ||
std::is_signed<__T>::value) {
__ckd_intmax_t __z = __x * __y;
return __z != (*__res = __z);
} else if (!std::is_same<__T, __ckd_uintmax_t>::value) {
__ckd_uintmax_t __z = __x * __y;
*__res = __z;
return (
__z != static_cast<__T>(__z) ||
((std::is_signed<__U>::value || std::is_signed<__V>::value) &&
static_cast<__ckd_intmax_t>(__z) < 0));
}
}
switch (std::is_signed<__T>::value << 2 | //
std::is_signed<__U>::value << 1 | //
std::is_signed<__V>::value) {
case 0: { // u = u * u
__ckd_uintmax_t __z = __x * __y;
int __o = __x && __z / __x != __y;
*__res = __z;
return __o | (sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res));
}
case 1: { // u = u * s
__ckd_uintmax_t __z = __x * __y;
int __o = __x && __z / __x != __y;
*__res = __z;
return (__o | ((static_cast<__ckd_intmax_t>(__y) < 0) & !!__x) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
case 2: { // u = s * u
__ckd_uintmax_t __z = __x * __y;
int __o = __x && __z / __x != __y;
*__res = __z;
return (__o | ((static_cast<__ckd_intmax_t>(__x) < 0) & !!__y) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
case 3: { // u = s * s
int __o = false;
if (static_cast<__ckd_intmax_t>(__x & __y) < 0) {
__x = 0 - __x;
__y = 0 - __y;
} else if (static_cast<__ckd_intmax_t>(__x ^ __y) < 0) {
__o = __x && __y;
}
__ckd_uintmax_t __z = __x * __y;
__o |= __x && __z / __x != __y;
*__res = __z;
return __o | (sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res));
}
case 4: { // s = u * u
__ckd_uintmax_t __z = __x * __y;
int __o = __x && __z / __x != __y;
*__res = __z;
return (__o | (static_cast<__ckd_intmax_t>(__z) < 0) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
case 5: { // s = u * s
__ckd_uintmax_t __t = 0 - __y;
__t = static_cast<__ckd_intmax_t>(__t) < 0 ? __y : __t;
__ckd_uintmax_t __p = __t * __x;
int __o = __t && __p / __t != __x;
int __n = static_cast<__ckd_intmax_t>(__y) < 0;
__ckd_uintmax_t __z = __n ? 0 - __p : __p;
*__res = __z;
__ckd_uintmax_t __m = std::numeric_limits<__ckd_intmax_t>::max();
return (__o | (__p > __m + __n) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
case 6: { // s = s * u
__ckd_uintmax_t __t = 0 - __x;
__t = static_cast<__ckd_intmax_t>(__t) < 0 ? __x : __t;
__ckd_uintmax_t __p = __t * __y;
int __o = __t && __p / __t != __y;
int __n = static_cast<__ckd_intmax_t>(__x) < 0;
__ckd_uintmax_t __z = __n ? 0 - __p : __p;
*__res = __z;
__ckd_uintmax_t __m = std::numeric_limits<__ckd_intmax_t>::max();
return (__o | (__p > __m + __n) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
case 7: { // s = s * s
__ckd_uintmax_t __z = __x * __y;
*__res = __z;
return ((((static_cast<__ckd_intmax_t>(__y) < 0) &&
(static_cast<__ckd_intmax_t>(__x) ==
std::numeric_limits<__ckd_intmax_t>::min())) ||
(__y && ((static_cast<__ckd_intmax_t>(__z) /
static_cast<__ckd_intmax_t>(__y)) !=
static_cast<__ckd_intmax_t>(__x)))) |
(sizeof(__T) < sizeof(__z) &&
__z != static_cast<__ckd_uintmax_t>(*__res)));
}
default:
for (;;)
(void)0;
}
}
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#define ckd_add(res, a, b) __ckd_expr(add, (res), (a), (b))
#define ckd_sub(res, a, b) __ckd_expr(sub, (res), (a), (b))
#define ckd_mul(res, a, b) __ckd_expr(mul, (res), (a), (b))
#if defined(__GNUC__) || defined(__llvm__)
#define __ckd_inline \
extern __inline \
__attribute__((__gnu_inline__, __always_inline__, __artificial__))
#else
#define __ckd_inline static inline
#endif
#ifdef __ckd_have_int128
#define __ckd_generic_int128(x, y) \
, \
signed __int128 \
: x \
, unsigned __int128 : y
#else
#define __ckd_generic_int128(x, y)
#endif
#define __ckd_sign(T) ((T)1 << (sizeof(T) * 8 - 1))
#define __ckd_is_signed(x) \
_Generic(x, \
signed char: 1, \
unsigned char: 0, \
signed short: 1, \
unsigned short: 0, \
signed int: 1, \
unsigned int: 0, \
signed long: 1, \
unsigned long: 0, \
signed long long: 1, \
unsigned long long: 0 __ckd_generic_int128(1, 0))
#define __ckd_expr(op, res, a, b) \
(_Generic(*res, \
signed char: __ckd_##op##_schar, \
unsigned char: __ckd_##op##_uchar, \
signed short: __ckd_##op##_sshort, \
unsigned short: __ckd_##op##_ushort, \
signed int: __ckd_##op##_sint, \
unsigned int: __ckd_##op##_uint, \
signed long: __ckd_##op##_slong, \
unsigned long: __ckd_##op##_ulong, \
signed long long: __ckd_##op##_slonger, \
unsigned long long: __ckd_##op##_ulonger __ckd_generic_int128( \
__ckd_##op##_sint128, __ckd_##op##_uint128))( \
res, a, b, __ckd_is_signed(a), __ckd_is_signed(b)))
#define __ckd_declare_add(S, T) \
__ckd_inline char S(void* __res, \
__ckd_uintmax_t __x, \
__ckd_uintmax_t __y, \
char __a_signed, \
char __b_signed) \
{ \
__ckd_uintmax_t __z = __x + __y; \
*(T*)__res = __z; \
char __truncated = 0; \
if (sizeof(T) < sizeof(__ckd_intmax_t)) { \
__truncated = __z != (__ckd_uintmax_t)(T)__z; \
} \
switch (__ckd_is_signed((T)0) << 2 | __a_signed << 1 | __b_signed) { \
case 0: /* u = u + u */ \
return __truncated | (__z < __x); \
case 1: /* u = u + s */ \
__y ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | \
((__ckd_intmax_t)((__z ^ __x) & (__z ^ __y)) < 0); \
case 2: /* u = s + u */ \
__x ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | \
((__ckd_intmax_t)((__z ^ __x) & (__z ^ __y)) < 0); \
case 3: /* u = s + s */ \
return __truncated | \
((__ckd_intmax_t)(((__z | __x) & __y) | \
((__z & __x) & ~__y)) < 0); \
case 4: /* s = u + u */ \
return __truncated | (__z < __x) | ((__ckd_intmax_t)__z < 0); \
case 5: /* s = u + s */ \
__y ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | (__x + __y < __y); \
case 6: /* s = s + u */ \
__x ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | (__x + __y < __x); \
case 7: /* s = s + s */ \
return __truncated | \
((__ckd_intmax_t)((__z ^ __x) & (__z ^ __y)) < 0); \
default: \
for (;;) \
(void)0; \
} \
}
__ckd_declare_add(__ckd_add_schar,
signed char) __ckd_declare_add(__ckd_add_uchar, unsigned char)
__ckd_declare_add(__ckd_add_sshort, signed short) __ckd_declare_add(
__ckd_add_ushort,
unsigned short) __ckd_declare_add(__ckd_add_sint, signed int)
__ckd_declare_add(__ckd_add_uint, unsigned int) __ckd_declare_add(
__ckd_add_slong,
signed long) __ckd_declare_add(__ckd_add_ulong, unsigned long)
__ckd_declare_add(__ckd_add_slonger,
signed long long) __ckd_declare_add(__ckd_add_ulonger,
unsigned long long)
#ifdef __ckd_have_int128
__ckd_declare_add(__ckd_add_sint128,
signed __int128) __ckd_declare_add(__ckd_add_uint128,
unsigned __int128)
#endif
#define __ckd_declare_sub(S, T) \
__ckd_inline char S(void* __res, \
__ckd_uintmax_t __x, \
__ckd_uintmax_t __y, \
char __a_signed, \
char __b_signed) \
{ \
__ckd_uintmax_t __z = __x - __y; \
*(T*)__res = __z; \
char __truncated = 0; \
if (sizeof(T) < sizeof(__ckd_intmax_t)) { \
__truncated = __z != (__ckd_uintmax_t)(T)__z; \
} \
switch (__ckd_is_signed((T)0) << 2 | __a_signed << 1 | __b_signed) { \
case 0: /* u = u - u */ \
return __truncated | (__x < __y); \
case 1: /* u = u - s */ \
__y ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | \
((__ckd_intmax_t)((__x ^ __y) & (__z ^ __x)) < 0); \
case 2: /* u = s - u */ \
return __truncated | (__y > __x) | ((__ckd_intmax_t)__x < 0); \
case 3: /* u = s - s */ \
return __truncated | \
((__ckd_intmax_t)(((__z & __x) & __y) | \
((__z | __x) & ~__y)) < 0); \
case 4: /* s = u - u */ \
return __truncated | \
((__x < __y) ^ ((__ckd_intmax_t)__z < 0)); \
case 5: /* s = u - s */ \
__y ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | (__x >= __y); \
case 6: /* s = s - u */ \
__x ^= __ckd_sign(__ckd_uintmax_t); \
return __truncated | (__x < __y); \
case 7: /* s = s - s */ \
return __truncated | \
((__ckd_intmax_t)((__x ^ __y) & (__z ^ __x)) < 0); \
default: \
for (;;) \
(void)0; \
} \
}
__ckd_declare_sub(__ckd_sub_schar, signed char) __ckd_declare_sub(
__ckd_sub_uchar,
unsigned char) __ckd_declare_sub(__ckd_sub_sshort, signed short)
__ckd_declare_sub(__ckd_sub_ushort,
unsigned short) __ckd_declare_sub(__ckd_sub_sint,
signed int)
__ckd_declare_sub(__ckd_sub_uint, unsigned int) __ckd_declare_sub(
__ckd_sub_slong,
signed long) __ckd_declare_sub(__ckd_sub_ulong, unsigned long)
__ckd_declare_sub(__ckd_sub_slonger, signed long long)
__ckd_declare_sub(__ckd_sub_ulonger, unsigned long long)
#ifdef __ckd_have_int128
__ckd_declare_sub(__ckd_sub_sint128, signed __int128)
__ckd_declare_sub(__ckd_sub_uint128, unsigned __int128)
#endif
#define __ckd_declare_mul(S, T) \
__ckd_inline char S(void* __res, \
__ckd_uintmax_t __x, \
__ckd_uintmax_t __y, \
char __a_signed, \
char __b_signed) \
{ \
switch (__ckd_is_signed((T)0) << 2 | __a_signed << 1 | __b_signed) { \
case 0: { /* u = u * u */ \
__ckd_uintmax_t __z = __x * __y; \
int __o = __x && __z / __x != __y; \
*(T*)__res = __z; \
return __o | (sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res); \
} \
case 1: { /* u = u * s */ \
__ckd_uintmax_t __z = __x * __y; \
int __o = __x && __z / __x != __y; \
*(T*)__res = __z; \
return (__o | (((__ckd_intmax_t)__y < 0) & !!__x) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
case 2: { /* u = s * u */ \
__ckd_uintmax_t __z = __x * __y; \
int __o = __x && __z / __x != __y; \
*(T*)__res = __z; \
return (__o | (((__ckd_intmax_t)__x < 0) & !!__y) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
case 3: { /* u = s * s */ \
int __o = 0; \
if ((__ckd_intmax_t)(__x & __y) < 0) { \
__x = 0 - __x; \
__y = 0 - __y; \
} else if ((__ckd_intmax_t)(__x ^ __y) < 0) { \
__o = __x && __y; \
} \
__ckd_uintmax_t __z = __x * __y; \
__o |= __x && __z / __x != __y; \
*(T*)__res = __z; \
return __o | (sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res); \
} \
case 4: { /* s = u * u */ \
__ckd_uintmax_t __z = __x * __y; \
int __o = __x && __z / __x != __y; \
*(T*)__res = __z; \
return (__o | ((__ckd_intmax_t)(__z) < 0) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
case 5: { /* s = u * s */ \
__ckd_uintmax_t __t = 0 - __y; \
__t = (__ckd_intmax_t)(__t) < 0 ? __y : __t; \
__ckd_uintmax_t __p = __t * __x; \
int __o = __t && __p / __t != __x; \
int __n = (__ckd_intmax_t)__y < 0; \
__ckd_uintmax_t __z = __n ? 0 - __p : __p; \
*(T*)__res = __z; \
__ckd_uintmax_t __m = __ckd_sign(__ckd_uintmax_t) - 1; \
return (__o | (__p > __m + __n) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
case 6: { /* s = s * u */ \
__ckd_uintmax_t __t = 0 - __x; \
__t = (__ckd_intmax_t)(__t) < 0 ? __x : __t; \
__ckd_uintmax_t __p = __t * __y; \
int __o = __t && __p / __t != __y; \
int __n = (__ckd_intmax_t)__x < 0; \
__ckd_uintmax_t __z = __n ? 0 - __p : __p; \
*(T*)__res = __z; \
__ckd_uintmax_t __m = __ckd_sign(__ckd_uintmax_t) - 1; \
return (__o | (__p > __m + __n) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
case 7: { /* s = s * s */ \
__ckd_uintmax_t __z = __x * __y; \
*(T*)__res = __z; \
return ( \
((((__ckd_intmax_t)__y < 0) && \
(__x == __ckd_sign(__ckd_uintmax_t))) || \
(__y && (((__ckd_intmax_t)__z / (__ckd_intmax_t)__y) != \
(__ckd_intmax_t)__x))) | \
(sizeof(T) < sizeof(__z) && \
__z != (__ckd_uintmax_t) * (T*)__res)); \
} \
default: \
for (;;) \
(void)0; \
} \
}
__ckd_declare_mul(__ckd_mul_schar, signed char)
__ckd_declare_mul(__ckd_mul_uchar, unsigned char)
__ckd_declare_mul(
__ckd_mul_sshort,
signed short) __ckd_declare_mul(__ckd_mul_ushort,
unsigned short)
__ckd_declare_mul(__ckd_mul_sint, signed int)
__ckd_declare_mul(__ckd_mul_uint, unsigned int)
__ckd_declare_mul(__ckd_mul_slong,
signed long)
__ckd_declare_mul(__ckd_mul_ulong,
unsigned long)
__ckd_declare_mul(__ckd_mul_slonger,
signed long long)
__ckd_declare_mul(__ckd_mul_ulonger,
unsigned long long)
#ifdef __ckd_have_int128
__ckd_declare_mul(__ckd_mul_sint128,
signed __int128)
__ckd_declare_mul(__ckd_mul_uint128,
unsigned __int128)
#endif
#else
#pragma message "checked integer arithmetic unsupported in this environment"
#define ckd_add(res, x, y) (*(res) = (x) + (y), 0)
#define ckd_sub(res, x, y) (*(res) = (x) - (y), 0)
#define ckd_mul(res, x, y) (*(res) = (x) * (y), 0)
#endif /* GNU */
#endif /* stdckdint.h */
#endif /* JTCKDINT_H_ */

View File

@@ -0,0 +1,59 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "wpi/util/json.hpp"
#include <gtest/gtest.h>
namespace wpi::util {
TEST(JsonTest, ParseMaxUint64) {
auto j = json::parse_or_throw("18446744073709551615");
EXPECT_TRUE(j.is_uint());
EXPECT_EQ(j.get_uint(), 18446744073709551615ull);
}
TEST(JsonTest, MarshalMaxUint64) {
json j{18446744073709551615ull};
EXPECT_EQ(j.to_string(), "18446744073709551615");
}
TEST(JsonTest, AssignBool) {
json j;
j = true;
ASSERT_TRUE(j.is_bool());
EXPECT_TRUE(j.get_bool());
}
TEST(JsonTest, AssignBoolToMap) {
json j;
j["key"] = true;
ASSERT_TRUE(j["key"].is_bool());
EXPECT_TRUE(j["key"].get_bool());
}
TEST(JsonTest, AssignBoolToArray) {
json j;
j.emplace_back(true);
ASSERT_TRUE(j[0].is_bool());
EXPECT_TRUE(j[0].get_bool());
}
TEST(JsonTest, BoolObject) {
json j = json::object("key", true);
ASSERT_TRUE(j["key"].is_bool());
EXPECT_TRUE(j["key"].get_bool());
}
TEST(JsonTest, BoolArray) {
json j = json::array(true, false, true);
ASSERT_TRUE(j[0].is_bool());
EXPECT_TRUE(j[0].get_bool());
ASSERT_TRUE(j[1].is_bool());
EXPECT_FALSE(j[1].get_bool());
ASSERT_TRUE(j[2].is_bool());
EXPECT_TRUE(j[2].get_bool());
}
} // namespace wpi::util