From de3e211fdb7aa05ab036859a7f3c9917ea64a4fc Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sun, 29 Mar 2026 15:37:32 -0700 Subject: [PATCH] [upstream_utils] Add jart/json.cpp --- ThirdPartyNotices.txt | 19 + upstream_utils/json.py | 54 + .../0001-Move-to-wpi-util-namespace.patch | 48 + .../0002-Rename-class-from-Json-to-json.patch | 599 +++++++ .../0003-Use-snake_case-names.patch | 286 +++ .../0004-Suppress-pedantic-warning.patch | 24 + .../0005-Suppress-type-limits-warning.patch | 24 + ...te-double-conversion-include-for-wpi.patch | 24 + ...-typedefs-for-array-and-object-types.patch | 155 ++ .../0008-Change-Type-to-an-enum-class.patch | 582 ++++++ .../0009-Rename-long-to-int.patch | 183 ++ ...-Make-non-object-functions-constexpr.patch | 102 ++ ...11-Add-array-and-object-constructors.patch | 28 + ...-getters-for-string-array-and-object.patch | 84 + .../json_patches/0013-Use-wpi-StringMap.patch | 35 + .../0014-Use-std-string_view.patch | 128 ++ ...to-return-expected-with-error-string.patch | 93 + .../0016-Add-parse_or_throw.patch | 43 + .../json_patches/0017-Add-operator.patch | 67 + .../0018-Add-container-functions.patch | 166 ++ ...from_json-and-json_serializer-struct.patch | 106 ++ ...d-array-and-object-factory-functions.patch | 64 + .../0021-Add-array-constructor.patch | 55 + ...gize-and-marshal-and-use-raw_ostream.patch | 329 ++++ .../0023-Update-include-path.patch | 22 + ...upport-for-unsigned-long-long-values.patch | 326 ++++ .../0025-Make-bool-constructor-safe.patch | 22 + wpiutil/BUILD.bazel | 3 +- wpiutil/CMakeLists.txt | 2 +- wpiutil/build.gradle | 8 +- .../thirdparty/json/include/wpi/util/json.hpp | 424 +++++ .../main/native/thirdparty/json/src/json.cpp | 1569 +++++++++++++++++ .../native/thirdparty/json/src/jtckdint.h | 670 +++++++ wpiutil/src/test/native/cpp/json_test.cpp | 59 + 34 files changed, 6397 insertions(+), 6 deletions(-) create mode 100755 upstream_utils/json.py create mode 100644 upstream_utils/json_patches/0001-Move-to-wpi-util-namespace.patch create mode 100644 upstream_utils/json_patches/0002-Rename-class-from-Json-to-json.patch create mode 100644 upstream_utils/json_patches/0003-Use-snake_case-names.patch create mode 100644 upstream_utils/json_patches/0004-Suppress-pedantic-warning.patch create mode 100644 upstream_utils/json_patches/0005-Suppress-type-limits-warning.patch create mode 100644 upstream_utils/json_patches/0006-Update-double-conversion-include-for-wpi.patch create mode 100644 upstream_utils/json_patches/0007-Use-typedefs-for-array-and-object-types.patch create mode 100644 upstream_utils/json_patches/0008-Change-Type-to-an-enum-class.patch create mode 100644 upstream_utils/json_patches/0009-Rename-long-to-int.patch create mode 100644 upstream_utils/json_patches/0010-Make-non-object-functions-constexpr.patch create mode 100644 upstream_utils/json_patches/0011-Add-array-and-object-constructors.patch create mode 100644 upstream_utils/json_patches/0012-Add-const-getters-for-string-array-and-object.patch create mode 100644 upstream_utils/json_patches/0013-Use-wpi-StringMap.patch create mode 100644 upstream_utils/json_patches/0014-Use-std-string_view.patch create mode 100644 upstream_utils/json_patches/0015-Change-parse-to-return-expected-with-error-string.patch create mode 100644 upstream_utils/json_patches/0016-Add-parse_or_throw.patch create mode 100644 upstream_utils/json_patches/0017-Add-operator.patch create mode 100644 upstream_utils/json_patches/0018-Add-container-functions.patch create mode 100644 upstream_utils/json_patches/0019-Add-to_json-from_json-and-json_serializer-struct.patch create mode 100644 upstream_utils/json_patches/0020-Add-array-and-object-factory-functions.patch create mode 100644 upstream_utils/json_patches/0021-Add-array-constructor.patch create mode 100644 upstream_utils/json_patches/0022-Improve-stringize-and-marshal-and-use-raw_ostream.patch create mode 100644 upstream_utils/json_patches/0023-Update-include-path.patch create mode 100644 upstream_utils/json_patches/0024-Add-support-for-unsigned-long-long-values.patch create mode 100644 upstream_utils/json_patches/0025-Make-bool-constructor-safe.patch create mode 100644 wpiutil/src/main/native/thirdparty/json/include/wpi/util/json.hpp create mode 100644 wpiutil/src/main/native/thirdparty/json/src/json.cpp create mode 100644 wpiutil/src/main/native/thirdparty/json/src/jtckdint.h create mode 100644 wpiutil/src/test/native/cpp/json_test.cpp diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index ef9b15550f..b944c1bd38 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -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 ============================================================================== diff --git a/upstream_utils/json.py b/upstream_utils/json.py new file mode 100755 index 0000000000..91ac0b1b15 --- /dev/null +++ b/upstream_utils/json.py @@ -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() diff --git a/upstream_utils/json_patches/0001-Move-to-wpi-util-namespace.patch b/upstream_utils/json_patches/0001-Move-to-wpi-util-namespace.patch new file mode 100644 index 0000000000..fc010d313e --- /dev/null +++ b/upstream_utils/json_patches/0001-Move-to-wpi-util-namespace.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 + #include + +-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 diff --git a/upstream_utils/json_patches/0002-Rename-class-from-Json-to-json.patch b/upstream_utils/json_patches/0002-Rename-class-from-Json-to-json.patch new file mode 100644 index 0000000000..492b2dbe3d --- /dev/null +++ b/upstream_utils/json_patches/0002-Rename-class-from-Json-to-json.patch @@ -0,0 +1,599 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(other.array_value); ++ new (&array_value) std::vector(other.array_value); + break; + case Object: +- new (&object_value) std::map(other.object_value); ++ new (&object_value) std::map(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(other.array_value); ++ new (&array_value) std::vector(other.array_value); + break; + case Object: + new (&object_value) +- std::map(other.object_value); ++ std::map(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(std::move(other.array_value)); ++ new (&array_value) std::vector(std::move(other.array_value)); + break; + case Object: + new (&object_value) +- std::map(std::move(other.object_value)); ++ std::map(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(std::move(other.array_value)); ++ std::vector(std::move(other.array_value)); + break; + case Object: + new (&object_value) +- std::map(std::move(other.object_value)); ++ std::map(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::getArray() ++std::vector& ++json::getArray() + { + switch (type_) { + case Array: +@@ -532,8 +532,8 @@ Json::getArray() + } + } + +-std::map& +-Json::getObject() ++std::map& ++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(); ++ new (&array_value) std::vector(); + } + + void +-Json::setObject() ++json::setObject() + { + if (type_ >= String) + clear(); + type_ = Object; +- new (&object_value) std::map(); ++ new (&object_value) std::map(); + } + + 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::parse(const std::string& s) ++std::pair ++json::parse(const std::string& s) + { +- Json::Status s2; +- std::pair res; ++ json::Status s2; ++ std::pair 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 array_value; +- std::map object_value; ++ std::vector array_value; ++ std::map object_value; + }; + + public: + static const char* StatusToString(Status); +- static std::pair parse(const std::string&); ++ static std::pair 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& getArray(); +- std::map& getObject(); ++ std::vector& getArray(); ++ std::map& 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 diff --git a/upstream_utils/json_patches/0003-Use-snake_case-names.patch b/upstream_utils/json_patches/0003-Use-snake_case-names.patch new file mode 100644 index 0000000000..3cf8493083 --- /dev/null +++ b/upstream_utils/json_patches/0003-Use-snake_case-names.patch @@ -0,0 +1,286 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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::getArray() ++json::get_array() + { + switch (type_) { + case Array: +@@ -533,7 +533,7 @@ json::getArray() + } + + std::map& +-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& getArray(); +- std::map& 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& get_array(); ++ std::map& 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: diff --git a/upstream_utils/json_patches/0004-Suppress-pedantic-warning.patch b/upstream_utils/json_patches/0004-Suppress-pedantic-warning.patch new file mode 100644 index 0000000000..f960650176 --- /dev/null +++ b/upstream_utils/json_patches/0004-Suppress-pedantic-warning.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 diff --git a/upstream_utils/json_patches/0005-Suppress-type-limits-warning.patch b/upstream_utils/json_patches/0005-Suppress-type-limits-warning.patch new file mode 100644 index 0000000000..7bc2c20b84 --- /dev/null +++ b/upstream_utils/json_patches/0005-Suppress-type-limits-warning.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 diff --git a/upstream_utils/json_patches/0006-Update-double-conversion-include-for-wpi.patch b/upstream_utils/json_patches/0006-Update-double-conversion-include-for-wpi.patch new file mode 100644 index 0000000000..3c690b3d1c --- /dev/null +++ b/upstream_utils/json_patches/0006-Update-double-conversion-include-for-wpi.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 + #include + +-#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" diff --git a/upstream_utils/json_patches/0007-Use-typedefs-for-array-and-object-types.patch b/upstream_utils/json_patches/0007-Use-typedefs-for-array-and-object-types.patch new file mode 100644 index 0000000000..9a659dfdbb --- /dev/null +++ b/upstream_utils/json_patches/0007-Use-typedefs-for-array-and-object-types.patch @@ -0,0 +1,155 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(other.array_value); ++ new (&array_value) array_t(other.array_value); + break; + case Object: +- new (&object_value) std::map(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(other.array_value); ++ new (&array_value) array_t(other.array_value); + break; + case Object: +- new (&object_value) +- std::map(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(std::move(other.array_value)); ++ new (&array_value) array_t(std::move(other.array_value)); + break; + case Object: +- new (&object_value) +- std::map(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(std::move(other.array_value)); ++ new (&array_value) array_t(std::move(other.array_value)); + break; + case Object: +- new (&object_value) +- std::map(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::array_t& + json::get_array() + { + switch (type_) { +@@ -536,7 +532,7 @@ json::get_array() + } + } + +-std::map& ++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(); ++ new (&array_value) array_t(); + } + + void +@@ -562,7 +558,7 @@ json::set_object() + if (type_ >= String) + clear(); + type_ = Object; +- new (&object_value) std::map(); ++ 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; ++ using object_t = std::map; ++ + enum Type + { + Null, +@@ -83,8 +86,8 @@ class json + double double_value; + long long long_value; + std::string string_value; +- std::vector array_value; +- std::map 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& get_array(); +- std::map& get_object(); ++ array_t& get_array(); ++ object_t& get_object(); + + bool contains(const std::string&) const; + diff --git a/upstream_utils/json_patches/0008-Change-Type-to-an-enum-class.patch b/upstream_utils/json_patches/0008-Change-Type-to-an-enum-class.patch new file mode 100644 index 0000000000..871bc7c076 --- /dev/null +++ b/upstream_utils/json_patches/0008-Change-Type-to-an-enum-class.patch @@ -0,0 +1,582 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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; + using object_t = std::map; + +- 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; diff --git a/upstream_utils/json_patches/0009-Rename-long-to-int.patch b/upstream_utils/json_patches/0009-Rename-long-to-int.patch new file mode 100644 index 0000000000..a0c2c4b9e9 --- /dev/null +++ b/upstream_utils/json_patches/0009-Rename-long-to-int.patch @@ -0,0 +1,183 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(); diff --git a/upstream_utils/json_patches/0010-Make-non-object-functions-constexpr.patch b/upstream_utils/json_patches/0010-Make-non-object-functions-constexpr.patch new file mode 100644 index 0000000000..0db54df295 --- /dev/null +++ b/upstream_utils/json_patches/0010-Make-non-object-functions-constexpr.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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; + } diff --git a/upstream_utils/json_patches/0011-Add-array-and-object-constructors.patch b/upstream_utils/json_patches/0011-Add-array-and-object-constructors.patch new file mode 100644 index 0000000000..22796c2744 --- /dev/null +++ b/upstream_utils/json_patches/0011-Add-array-and-object-constructors.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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_; diff --git a/upstream_utils/json_patches/0012-Add-const-getters-for-string-array-and-object.patch b/upstream_utils/json_patches/0012-Add-const-getters-for-string-array-and-object.patch new file mode 100644 index 0000000000..757e1c4e3a --- /dev/null +++ b/upstream_utils/json_patches/0012-Add-const-getters-for-string-array-and-object.patch @@ -0,0 +1,84 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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; + diff --git a/upstream_utils/json_patches/0013-Use-wpi-StringMap.patch b/upstream_utils/json_patches/0013-Use-wpi-StringMap.patch new file mode 100644 index 0000000000..2ceb526a3b --- /dev/null +++ b/upstream_utils/json_patches/0013-Use-wpi-StringMap.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 ++ + #include + #include + ++#include "wpi/util/StringMap.hpp" ++ + namespace wpi::util { + + class json + { + public: + using array_t = std::vector; +- using object_t = std::map; ++ using object_t = wpi::util::StringMap; + + enum class Type + { diff --git a/upstream_utils/json_patches/0014-Use-std-string_view.patch b/upstream_utils/json_patches/0014-Use-std-string_view.patch new file mode 100644 index 0000000000..ec79df1781 --- /dev/null +++ b/upstream_utils/json_patches/0014-Use-std-string_view.patch @@ -0,0 +1,128 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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::parse(const std::string& s) ++json::parse(std::string_view s) + { + json::Status s2; + std::pair 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 ++#include + #include + + #include "wpi/util/StringMap.hpp" +@@ -94,7 +95,7 @@ class json + + public: + static const char* StatusToString(Status); +- static std::pair parse(const std::string&); ++ static std::pair 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); + }; + diff --git a/upstream_utils/json_patches/0015-Change-parse-to-return-expected-with-error-string.patch b/upstream_utils/json_patches/0015-Change-parse-to-return-expected-with-error-string.patch new file mode 100644 index 0000000000..3216c41c2e --- /dev/null +++ b/upstream_utils/json_patches/0015-Change-parse-to-return-expected-with-error-string.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 ++wpi::util::expected + json::parse(std::string_view s) + { +- json::Status s2; +- std::pair 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 + + #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 parse(std::string_view); ++ static wpi::util::expected 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); diff --git a/upstream_utils/json_patches/0016-Add-parse_or_throw.patch b/upstream_utils/json_patches/0016-Add-parse_or_throw.patch new file mode 100644 index 0000000000..05ec3308ff --- /dev/null +++ b/upstream_utils/json_patches/0016-Add-parse_or_throw.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 parse(std::string_view); ++ static json parse_or_throw(std::string_view); + + json(const json&); + json(json&&); diff --git a/upstream_utils/json_patches/0017-Add-operator.patch b/upstream_utils/json_patches/0017-Add-operator.patch new file mode 100644 index 0000000000..43f144c45f --- /dev/null +++ b/upstream_utils/json_patches/0017-Add-operator.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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; + using object_t = wpi::util::StringMap; +@@ -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 diff --git a/upstream_utils/json_patches/0018-Add-container-functions.patch b/upstream_utils/json_patches/0018-Add-container-functions.patch new file mode 100644 index 0000000000..ac65028d6c --- /dev/null +++ b/upstream_utils/json_patches/0018-Add-container-functions.patch @@ -0,0 +1,166 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(); diff --git a/upstream_utils/json_patches/0019-Add-to_json-from_json-and-json_serializer-struct.patch b/upstream_utils/json_patches/0019-Add-to_json-from_json-and-json_serializer-struct.patch new file mode 100644 index 0000000000..cc1d9d78c1 --- /dev/null +++ b/upstream_utils/json_patches/0019-Add-to_json-from_json-and-json_serializer-struct.patch @@ -0,0 +1,106 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 ++struct json_serializer; ++ ++namespace detail { ++template ++concept HasToJson = requires(json& j, const T& val) { ++ { to_json(j, val) }; ++}; ++ ++template ++concept HasFromJson = requires(const json& j, T& val) { ++ { from_json(j, val) }; ++}; ++ ++template ++concept HasJsonSerializer = requires (json& j, const json& cj, const T& val) { ++ typename json_serializer>; ++ { json_serializer>::to(j, val) }; ++}; ++ ++template ++concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) { ++ typename json_serializer>; ++ { json_serializer>::from(cj) } -> std::convertible_to>; ++}; ++} // namespace detail ++ + class json + { + friend bool operator==(const json& lhs, const json& rhs); +@@ -152,6 +181,18 @@ class json + { + } + ++ template ++ json(const T& value) : type_(Type::Null) ++ { ++ to_json(*this, value); ++ } ++ ++ template ++ json(const T& value) : type_(Type::Null) ++ { ++ json_serializer::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 ++ T get() const { ++ T value; ++ from_json(*this, value); ++ return value; ++ } ++ ++ template ++ T get() const { ++ return json_serializer::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 ++ json& operator=(const T& value) { ++ to_json(*this, value); ++ return *this; ++ } ++ ++ template ++ json& operator=(const T& value) { ++ json_serializer::to(*this, value); ++ return *this; ++ } ++ + json& operator[](size_t); + json& operator[](std::string_view); + diff --git a/upstream_utils/json_patches/0020-Add-array-and-object-factory-functions.patch b/upstream_utils/json_patches/0020-Add-array-and-object-factory-functions.patch new file mode 100644 index 0000000000..0577234da9 --- /dev/null +++ b/upstream_utils/json_patches/0020-Add-array-and-object-factory-functions.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 + #include ++#include ++#include + #include + + #include "wpi/util/StringMap.hpp" +@@ -53,6 +55,13 @@ concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) { + typename json_serializer>; + { json_serializer>::from(cj) } -> std::convertible_to>; + }; ++ ++template ++void apply_pairs_helper(F&& f, Tuple&& t, std::index_sequence) { ++ // This fold expression creates a sequence of calls to f for each pair ++ (f(std::get(std::forward(t)), ++ std::get(std::forward(t))), ...); ++} + } // namespace detail + + class json +@@ -275,6 +284,27 @@ class json + void set_array(); + void set_object(); + ++ template ++ static json array(Args&&... args) { ++ json j; ++ j.set_array(); ++ (j.array_value.emplace_back(std::forward(args)), ...); ++ return j; ++ } ++ ++ template ++ static json object(Args&&... args) { ++ json j; ++ j.set_object(); ++ detail::apply_pairs_helper( ++ [&](auto&& key, auto&& value) { ++ j.object_value[std::forward(key)] = std::forward(value); ++ }, ++ std::forward_as_tuple(args...), ++ std::make_index_sequence{}); ++ return j; ++ } ++ + std::string to_string() const; + std::string to_string_pretty() const; + diff --git a/upstream_utils/json_patches/0021-Add-array-constructor.patch b/upstream_utils/json_patches/0021-Add-array-constructor.patch new file mode 100644 index 0000000000..ebad63f003 --- /dev/null +++ b/upstream_utils/json_patches/0021-Add-array-constructor.patch @@ -0,0 +1,55 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 ++#include + #include + #include + #include +@@ -56,6 +58,19 @@ concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) { + { json_serializer>::from(cj) } -> std::convertible_to>; + }; + ++template ++concept JsonConvertible = ++ HasToJson || ++ HasJsonSerializer || ++ std::is_same_v || ++ std::is_same_v || ++ std::is_floating_point_v || ++ std::is_integral_v || ++ std::convertible_to || ++ std::convertible_to || ++ std::is_same_v> || ++ std::is_same_v>; ++ + template + void apply_pairs_helper(F&& f, Tuple&& t, std::index_sequence) { + // This fold expression creates a sequence of calls to f for each pair +@@ -190,6 +205,12 @@ class json + { + } + ++ template ++ requires detail::JsonConvertible> ++ json(const R& range) : type_(Type::Array), array_value(std::ranges::cbegin(range), std::ranges::cend(range)) ++ { ++ } ++ + template + json(const T& value) : type_(Type::Null) + { diff --git a/upstream_utils/json_patches/0022-Improve-stringize-and-marshal-and-use-raw_ostream.patch b/upstream_utils/json_patches/0022-Improve-stringize-and-marshal-and-use-raw_ostream.patch new file mode 100644 index 0000000000..85e3a0cbe2 --- /dev/null +++ b/upstream_utils/json_patches/0022-Improve-stringize-and-marshal-and-use-raw_ostream.patch @@ -0,0 +1,329 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(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 + 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); + }; + diff --git a/upstream_utils/json_patches/0023-Update-include-path.patch b/upstream_utils/json_patches/0023-Update-include-path.patch new file mode 100644 index 0000000000..168b22f5a1 --- /dev/null +++ b/upstream_utils/json_patches/0023-Update-include-path.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 diff --git a/upstream_utils/json_patches/0024-Add-support-for-unsigned-long-long-values.patch b/upstream_utils/json_patches/0024-Add-support-for-unsigned-long-long-values.patch new file mode 100644 index 0000000000..8aab172e8d --- /dev/null +++ b/upstream_utils/json_patches/0024-Add-support-for-unsigned-long-long-values.patch @@ -0,0 +1,326 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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(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(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 + #include + #include + #include +@@ -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); + diff --git a/upstream_utils/json_patches/0025-Make-bool-constructor-safe.patch b/upstream_utils/json_patches/0025-Make-bool-constructor-safe.patch new file mode 100644 index 0000000000..61a2e11c05 --- /dev/null +++ b/upstream_utils/json_patches/0025-Make-bool-constructor-safe.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Peter Johnson +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 auto value) : type_(Type::Bool), bool_value(value) + { + } + diff --git a/wpiutil/BUILD.bazel b/wpiutil/BUILD.bazel index f9ec7bd40e..39b3937519 100644 --- a/wpiutil/BUILD.bazel +++ b/wpiutil/BUILD.bazel @@ -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", diff --git a/wpiutil/CMakeLists.txt b/wpiutil/CMakeLists.txt index b0d6b1f1c3..70ee007590 100644 --- a/wpiutil/CMakeLists.txt +++ b/wpiutil/CMakeLists.txt @@ -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 diff --git a/wpiutil/build.gradle b/wpiutil/build.gradle index 4ae3ca9eae..ea4c7aec36 100644 --- a/wpiutil/build.gradle +++ b/wpiutil/build.gradle @@ -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') { diff --git a/wpiutil/src/main/native/thirdparty/json/include/wpi/util/json.hpp b/wpiutil/src/main/native/thirdparty/json/include/wpi/util/json.hpp new file mode 100644 index 0000000000..2746badf92 --- /dev/null +++ b/wpiutil/src/main/native/thirdparty/json/include/wpi/util/json.hpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include "wpi/util/StringMap.hpp" +#include "wpi/util/expected" + +namespace wpi::util { + +class json; +class raw_ostream; + +template +struct json_serializer; + +namespace detail { +template +concept HasToJson = requires(json& j, const T& val) { + { to_json(j, val) }; +}; + +template +concept HasFromJson = requires(const json& j, T& val) { + { from_json(j, val) }; +}; + +template +concept HasJsonSerializer = requires (json& j, const json& cj, const T& val) { + typename json_serializer>; + { json_serializer>::to(j, val) }; +}; + +template +concept HasJsonDeserializer = requires (json& j, const json& cj, const T& val) { + typename json_serializer>; + { json_serializer>::from(cj) } -> std::convertible_to>; +}; + +template +concept JsonConvertible = + HasToJson || + HasJsonSerializer || + std::is_same_v || + std::is_same_v || + std::is_floating_point_v || + std::is_integral_v || + std::convertible_to || + std::convertible_to || + std::is_same_v> || + std::is_same_v>; + +template +void apply_pairs_helper(F&& f, Tuple&& t, std::index_sequence) { + // This fold expression creates a sequence of calls to f for each pair + (f(std::get(std::forward(t)), + std::get(std::forward(t))), ...); +} +} // namespace detail + +class json +{ + friend bool operator==(const json& lhs, const json& rhs); + public: + using array_t = std::vector; + using object_t = wpi::util::StringMap; + + 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 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 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 + requires detail::JsonConvertible> + json(const R& range) : type_(Type::Array), array_value(std::ranges::cbegin(range), std::ranges::cend(range)) + { + } + + template + json(const T& value) : type_(Type::Null) + { + to_json(*this, value); + } + + template + json(const T& value) : type_(Type::Null) + { + json_serializer::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 + T get() const { + T value; + from_json(*this, value); + return value; + } + + template + T get() const { + return json_serializer::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 + static json array(Args&&... args) { + json j; + j.set_array(); + (j.array_value.emplace_back(std::forward(args)), ...); + return j; + } + + template + static json object(Args&&... args) { + json j; + j.set_object(); + detail::apply_pairs_helper( + [&](auto&& key, auto&& value) { + j.object_value[std::forward(key)] = std::forward(value); + }, + std::forward_as_tuple(args...), + std::make_index_sequence{}); + return j; + } + + std::string to_string() const; + std::string to_string_pretty() const; + + json& operator=(const json&); + json& operator=(json&&); + + template + json& operator=(const T& value) { + to_json(*this, value); + return *this; + } + + template + json& operator=(const T& value) { + json_serializer::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 diff --git a/wpiutil/src/main/native/thirdparty/json/src/json.cpp b/wpiutil/src/main/native/thirdparty/json/src/json.cpp new file mode 100644 index 0000000000..7f23ece0e8 --- /dev/null +++ b/wpiutil/src/main/native/thirdparty/json/src/json.cpp @@ -0,0 +1,1569 @@ +// -*- 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. + +#include "wpi/util/json.hpp" +#include "jtckdint.h" + +#include +#include +#include +#include +#include +#include + +#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" +#endif + +#define KEY 1 +#define COMMA 2 +#define COLON 4 +#define ARRAY 8 +#define OBJECT 16 +#define DEPTH 20 + +#define ASCII 0 +#define C0 1 +#define DQUOTE 2 +#define BACKSLASH 3 +#define UTF8_2 4 +#define UTF8_3 5 +#define UTF8_4 6 +#define C1 7 +#define UTF8_3_E0 8 +#define UTF8_3_ED 9 +#define UTF8_4_F0 10 +#define BADUTF8 11 +#define EVILUTF8 12 + +#define UTF16_MASK 0xfc00 +#define UTF16_MOAR 0xd800 // 0xD800..0xDBFF +#define UTF16_CONT 0xdc00 // 0xDC00..0xDFFF + +#define READ32LE(S) \ + ((uint_least32_t)(255 & (S)[3]) << 030 | \ + (uint_least32_t)(255 & (S)[2]) << 020 | \ + (uint_least32_t)(255 & (S)[1]) << 010 | \ + (uint_least32_t)(255 & (S)[0]) << 000) + +#define ThomPikeCont(x) (0200 == (0300 & (x))) +#define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3)) +#define ThomPikeLen(x) (7 - ThomPikeMsb(x)) +#define ThomPikeMsb(x) ((255 & (x)) < 252 ? Bsr(255 & ~(x)) : 1) +#define ThomPikeMerge(x, y) ((x) << 6 | (077 & (y))) + +#define IsSurrogate(wc) ((0xf800 & (wc)) == 0xd800) +#define IsHighSurrogate(wc) (((wc) & UTF16_MASK) == UTF16_MOAR) +#define IsLowSurrogate(wc) (((wc) & UTF16_MASK) == UTF16_CONT) +#define MergeUtf16(hi, lo) ((((hi) - 0xD800) << 10) + ((lo) - 0xDC00) + 0x10000) +#define EncodeUtf16(wc) \ + ((0x0000 <= (wc) && (wc) <= 0xFFFF) || (0xE000 <= (wc) && (wc) <= 0xFFFF) \ + ? (wc) \ + : 0x10000 <= (wc) && (wc) <= 0x10FFFF \ + ? (((((wc) - 0x10000) >> 10) + 0xD800) | \ + (unsigned)((((wc) - 0x10000) & 1023) + 0xDC00) << 16) \ + : 0xFFFD) + +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define ON_LOGIC_ERROR(s) throw std::logic_error(s) +#else +#define ON_LOGIC_ERROR(s) abort() +#endif + +namespace wpi::util { + +static const char kJsonStr[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, // 0000 ascii (0) + 1, 1, 1, 1, 1, 1, 1, 1, // 0010 + 1, 1, 1, 1, 1, 1, 1, 1, // 0020 c0 (1) + 1, 1, 1, 1, 1, 1, 1, 1, // 0030 + 0, 0, 2, 0, 0, 0, 0, 0, // 0040 dquote (2) + 0, 0, 0, 0, 0, 0, 0, 0, // 0050 + 0, 0, 0, 0, 0, 0, 0, 0, // 0060 + 0, 0, 0, 0, 0, 0, 0, 0, // 0070 + 0, 0, 0, 0, 0, 0, 0, 0, // 0100 + 0, 0, 0, 0, 0, 0, 0, 0, // 0110 + 0, 0, 0, 0, 0, 0, 0, 0, // 0120 + 0, 0, 0, 0, 3, 0, 0, 0, // 0130 backslash (3) + 0, 0, 0, 0, 0, 0, 0, 0, // 0140 + 0, 0, 0, 0, 0, 0, 0, 0, // 0150 + 0, 0, 0, 0, 0, 0, 0, 0, // 0160 + 0, 0, 0, 0, 0, 0, 0, 0, // 0170 + 7, 7, 7, 7, 7, 7, 7, 7, // 0200 c1 (8) + 7, 7, 7, 7, 7, 7, 7, 7, // 0210 + 7, 7, 7, 7, 7, 7, 7, 7, // 0220 + 7, 7, 7, 7, 7, 7, 7, 7, // 0230 + 11, 11, 11, 11, 11, 11, 11, 11, // 0240 latin1 (4) + 11, 11, 11, 11, 11, 11, 11, 11, // 0250 + 11, 11, 11, 11, 11, 11, 11, 11, // 0260 + 11, 11, 11, 11, 11, 11, 11, 11, // 0270 + 12, 12, 4, 4, 4, 4, 4, 4, // 0300 utf8-2 (5) + 4, 4, 4, 4, 4, 4, 4, 4, // 0310 + 4, 4, 4, 4, 4, 4, 4, 4, // 0320 utf8-2 + 4, 4, 4, 4, 4, 4, 4, 4, // 0330 + 8, 5, 5, 5, 5, 5, 5, 5, // 0340 utf8-3 (6) + 5, 5, 5, 5, 5, 9, 5, 5, // 0350 + 10, 6, 6, 6, 6, 11, 11, 11, // 0360 utf8-4 (7) + 11, 11, 11, 11, 11, 11, 11, 11, // 0370 +}; + +static const char kEscapeLiteral[128] = { + 9, 9, 9, 9, 9, 9, 9, 9, 9, 1, 2, 9, 4, 3, 9, 9, // 0x00 + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 0x10 + 0, 0, 7, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 6, // 0x20 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9, 9, 0, // 0x30 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, // 0x50 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, // 0x70 +}; + +alignas(signed char) static const signed char kHexToInt[256] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x20 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 0x30 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x40 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x50 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x60 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x70 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x80 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x90 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xa0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xb0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xc0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xd0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xe0 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xf0 +}; + +static const double_conversion::DoubleToStringConverter kDoubleToJson( + double_conversion::DoubleToStringConverter::UNIQUE_ZERO | + double_conversion::DoubleToStringConverter::EMIT_POSITIVE_EXPONENT_SIGN, + "1e5000", + "null", + 'e', + -6, + 21, + 6, + 0); + +static const double_conversion::StringToDoubleConverter kJsonToDouble( + double_conversion::StringToDoubleConverter::ALLOW_CASE_INSENSITIVITY | + double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | + double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK | + double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES, + 0.0, + 1.0, + "Infinity", + "NaN"); + +#if defined(__GNUC__) || defined(__clang__) +#define Bsr(x) (__builtin_clz(x) ^ (sizeof(int) * CHAR_BIT - 1)) +#else +static int +Bsr(int x) +{ + int r = 0; + if (x & 0xFFFF0000u) { + x >>= 16; + r |= 16; + } + if (x & 0xFF00) { + x >>= 8; + r |= 8; + } + if (x & 0xF0) { + x >>= 4; + r |= 4; + } + if (x & 0xC) { + x >>= 2; + r |= 2; + } + if (x & 0x2) { + r |= 1; + } + return r; +} +#endif + +static double +StringToDouble(const char* s, size_t n, int* out_processed) +{ + if (n == (size_t)-1) + n = strlen(s); + int processed; + double res = kJsonToDouble.StringToDouble(s, n, &processed); + if (out_processed) + *out_processed = processed; + return res; +} + +static char* +UlongToString(char* p, unsigned long long x) +{ + char t; + size_t i, a, b; + i = 0; + do { + p[i++] = x % 10 + '0'; + x = x / 10; + } while (x > 0); + p[i] = '\0'; + if (i) { + for (a = 0, b = i - 1; a < b; ++a, --b) { + t = p[a]; + p[a] = p[b]; + p[b] = t; + } + } + return p + i; +} + +static char* +LongToString(char* p, long long x) +{ + if (x < 0) + *p++ = '-', x = 0 - (unsigned long long)x; + return UlongToString(p, x); +} + +json::json(const char* value) +{ + if (value) { + type_ = Type::String; + new (&string_value) std::string(value); + } else { + type_ = Type::Null; + } +} + +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) + clear(); +} + +void +json::clear() +{ + switch (type_) { + case Type::String: + string_value.~basic_string(); + break; + case Type::Array: + array_value.~array_t(); + break; + case Type::Object: + object_value.~object_t(); + break; + default: + break; + } + type_ = Type::Null; +} + +json::json(const json& other) : type_(other.type_) +{ + switch (type_) { + case Type::Null: + break; + case Type::Bool: + bool_value = other.bool_value; + break; + 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; + case Type::Double: + double_value = other.double_value; + break; + case Type::String: + new (&string_value) std::string(other.string_value); + break; + case Type::Array: + new (&array_value) array_t(other.array_value); + break; + case Type::Object: + new (&object_value) object_t(other.object_value); + break; + default: + ON_LOGIC_ERROR("Unhandled JSON type."); + } +} + +json& +json::operator=(const json& other) +{ + if (this != &other) { + if (type_ >= Type::String) + clear(); + type_ = other.type_; + switch (type_) { + case Type::Null: + break; + case Type::Bool: + bool_value = other.bool_value; + break; + 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; + case Type::Double: + double_value = other.double_value; + break; + case Type::String: + new (&string_value) std::string(other.string_value); + break; + case Type::Array: + new (&array_value) array_t(other.array_value); + break; + case Type::Object: + new (&object_value) object_t(other.object_value); + break; + default: + ON_LOGIC_ERROR("Unhandled JSON type."); + } + } + return *this; +} + +json::json(json&& other) : type_(other.type_) +{ + switch (type_) { + case Type::Null: + break; + case Type::Bool: + bool_value = other.bool_value; + break; + 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; + case Type::Double: + double_value = other.double_value; + break; + case Type::String: + new (&string_value) std::string(std::move(other.string_value)); + break; + case Type::Array: + new (&array_value) array_t(std::move(other.array_value)); + break; + case Type::Object: + new (&object_value) object_t(std::move(other.object_value)); + break; + default: + ON_LOGIC_ERROR("Unhandled JSON type."); + } + other.type_ = Type::Null; +} + +json& +json::operator=(json&& other) +{ + if (this != &other) { + if (type_ >= Type::String) + clear(); + type_ = other.type_; + switch (type_) { + case Type::Null: + break; + case Type::Bool: + bool_value = other.bool_value; + break; + 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; + case Type::Double: + double_value = other.double_value; + break; + case Type::String: + new (&string_value) std::string(std::move(other.string_value)); + break; + case Type::Array: + new (&array_value) array_t(std::move(other.array_value)); + break; + case Type::Object: + new (&object_value) object_t(std::move(other.object_value)); + break; + default: + ON_LOGIC_ERROR("Unhandled JSON type.");; + } + other.type_ = Type::Null; + } + return *this; +} + +double +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: + return double_value; + default: + ON_LOGIC_ERROR("JSON value is not a number."); + } +} + +long long +json::get_int() const +{ + switch (type_) { + case Type::Int: + return long_value; + default: + ON_LOGIC_ERROR("JSON value is not a long."); + } +} + +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 +{ + switch (type_) { + case Type::Bool: + return bool_value; + default: + ON_LOGIC_ERROR("JSON value is not a bool."); + } +} + +float +json::get_float() const +{ + switch (type_) { + case Type::Float: + return float_value; + case Type::Double: + return double_value; + default: + ON_LOGIC_ERROR("JSON value is not a floating-point number."); + } +} + +double +json::get_double() const +{ + switch (type_) { + case Type::Float: + return float_value; + case Type::Double: + return double_value; + default: + ON_LOGIC_ERROR("JSON value is not a floating-point number."); + } +} + +std::string& +json::get_string() +{ + switch (type_) { + case Type::String: + return string_value; + default: + ON_LOGIC_ERROR("JSON value is not a 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() +{ + switch (type_) { + case Type::Array: + return array_value; + default: + ON_LOGIC_ERROR("JSON value is not an 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() +{ + switch (type_) { + case Type::Object: + return object_value; + default: + ON_LOGIC_ERROR("JSON value is not an 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() +{ + if (type_ >= Type::String) + clear(); + type_ = Type::Array; + new (&array_value) array_t(); +} + +void +json::set_object() +{ + if (type_ >= Type::String) + clear(); + type_ = Type::Object; + new (&object_value) object_t(); +} + +bool +json::contains(std::string_view key) const +{ + if (!is_object()) + return false; + 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) +{ + if (!is_array()) + set_array(); + if (index >= array_value.size()) { + array_value.resize(index + 1); + } + return array_value[index]; +} + +json& +json::operator[](std::string_view key) +{ + if (!is_object()) + set_object(); + 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 +{ + std::string b; + wpi::util::raw_string_ostream os(b); + marshal(os, false, 0); + return b; +} + +std::string +json::to_string_pretty() const +{ + std::string b; + wpi::util::raw_string_ostream os(b); + marshal(os, true, 0); + return b; +} + +void +json::marshal(wpi::util::raw_ostream& os, bool pretty, int indent) const +{ + switch (type_) { + case Type::Null: + stringify_null(os); + break; + case Type::String: + stringify_string(os, string_value); + break; + case Type::Bool: + stringify_bool(os, bool_value); + break; + 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; + case Type::Double: + stringify_double(os, double_value); + break; + case Type::Array: + stringify_array(os, array_value, pretty, indent); + break; + case Type::Object: + stringify_object(os, object_value, pretty, indent); + break; + default: + ON_LOGIC_ERROR("Unhandled JSON type."); + } +} + +void +json::stringify_null(wpi::util::raw_ostream& os) +{ + 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_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; + 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(wpi::util::raw_ostream& os, std::string_view s) +{ + size_t i, j, m; + wint_t x, a, b; + unsigned long long w; + for (i = 0; i < s.size();) { + x = s[i++] & 255; + if (x >= 0300) { + a = ThomPikeByte(x); + m = ThomPikeLen(x) - 1; + if (i + m <= s.size()) { + for (j = 0;;) { + b = s[i + j] & 0xff; + if (!ThomPikeCont(b)) + break; + a = ThomPikeMerge(a, b); + if (++j == m) { + x = a; + i += j; + break; + } + } + } + } + switch (0 <= x && x <= 127 ? kEscapeLiteral[x] : 9) { + case 0: + os << static_cast(x); + break; + case 1: + os << "\\t"; + break; + case 2: + os << "\\n"; + break; + case 3: + os << "\\r"; + break; + case 4: + os << "\\f"; + break; + case 5: + os << "\\\\"; + break; + case 6: + os << "\\/"; + break; + case 7: + os << "\\\""; + break; + case 9: + w = EncodeUtf16(x); + do { + char esc[6]; + esc[0] = '\\'; + esc[1] = 'u'; + esc[2] = "0123456789abcdef"[(w & 0xF000) >> 014]; + esc[3] = "0123456789abcdef"[(w & 0x0F00) >> 010]; + esc[4] = "0123456789abcdef"[(w & 0x00F0) >> 004]; + esc[5] = "0123456789abcdef"[(w & 0x000F) >> 000]; + os.write(esc, 6); + } while ((w >>= 16)); + break; + default: + ON_LOGIC_ERROR("Unhandled character escape code during string serialization."); + } + } +} + +json::Status +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) + return depth_exceeded; + for (a = p, d = +1; p < e;) { + switch ((c = *p++ & 255)) { + case ' ': // spaces + case '\n': + case '\r': + case '\t': + a = p; + break; + + case ',': // present in list and object + if (context & COMMA) { + context = 0; + a = p; + break; + } else { + return unexpected_comma; + } + + case ':': // present only in object after key + if (context & COLON) { + context = 0; + a = p; + break; + } else { + return unexpected_colon; + } + + case 'n': // null + if (context & (KEY | COLON | COMMA)) + goto OnColonCommaKey; + if (p + 3 <= e && READ32LE(p - 1) == READ32LE("null")) { + p += 3; + return success; + } else { + return illegal_character; + } + + case 'f': // false + if (context & (KEY | COLON | COMMA)) + goto OnColonCommaKey; + if (p + 4 <= e && READ32LE(p) == READ32LE("alse")) { + j.type_ = Type::Bool; + j.bool_value = false; + p += 4; + return success; + } else { + return illegal_character; + } + + case 't': // true + if (context & (KEY | COLON | COMMA)) + goto OnColonCommaKey; + if (p + 3 <= e && READ32LE(p - 1) == READ32LE("true")) { + j.type_ = Type::Bool; + j.bool_value = true; + p += 3; + return success; + } else { + return illegal_character; + } + + default: + return illegal_character; + + OnColonCommaKey: + if (context & KEY) + return object_key_must_be_string; + OnColonComma: + if (context & COLON) + return missing_colon; + return missing_comma; + + case '-': // negative + if (context & (COLON | COMMA | KEY)) + goto OnColonCommaKey; + if (p < e && isdigit(*p)) { + d = -1; + break; + } else { + return bad_negative; + } + + case '0': // zero or number + if (context & (COLON | COMMA | KEY)) + goto OnColonCommaKey; + if (p < e) { + if (*p == '.') { + if (p + 1 == e || !isdigit(p[1])) + return bad_double; + goto UseDubble; + } else if (*p == 'e' || *p == 'E') { + goto UseDubble; + } else if (isdigit(*p)) { + return unexpected_octal; + } + } + j.type_ = Type::Int; + j.long_value = 0; + return success; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': // integer + if (context & (COLON | COMMA | KEY)) + goto OnColonCommaKey; + 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(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; + } + } + if (ux <= static_cast(LLONG_MAX)) { + j.type_ = Type::Int; + j.long_value = ux; + } else { + 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; + } + return success; + + UseDubble: // number + j.type_ = 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')) + return bad_exponent; + p = a + c; + return success; + + case '[': { // Array + if (context & (COLON | COMMA | KEY)) + goto OnColonCommaKey; + j.set_array(); + 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; + j.array_value.emplace_back(std::move(value)); + context = ARRAY | COMMA; + } + } + + case ']': + if (context & ARRAY) + return absent_value; + return unexpected_end_of_array; + + case '}': + if (context & OBJECT) + return absent_value; + return unexpected_end_of_object; + + case '{': { // Object + if (context & (COLON | COMMA | KEY)) + goto OnColonCommaKey; + j.set_object(); + context = KEY | OBJECT; + json key, value; + for (;;) { + Status status = parse(key, p, e, context, depth - 1); + if (status == absent_value) + return success; + if (status != success) + return status; + if (!key.is_string()) + return object_key_must_be_string; + status = parse(value, p, e, COLON, depth - 1); + if (status == absent_value) + return object_missing_value; + if (status != success) + return status; + j.object_value.emplace(std::move(key.string_value), + std::move(value)); + context = KEY | COMMA | OBJECT; + key.clear(); + } + } + + case '"': { // string + std::string b; + if (context & (COLON | COMMA)) + goto OnColonComma; + for (;;) { + if (p >= e) + return unexpected_end_of_string; + switch (kJsonStr[(c = *p++ & 255)]) { + + case ASCII: + b += c; + break; + + case DQUOTE: + j.type_ = Type::String; + new (&j.string_value) std::string(std::move(b)); + return success; + + case BACKSLASH: + if (p >= e) + return unexpected_end_of_string; + switch ((c = *p++ & 255)) { + case '"': + case '/': + case '\\': + b += c; + break; + case 'b': + b += '\b'; + break; + case 'f': + b += '\f'; + break; + case 'n': + b += '\n'; + break; + case 'r': + b += '\r'; + break; + case 't': + b += '\t'; + break; + case 'x': + if (p + 2 <= e && // + (A = kHexToInt[p[0] & 255]) != + -1 && // HEX + (B = kHexToInt[p[1] & 255]) != -1) { // + c = A << 4 | B; + if (!(0x20 <= c && c <= 0x7E)) + return hex_escape_not_printable; + p += 2; + b += c; + break; + } else { + return invalid_hex_escape; + } + case 'u': + if (p + 4 <= e && // + (A = kHexToInt[p[0] & 255]) != -1 && // + (B = kHexToInt[p[1] & 255]) != + -1 && // UCS-2 + (C = kHexToInt[p[2] & 255]) != -1 && // + (D = kHexToInt[p[3] & 255]) != -1) { // + c = A << 12 | B << 8 | C << 4 | D; + if (!IsSurrogate(c)) { + p += 4; + } else if (IsHighSurrogate(c)) { + if (p + 4 + 6 <= e && // + p[4] == '\\' && // + p[5] == 'u' && // + (A = kHexToInt[p[6] & 255]) != + -1 && // UTF-16 + (B = kHexToInt[p[7] & 255]) != + -1 && // + (C = kHexToInt[p[8] & 255]) != + -1 && // + (D = kHexToInt[p[9] & 255]) != + -1) { // + u = + A << 12 | B << 8 | C << 4 | D; + if (IsLowSurrogate(u)) { + p += 4 + 6; + c = MergeUtf16(c, u); + } else { + goto BadUnicode; + } + } else { + goto BadUnicode; + } + } else { + goto BadUnicode; + } + // UTF-8 + EncodeUtf8: + if (c <= 0x7f) { + w[0] = c; + i = 1; + } else if (c <= 0x7ff) { + w[0] = 0300 | (c >> 6); + w[1] = 0200 | (c & 077); + i = 2; + } else if (c <= 0xffff) { + if (IsSurrogate(c)) { + ReplacementCharacter: + c = 0xfffd; + } + w[0] = 0340 | (c >> 12); + w[1] = 0200 | ((c >> 6) & 077); + w[2] = 0200 | (c & 077); + i = 3; + } else if (~(c >> 18) & 007) { + w[0] = 0360 | (c >> 18); + w[1] = 0200 | ((c >> 12) & 077); + w[2] = 0200 | ((c >> 6) & 077); + w[3] = 0200 | (c & 077); + i = 4; + } else { + goto ReplacementCharacter; + } + b.append(w, i); + } else { + return invalid_unicode_escape; + BadUnicode: + // Echo invalid \uXXXX sequences + // Rather than corrupting UTF-8! + b += "\\u"; + } + break; + default: + return invalid_escape_character; + } + break; + + case UTF8_2: + if (p < e && // + (p[0] & 0300) == 0200) { // + c = (c & 037) << 6 | // + (p[0] & 077); // + p += 1; + goto EncodeUtf8; + } else { + return malformed_utf8; + } + + case UTF8_3_E0: + if (p + 2 <= e && // + (p[0] & 0377) < 0240 && // + (p[0] & 0300) == 0200 && // + (p[1] & 0300) == 0200) { + return overlong_utf8_0x7ff; + } + // fallthrough + + case UTF8_3: + ThreeUtf8: + if (p + 2 <= e && // + (p[0] & 0300) == 0200 && // + (p[1] & 0300) == 0200) { // + c = (c & 017) << 12 | // + (p[0] & 077) << 6 | // + (p[1] & 077); // + p += 2; + goto EncodeUtf8; + } else { + return malformed_utf8; + } + + case UTF8_3_ED: + if (p + 2 <= e && // + (p[0] & 0377) >= 0240) { // + if (p + 5 <= e && // + (p[0] & 0377) >= 0256 && // + (p[1] & 0300) == 0200 && // + (p[2] & 0377) == 0355 && // + (p[3] & 0377) >= 0260 && // + (p[4] & 0300) == 0200) { // + A = (0355 & 017) << 12 | // CESU-8 + (p[0] & 077) << 6 | // + (p[1] & 077); // + B = (0355 & 017) << 12 | // + (p[3] & 077) << 6 | // + (p[4] & 077); // + c = ((A - 0xDB80) << 10) + // + ((B - 0xDC00) + 0x10000); // + goto EncodeUtf8; + } else if ((p[0] & 0300) == 0200 && // + (p[1] & 0300) == 0200) { // + return utf16_surrogate_in_utf8; + } else { + return malformed_utf8; + } + } + goto ThreeUtf8; + + case UTF8_4_F0: + if (p + 3 <= e && (p[0] & 0377) < 0220 && + (((uint_least32_t)(p[+2] & 0377) << 030 | + (uint_least32_t)(p[+1] & 0377) << 020 | + (uint_least32_t)(p[+0] & 0377) << 010 | + (uint_least32_t)(p[-1] & 0377) << 000) & + 0xC0C0C000) == 0x80808000) { + return overlong_utf8_0xffff; + } + // fallthrough + case UTF8_4: + if (p + 3 <= e && // + ((A = + ((uint_least32_t)(p[+2] & 0377) << 030 | // + (uint_least32_t)(p[+1] & 0377) << 020 | // + (uint_least32_t)(p[+0] & 0377) << 010 | // + (uint_least32_t)(p[-1] & 0377) + << 000)) & // + 0xC0C0C000) == 0x80808000) { // + A = (A & 7) << 18 | // + (A & (077 << 010)) << (12 - 010) | // + (A & (077 << 020)) >> -(6 - 020) | // + (A & (077 << 030)) >> 030; // + if (A <= 0x10FFFF) { + c = A; + p += 3; + goto EncodeUtf8; + } else { + return utf8_exceeds_utf16_range; + } + } else { + return malformed_utf8; + } + + case EVILUTF8: + if (p < e && (p[0] & 0300) == 0200) + return overlong_ascii; + // fallthrough + case BADUTF8: + return illegal_utf8_character; + case C0: + return non_del_c0_control_code_in_string; + case C1: + return c1_control_code_in_string; + default: + ON_LOGIC_ERROR("Unhandled character category during string parsing."); + } + } + } + } + } + if (depth == DEPTH) + return absent_value; + return unexpected_eof; +} + +wpi::util::expected +json::parse(std::string_view s) +{ + const char* p = s.data(); + const char* e = s.data() + s.size(); + json j; + Status result = parse(j, p, e, 0, DEPTH); + if (result != success) { + return wpi::util::unexpected(StatusToString(result)); + } + json j2; + Status s2 = parse(j2, p, e, 0, DEPTH); + if (s2 != absent_value) { + return wpi::util::unexpected(StatusToString(trailing_content)); + } + 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) +{ + switch (status) { + case success: + return "success"; + case bad_double: + return "bad_double"; + case absent_value: + return "absent_value"; + case bad_negative: + return "bad_negative"; + case bad_exponent: + return "bad_exponent"; + case missing_comma: + return "missing_comma"; + case missing_colon: + return "missing_colon"; + case malformed_utf8: + return "malformed_utf8"; + case depth_exceeded: + return "depth_exceeded"; + case stack_overflow: + return "stack_overflow"; + case unexpected_eof: + return "unexpected_eof"; + case overlong_ascii: + return "overlong_ascii"; + case unexpected_comma: + return "unexpected_comma"; + case unexpected_colon: + return "unexpected_colon"; + case unexpected_octal: + return "unexpected_octal"; + case trailing_content: + return "trailing_content"; + case illegal_character: + return "illegal_character"; + case invalid_hex_escape: + return "invalid_hex_escape"; + case overlong_utf8_0x7ff: + return "overlong_utf8_0x7ff"; + case overlong_utf8_0xffff: + return "overlong_utf8_0xffff"; + case object_missing_value: + return "object_missing_value"; + case illegal_utf8_character: + return "illegal_utf8_character"; + case invalid_unicode_escape: + return "invalid_unicode_escape"; + case utf16_surrogate_in_utf8: + return "utf16_surrogate_in_utf8"; + case unexpected_end_of_array: + return "unexpected_end_of_array"; + case hex_escape_not_printable: + return "hex_escape_not_printable"; + case invalid_escape_character: + return "invalid_escape_character"; + case utf8_exceeds_utf16_range: + return "utf8_exceeds_utf16_range"; + case unexpected_end_of_string: + return "unexpected_end_of_string"; + case unexpected_end_of_object: + return "unexpected_end_of_object"; + case object_key_must_be_string: + return "object_key_must_be_string"; + case c1_control_code_in_string: + return "c1_control_code_in_string"; + case non_del_c0_control_code_in_string: + return "non_del_c0_control_code_in_string"; + default: + ON_LOGIC_ERROR("Unhandled Json status value."); + } +} + +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::Uint: + return lhs.ulong_value == rhs.ulong_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/wpiutil/src/main/native/thirdparty/json/src/jtckdint.h b/wpiutil/src/main/native/thirdparty/json/src/jtckdint.h new file mode 100644 index 0000000000..ac6a52e5aa --- /dev/null +++ b/wpiutil/src/main/native/thirdparty/json/src/jtckdint.h @@ -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() && !defined(__cplusplus) +#include +#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() && __ckd_has_include()))) +#include +#include + +template +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 +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 +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_ */ diff --git a/wpiutil/src/test/native/cpp/json_test.cpp b/wpiutil/src/test/native/cpp/json_test.cpp new file mode 100644 index 0000000000..ba54bcd861 --- /dev/null +++ b/wpiutil/src/test/native/cpp/json_test.cpp @@ -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 + +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