Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/extras/catch_amalgamated.cpp
diff options
context:
space:
mode:
authorLeitwolf112026-10-25 13:57:03 +0300
committerLeitwolf112026-10-25 13:57:03 +0300
commit45f8c539e86099cb80ecf29b0bf2b3340e141001 (patch)
treecf7f28b76fb2994a3750ecdd127b5738f4d56ad1 /extras/catch_amalgamated.cpp
parentd2e3e074bb2e1b8ffd9fc990d0b87667ea602f58 (diff)
downloadcatch3-45f8c539e86099cb80ecf29b0bf2b3340e141001.tar.gz
Read and write JSON with yyjson and JUnit XML with pugixml
The runner's hand-written JSON parser and writer, and its XML escaping, are replaced by two established MIT libraries, vendored in third_party/: yyjson 0.13.0 and pugixml 1.16. JsonValue and the JUnit model stay as they are. Both are compiled into Catch3's own translation units, so a project that links its own yyjson or pugixml is not affected: - src/Progmasoft/Runner/Yyjson.inl includes yyjson.c into Json.cpp with yyjson_api defined as static, so no yyjson symbol leaves that file. - src/Progmasoft/PugiXml.inl includes pugixml header-only into XmlWriter.cpp with its namespace renamed to Progmasoft_Catch3_Pugi. - Both are read as system headers, outside the warnings Catch3 builds with; they are .inl files, which the header install rules leave out. JSON keeps its behaviour: strings are bytes and come back as written (invalid UTF-8 is allowed both ways), non-finite numbers are written as null, documents nested deeper than 256 levels are refused while they are converted, and whole numbers are written as integers. Fractions are now written as the shortest text that reads back as the same double. JUnit XML stays equivalent; pugixml writes ' and > in attributes as they are, U+FFFD as the character itself and empty elements as <x />. The tests follow. third_party/{yyjson,pugixml} each have BUILD.bazel, CMakeLists.txt, meson.build, xmake.lua and premake5.lua, which only provide the include directory to the library; CMake links them through BUILD_INTERFACE. tools/scripts/updateThirdParty.py pins the version and sha256 of each archive; --check, now a step of the header and license workflow, fails when a vendored file differs from its archive, and --update writes them. Renovate proposes new versions through a regex manager on that table. The amalgamation now has a third file, catch_amalgamated_third_party.hpp, with both wrappers and the libraries inlined; catch_amalgamated.cpp includes it once. The documents and the release notes say so.
Diffstat (limited to 'extras/catch_amalgamated.cpp')
-rw-r--r--extras/catch_amalgamated.cpp941
1 files changed, 241 insertions, 700 deletions
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp
index 0f336b53..a9cdb04b 100644
--- a/extras/catch_amalgamated.cpp
+++ b/extras/catch_amalgamated.cpp
@@ -7,7 +7,7 @@
// SPDX-License-Identifier: BSL-1.0
// Catch3 v0.3.0, on the Catch2 v3.16.1 engine
-// Generated: 2026-10-25 02:37:10.065190
+// Generated: 2026-10-25 13:52:54.556111
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -658,9 +658,6 @@ namespace Progmasoft::Catch3::Runner
private:
void
- Write(std::string &output, int indent, int depth) const;
-
- void
Destroy() noexcept;
void
@@ -8976,6 +8973,8 @@ namespace Progmasoft::Catch3
// Copyright (c) 2026 Progmasoft.
// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
+#include "catch_amalgamated_third_party.hpp"
+
#include <ostream>
#include <sstream>
#include <string_view>
@@ -9055,63 +9054,50 @@ namespace Progmasoft::Catch3
(value >= 0x10000 && value <= 0x10FFFF);
}
- void
- WriteEscaped(std::string_view value, std::ostream &output, bool attribute)
+ /// The text as XML can hold it: every byte that is not part of a
+ /// well-formed character XML allows becomes U+FFFD, replaced and not
+ /// dropped, so the reader sees that something was there. pugixml
+ /// escapes the rest.
+ std::string
+ XmlText(std::string_view value)
{
+ std::string result;
+ result.reserve(value.size());
for (std::size_t index = 0; index < value.size();)
{
const DecodedCharacter character = DecodeUtf8(value, index);
if (character.length == 0 || !IsXmlCharacter(character.value))
{
- // A character XML cannot hold is replaced, not dropped,
- // so the reader sees that something was there.
- output << "&#xFFFD;";
+ result += "\xEF\xBF\xBD";
++index;
continue;
}
- if (character.value < 0x80)
- {
- switch (static_cast<char>(character.value))
- {
- case '&':
- output << "&amp;";
- break;
- case '<':
- output << "&lt;";
- break;
- case '>':
- output << "&gt;";
- break;
- case '"':
- output << (attribute ? "&quot;" : "\"");
- break;
- case '\'':
- output << (attribute ? "&apos;" : "'");
- break;
- case '\n':
- output << (attribute ? "&#10;" : "\n");
- break;
- default:
- output.put(static_cast<char>(character.value));
- }
- }
- else
- {
- output.write(value.data() + index,
- static_cast<std::streamsize>(character.length));
- }
+ result.append(value.data() + index, character.length);
index += character.length;
}
+ return result;
}
void
- WriteAttribute(std::ostream &output,
- std::string_view name,
+ AddAttribute(pugi::xml_node node,
+ const char *name,
+ std::string_view value)
+ {
+ node.append_attribute(name).set_value(XmlText(value).c_str());
+ }
+
+ void
+ AddText(pugi::xml_node node, std::string_view value)
+ {
+ node.text().set(XmlText(value).c_str());
+ }
+
+ void
+ AddTextElement(pugi::xml_node parent,
+ const char *name,
std::string_view value)
{
- output << ' ' << name << "=\"";
- WriteEscaped(value, output, true);
- output << '"';
+ AddText(parent.append_child(name), value);
}
std::string
@@ -9125,27 +9111,6 @@ namespace Progmasoft::Catch3
"." + fraction;
}
- void
- WriteIndent(std::ostream &output, unsigned level)
- {
- for (unsigned index = 0; index < level; ++index)
- {
- output << " ";
- }
- }
-
- void
- WriteTextElement(std::ostream &output,
- unsigned indent,
- std::string_view name,
- std::string_view value)
- {
- WriteIndent(output, indent);
- output << '<' << name << '>';
- WriteEscaped(value, output, false);
- output << "</" << name << ">\n";
- }
-
std::string
Output(const Attempt &attempt, bool errors)
{
@@ -9159,27 +9124,22 @@ namespace Progmasoft::Catch3
/// An earlier attempt: `<rerunFailure>` before a final failure,
/// `<flakyFailure>` before a final pass.
void
- WriteEarlierAttempt(std::ostream &output,
- const Attempt &attempt,
- std::string_view element)
- {
- WriteIndent(output, 3);
- output << '<' << element;
- WriteAttribute(output, "message", SummarizeFailure(attempt));
- WriteAttribute(output, "type", CategoryName(attempt.category));
- output << ">\n";
- WriteTextElement(output, 4, "stackTrace", DescribeFailure(attempt));
+ AddEarlierAttempt(pugi::xml_node testCase,
+ const Attempt &attempt,
+ const char *element)
+ {
+ pugi::xml_node node = testCase.append_child(element);
+ AddAttribute(node, "message", SummarizeFailure(attempt));
+ AddAttribute(node, "type", CategoryName(attempt.category));
+ AddTextElement(node, "stackTrace", DescribeFailure(attempt));
if (!attempt.standardOutput.empty())
{
- WriteTextElement(
- output, 4, "system-out", Output(attempt, false));
+ AddTextElement(node, "system-out", Output(attempt, false));
}
if (!Output(attempt, true).empty())
{
- WriteTextElement(output, 4, "system-err", Output(attempt, true));
+ AddTextElement(node, "system-err", Output(attempt, true));
}
- WriteIndent(output, 3);
- output << "</" << element << ">\n";
}
std::string
@@ -9200,23 +9160,21 @@ namespace Progmasoft::Catch3
}
void
- WriteTestCase(std::ostream &output,
- const TestResult &test,
- std::string_view suiteName)
- {
- WriteIndent(output, 2);
- output << "<testcase";
- WriteAttribute(output,
- "classname",
- test.className.empty() ? suiteName : test.className);
- WriteAttribute(output, "name", test.name);
- WriteAttribute(output, "time", JUnitSeconds(test.DurationMicroseconds()));
+ AddTestCase(pugi::xml_node suite,
+ const TestResult &test,
+ std::string_view suiteName)
+ {
+ pugi::xml_node node = suite.append_child("testcase");
+ AddAttribute(node,
+ "classname",
+ test.className.empty() ? suiteName : test.className);
+ AddAttribute(node, "name", test.name);
+ AddAttribute(node, "time", JUnitSeconds(test.DurationMicroseconds()));
if (!test.file.empty())
{
- WriteAttribute(output, "file", test.file);
- WriteAttribute(output, "line", std::to_string(test.line));
+ AddAttribute(node, "file", test.file);
+ AddAttribute(node, "line", std::to_string(test.line));
}
- output << ">\n";
const Attempt *last =
test.attempts.empty() ? nullptr : &test.attempts.back();
@@ -9226,12 +9184,10 @@ namespace Progmasoft::Catch3
const bool error = test.outcome != Outcome::kFailed ||
last->category ==
FailureCategory::kInfrastructure;
- const std::string_view element = error ? "error" : "failure";
- WriteIndent(output, 3);
- output << '<' << element;
- WriteAttribute(output, "message", SummarizeFailure(*last));
- WriteAttribute(output, "type", CategoryName(test.category));
- output << '>';
+ pugi::xml_node failure =
+ node.append_child(error ? "error" : "failure");
+ AddAttribute(failure, "message", SummarizeFailure(*last));
+ AddAttribute(failure, "type", CategoryName(test.category));
std::string details = DescribeFailure(*last);
for (const auto &finding : test.analysis)
{
@@ -9241,18 +9197,15 @@ namespace Progmasoft::Catch3
{
details += "replay: " + test.replayCommand + "\n";
}
- WriteEscaped(details, output, false);
- output << "</" << element << ">\n";
+ AddText(failure, details);
}
else if (test.outcome == Outcome::kSkipped ||
test.outcome == Outcome::kBlocked ||
test.outcome == Outcome::kNotRun ||
test.outcome == Outcome::kUpToDate)
{
- WriteIndent(output, 3);
- output << "<skipped";
- WriteAttribute(output, "message", SkipReason(test));
- output << "/>\n";
+ AddAttribute(
+ node.append_child("skipped"), "message", SkipReason(test));
}
// Every earlier failed attempt, so retries stay visible.
for (std::size_t index = 0; index + 1 < test.attempts.size();
@@ -9263,20 +9216,36 @@ namespace Progmasoft::Catch3
{
continue;
}
- WriteEarlierAttempt(output,
- attempt,
- failedRun ? "rerunFailure" : "flakyFailure");
+ AddEarlierAttempt(node,
+ attempt,
+ failedRun ? "rerunFailure" : "flakyFailure");
}
if (last != nullptr && !last->standardOutput.empty())
{
- WriteTextElement(output, 3, "system-out", Output(*last, false));
+ AddTextElement(node, "system-out", Output(*last, false));
}
if (last != nullptr && !Output(*last, true).empty())
{
- WriteTextElement(output, 3, "system-err", Output(*last, true));
+ AddTextElement(node, "system-err", Output(*last, true));
}
- WriteIndent(output, 2);
- output << "</testcase>\n";
+ }
+
+ void
+ AddCounts(pugi::xml_node node,
+ const RunResult &run,
+ const RunSummary &summary)
+ {
+ AddAttribute(node, "name", run.binary);
+ AddAttribute(node, "tests", std::to_string(summary.tests));
+ AddAttribute(node, "failures", std::to_string(summary.failed));
+ AddAttribute(node,
+ "errors",
+ std::to_string(summary.crashed + summary.timedOut));
+ AddAttribute(node,
+ "skipped",
+ std::to_string(summary.skipped + summary.blocked +
+ summary.notRun + summary.upToDate));
+ AddAttribute(node, "time", JUnitSeconds(run.wallMicroseconds));
}
} // namespace
@@ -9284,38 +9253,21 @@ namespace Progmasoft::Catch3
JUnitXmlWriter::Write(const RunResult &run, std::ostream &output)
{
const RunSummary summary = Summarize(run);
- const std::size_t errors = summary.crashed + summary.timedOut;
- const std::size_t failures = summary.failed;
- const std::size_t skipped =
- summary.skipped + summary.blocked + summary.notRun +
- summary.upToDate;
- const std::string time = JUnitSeconds(run.wallMicroseconds);
- output << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<testsuites";
- WriteAttribute(output, "name", run.binary);
- WriteAttribute(output, "tests", std::to_string(summary.tests));
- WriteAttribute(output, "failures", std::to_string(failures));
- WriteAttribute(output, "errors", std::to_string(errors));
- WriteAttribute(output, "skipped", std::to_string(skipped));
- WriteAttribute(output, "time", time);
- output << ">\n";
- WriteIndent(output, 1);
- output << "<testsuite";
- WriteAttribute(output, "name", run.binary);
- WriteAttribute(output, "tests", std::to_string(summary.tests));
- WriteAttribute(output, "failures", std::to_string(failures));
- WriteAttribute(output, "errors", std::to_string(errors));
- WriteAttribute(output, "skipped", std::to_string(skipped));
- WriteAttribute(output, "time", time);
- output << ">\n";
- WriteIndent(output, 2);
- output << "<properties>\n";
- const auto property = [&](std::string_view name,
- const std::string &value) {
- WriteIndent(output, 3);
- output << "<property";
- WriteAttribute(output, "name", name);
- WriteAttribute(output, "value", value);
- output << "/>\n";
+ pugi::xml_document document;
+ pugi::xml_node declaration =
+ document.append_child(pugi::node_declaration);
+ declaration.append_attribute("version").set_value("1.0");
+ declaration.append_attribute("encoding").set_value("UTF-8");
+
+ pugi::xml_node suites = document.append_child("testsuites");
+ AddCounts(suites, run, summary);
+ pugi::xml_node suite = suites.append_child("testsuite");
+ AddCounts(suite, run, summary);
+ pugi::xml_node properties = suite.append_child("properties");
+ const auto property = [&](const char *name, const std::string &value) {
+ pugi::xml_node node = properties.append_child("property");
+ AddAttribute(node, "name", name);
+ AddAttribute(node, "value", value);
};
property("random-seed", std::to_string(run.seed));
property("workers", std::to_string(run.workers));
@@ -9327,14 +9279,12 @@ namespace Progmasoft::Catch3
}
property("flaky", std::to_string(summary.flaky));
property("quarantined", std::to_string(summary.quarantined));
- WriteIndent(output, 2);
- output << "</properties>\n";
for (const auto &test : run.tests)
{
- WriteTestCase(output, test, run.binary);
+ AddTestCase(suite, test, run.binary);
}
- WriteIndent(output, 1);
- output << "</testsuite>\n</testsuites>\n";
+
+ document.save(output, " ", pugi::format_indent, pugi::encoding_utf8);
return static_cast<bool>(output);
}
@@ -12180,12 +12130,9 @@ namespace Progmasoft::Catch3::Runner
// Copyright (c) 2026 Progmasoft.
// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
-#include <clocale>
+
#include <cmath>
-#include <cstdio>
#include <cstdlib>
-#include <limits>
-#include <optional>
namespace Progmasoft::Catch3::Runner
{
@@ -12223,481 +12170,160 @@ namespace Progmasoft::Catch3::Runner
}
/// Documents nested deeper than this are refused, so a hostile or
- /// broken file cannot exhaust the stack of the parser.
+ /// broken file cannot exhaust the stack while it is converted.
constexpr int kMaximumDepth = 256;
- void
- AppendUtf8(std::string &output, std::uint32_t codePoint)
- {
- if (codePoint < 0x80)
- {
- output.push_back(static_cast<char>(codePoint));
- }
- else if (codePoint < 0x800)
- {
- output.push_back(static_cast<char>(0xC0 | (codePoint >> 6)));
- output.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
- }
- else if (codePoint < 0x10000)
- {
- output.push_back(static_cast<char>(0xE0 | (codePoint >> 12)));
- output.push_back(
- static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
- output.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
- }
- else
- {
- output.push_back(static_cast<char>(0xF0 | (codePoint >> 18)));
- output.push_back(
- static_cast<char>(0x80 | ((codePoint >> 12) & 0x3F)));
- output.push_back(
- static_cast<char>(0x80 | ((codePoint >> 6) & 0x3F)));
- output.push_back(static_cast<char>(0x80 | (codePoint & 0x3F)));
- }
- }
+ /// Strings are bytes: test names and output need not be UTF-8, and
+ /// they come back as they were written.
+ constexpr yyjson_read_flag kReadFlags =
+ YYJSON_READ_ALLOW_INVALID_UNICODE;
- /// Reads a JSON number exactly. The grammar is checked here; strtod
- /// does the rounding, with the point replaced by the decimal
- /// separator of the current locale, which a test may have changed.
- std::optional<double>
- ToDouble(std::string_view digits)
- {
- std::size_t index = 0;
- const auto skipDigits = [&] {
- const std::size_t start = index;
- while (index < digits.size() && digits[index] >= '0' &&
- digits[index] <= '9')
- {
- ++index;
- }
- return index > start;
- };
- if (index < digits.size() && digits[index] == '-')
- {
- ++index;
- }
- if (!skipDigits())
- {
- return std::nullopt;
- }
- std::size_t point = std::string_view::npos;
- if (index < digits.size() && digits[index] == '.')
- {
- point = index++;
- if (!skipDigits())
- {
- return std::nullopt;
- }
- }
- if (index < digits.size() &&
- (digits[index] == 'e' || digits[index] == 'E'))
- {
- ++index;
- if (index < digits.size() &&
- (digits[index] == '+' || digits[index] == '-'))
- {
- ++index;
- }
- if (!skipDigits())
- {
- return std::nullopt;
- }
- }
- if (index != digits.size())
- {
- return std::nullopt;
- }
- std::string copy{digits};
- if (point != std::string_view::npos)
- {
- const char *separator = std::localeconv()->decimal_point;
- copy.replace(point,
- 1,
- separator == nullptr || *separator == '\0'
- ? "."
- : separator);
- }
- return std::strtod(copy.c_str(), nullptr);
- }
+ /// JSON has no infinities; null is the conventional stand-in.
+ constexpr yyjson_write_flag kWriteFlags =
+ YYJSON_WRITE_ALLOW_INVALID_UNICODE |
+ YYJSON_WRITE_INF_AND_NAN_AS_NULL;
- class Parser final
+ /// Converts a value of yyjson's tree; false when it is nested too
+ /// deeply.
+ bool
+ FromYyjson(yyjson_val *value, int depth, JsonValue &result)
{
- public:
- explicit Parser(std::string_view text)
- : text_{text}
+ if (depth > kMaximumDepth)
{
+ return false;
}
-
- Expected<JsonValue>
- ParseDocument()
- {
- SkipWhitespace();
- auto value = ParseValue(0);
- if (!value)
- {
- return value;
- }
- SkipWhitespace();
- if (position_ != text_.size())
- {
- return Fail("unexpected text after the value");
- }
- return value;
- }
-
- private:
- Error
- Fail(const std::string &message) const
- {
- return Error{"invalid JSON at offset " +
- std::to_string(position_) + ": " + message};
- }
-
- void
- SkipWhitespace()
+ switch (yyjson_get_type(value))
{
- while (position_ < text_.size())
+ case YYJSON_TYPE_BOOL:
+ result = JsonValue{yyjson_get_bool(value)};
+ return true;
+ case YYJSON_TYPE_NUM:
+ result = JsonValue{yyjson_get_num(value)};
+ return true;
+ case YYJSON_TYPE_STR:
+ result = JsonValue{std::string_view{
+ yyjson_get_str(value), yyjson_get_len(value)}};
+ return true;
+ case YYJSON_TYPE_ARR:
{
- const char character = text_[position_];
- if (character != ' ' && character != '\t' &&
- character != '\n' && character != '\r')
+ JsonValue array = JsonValue::Array();
+ auto &items = array.MutableItems();
+ items.reserve(yyjson_arr_size(value));
+ yyjson_arr_iter iterator = yyjson_arr_iter_with(value);
+ while (yyjson_val *item = yyjson_arr_iter_next(&iterator))
{
- return;
+ items.emplace_back();
+ if (!FromYyjson(item, depth + 1, items.back()))
+ {
+ return false;
+ }
}
- ++position_;
- }
- }
-
- bool
- Consume(std::string_view word)
- {
- if (text_.substr(position_, word.size()) == word)
- {
- position_ += word.size();
+ result = std::move(array);
return true;
}
- return false;
- }
-
- Expected<JsonValue>
- ParseValue(int depth)
- {
- if (depth > kMaximumDepth)
- {
- return Fail("nested too deeply");
- }
- if (position_ >= text_.size())
- {
- return Fail("a value was expected");
- }
- const char character = text_[position_];
- if (character == '{')
- {
- return ParseObject(depth);
- }
- if (character == '[')
- {
- return ParseArray(depth);
- }
- if (character == '"')
+ case YYJSON_TYPE_OBJ:
{
- auto text = ParseString();
- if (!text)
+ JsonValue object = JsonValue::Object();
+ auto &members = object.MutableMembers();
+ members.reserve(yyjson_obj_size(value));
+ yyjson_obj_iter iterator = yyjson_obj_iter_with(value);
+ while (yyjson_val *key = yyjson_obj_iter_next(&iterator))
{
- return text.GetError();
+ members.emplace_back(
+ std::string{yyjson_get_str(key),
+ yyjson_get_len(key)},
+ JsonValue{});
+ if (!FromYyjson(yyjson_obj_iter_get_val(key),
+ depth + 1,
+ members.back().second))
+ {
+ return false;
+ }
}
- return JsonValue{std::move(*text)};
- }
- if (Consume("true"))
- {
- return JsonValue{true};
- }
- if (Consume("false"))
- {
- return JsonValue{false};
- }
- if (Consume("null"))
- {
- return JsonValue{};
+ result = std::move(object);
+ return true;
}
- return ParseNumber();
+ default:
+ result = JsonValue{};
+ return true;
}
+ }
- Expected<JsonValue>
- ParseNumber()
+ /// A whole number within the exact range of a double is written as
+ /// an integer, anything else as the shortest text that reads back as
+ /// the same double.
+ yyjson_mut_val *
+ ToYyjsonNumber(yyjson_mut_doc *document, double value)
+ {
+ if (std::isfinite(value) &&
+ std::fabs(value) < 9.007199254740992e15 &&
+ value == std::trunc(value))
{
- const std::size_t start = position_;
- if (position_ < text_.size() && text_[position_] == '-')
- {
- ++position_;
- }
- while (position_ < text_.size())
- {
- const char character = text_[position_];
- const bool numeric =
- (character >= '0' && character <= '9') ||
- character == '.' || character == 'e' ||
- character == 'E' || character == '+' ||
- character == '-';
- if (!numeric)
- {
- break;
- }
- ++position_;
- }
- if (position_ == start)
- {
- return Fail("a value was expected");
- }
- const std::string_view digits =
- text_.substr(start, position_ - start);
- const auto value = ToDouble(digits);
- if (!value)
- {
- position_ = start;
- return Fail("malformed number");
- }
- return JsonValue{*value};
+ return yyjson_mut_sint(document,
+ static_cast<std::int64_t>(value));
}
+ return yyjson_mut_real(document, value);
+ }
- Expected<std::uint32_t>
- ParseHexQuad()
+ /// Builds yyjson's tree of a value. Strings are referenced, not
+ /// copied: the tree is written before `value` can change.
+ yyjson_mut_val *
+ ToYyjson(yyjson_mut_doc *document, const JsonValue &value)
+ {
+ switch (value.GetKind())
{
- if (position_ + 4 > text_.size())
+ case JsonValue::Kind::kNull:
+ return yyjson_mut_null(document);
+ case JsonValue::Kind::kBoolean:
+ return yyjson_mut_bool(document, value.AsBoolean());
+ case JsonValue::Kind::kNumber:
+ return ToYyjsonNumber(document, value.AsNumber());
+ case JsonValue::Kind::kString:
{
- return Fail("truncated \\u escape");
+ const std::string &text = value.AsString();
+ return yyjson_mut_strn(document, text.data(), text.size());
}
- std::uint32_t value = 0;
- for (int index = 0; index < 4; ++index)
+ case JsonValue::Kind::kArray:
{
- const char character = text_[position_++];
- value <<= 4;
- if (character >= '0' && character <= '9')
- {
- value |= static_cast<std::uint32_t>(character - '0');
- }
- else if (character >= 'a' && character <= 'f')
+ yyjson_mut_val *array = yyjson_mut_arr(document);
+ for (const auto &item : value.Items())
{
- value |=
- static_cast<std::uint32_t>(character - 'a' + 10);
- }
- else if (character >= 'A' && character <= 'F')
- {
- value |=
- static_cast<std::uint32_t>(character - 'A' + 10);
- }
- else
- {
- return Fail("malformed \\u escape");
- }
- }
- return value;
- }
-
- Expected<std::string>
- ParseString()
- {
- ++position_; // The opening quote.
- std::string result;
- while (position_ < text_.size())
- {
- const char character = text_[position_++];
- if (character == '"')
- {
- return result;
- }
- if (static_cast<unsigned char>(character) < 0x20)
- {
- return Fail("control character in a string");
- }
- if (character != '\\')
- {
- result.push_back(character);
- continue;
- }
- if (position_ >= text_.size())
- {
- break;
- }
- const char escape = text_[position_++];
- switch (escape)
- {
- case '"':
- case '\\':
- case '/':
- result.push_back(escape);
- break;
- case 'b':
- result.push_back('\b');
- break;
- case 'f':
- result.push_back('\f');
- break;
- case 'n':
- result.push_back('\n');
- break;
- case 'r':
- result.push_back('\r');
- break;
- case 't':
- result.push_back('\t');
- break;
- case 'u':
- {
- auto high = ParseHexQuad();
- if (!high)
- {
- return high.GetError();
- }
- std::uint32_t codePoint = *high;
- if (codePoint >= 0xD800 && codePoint <= 0xDBFF &&
- Consume("\\u"))
- {
- auto low = ParseHexQuad();
- if (!low)
- {
- return low.GetError();
- }
- if (*low >= 0xDC00 && *low <= 0xDFFF)
- {
- codePoint = 0x10000 +
- ((codePoint - 0xD800) << 10) +
-