Merge branch 'json-view/10-view-document' into json-view/11-view-access

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-29 22:39:38 +02:00
5 changed files with 55 additions and 17 deletions
+11 -3
View File
@@ -418,6 +418,14 @@ class builder
return w; return w;
} }
/// a compile-time option as a runtime condition: testing the template
/// argument directly makes a condition like `TrailingCommas && c == ']'`
/// constant when the option is off, which MSVC reports as C4127
static NLOHMANN_VIEW_ALWAYS_INLINE bool enabled(bool option) noexcept
{
return option;
}
/// The parse state and the parser proper. The cursor is a local object of /// The parse state and the parser proper. The cursor is a local object of
/// run() whose address never escapes (everything it calls out of line is a /// run() whose address never escapes (everything it calls out of line is a
/// member of the builder and gets the positions it needs), so that the /// member of the builder and gets the positions it needs), so that the
@@ -547,7 +555,7 @@ arr_next:
{ {
return false; return false;
} }
if (TrailingCommas && cur() == ']') if (enabled(TrailingCommas) && cur() == ']')
{ {
++p; ++p;
goto close_container; goto close_container;
@@ -614,7 +622,7 @@ obj_next:
{ {
return false; return false;
} }
if (TrailingCommas && cur() == '}') if (enabled(TrailingCommas) && cur() == '}')
{ {
++p; ++p;
goto close_container; goto close_container;
@@ -756,7 +764,7 @@ indent_done:
{ {
++p; ++p;
} }
if (Comments && cur() == '/') if (enabled(Comments) && cur() == '/')
{ {
const unsigned char* const q = cold.comment(p); const unsigned char* const q = cold.comment(p);
if (q == nullptr) if (q == nullptr)
+11 -3
View File
@@ -956,6 +956,14 @@ class builder
return w; return w;
} }
/// a compile-time option as a runtime condition: testing the template
/// argument directly makes a condition like `TrailingCommas && c == ']'`
/// constant when the option is off, which MSVC reports as C4127
static NLOHMANN_VIEW_ALWAYS_INLINE bool enabled(bool option) noexcept
{
return option;
}
/// The parse state and the parser proper. The cursor is a local object of /// The parse state and the parser proper. The cursor is a local object of
/// run() whose address never escapes (everything it calls out of line is a /// run() whose address never escapes (everything it calls out of line is a
/// member of the builder and gets the positions it needs), so that the /// member of the builder and gets the positions it needs), so that the
@@ -1085,7 +1093,7 @@ arr_next:
{ {
return false; return false;
} }
if (TrailingCommas && cur() == ']') if (enabled(TrailingCommas) && cur() == ']')
{ {
++p; ++p;
goto close_container; goto close_container;
@@ -1152,7 +1160,7 @@ obj_next:
{ {
return false; return false;
} }
if (TrailingCommas && cur() == '}') if (enabled(TrailingCommas) && cur() == '}')
{ {
++p; ++p;
goto close_container; goto close_container;
@@ -1294,7 +1302,7 @@ indent_done:
{ {
++p; ++p;
} }
if (Comments && cur() == '/') if (enabled(Comments) && cur() == '/')
{ {
const unsigned char* const q = cold.comment(p); const unsigned char* const q = cold.comment(p);
if (q == nullptr) if (q == nullptr)
+15 -6
View File
@@ -1465,7 +1465,8 @@ TEST_CASE("Eisel-Lemire float conversion")
// than the distance to the rounding boundary, so it must not change // than the distance to the rounding boundary, so it must not change
std::string longer = token; std::string longer = token;
const std::size_t e = longer.find('e'); const std::size_t e = longer.find('e');
const std::string extra = longer.find('.') == std::string::npos ? ".000000000000000000001" : "000000000000000000001"; const std::size_t dot = longer.find('.');
const std::string extra = dot == std::string::npos ? ".000000000000000000001" : "000000000000000000001";
longer.insert(e == std::string::npos ? longer.size() : e, extra); longer.insert(e == std::string::npos ? longer.size() : e, extra);
CAPTURE(longer); CAPTURE(longer);
if (eisel_lemire(longer, out)) if (eisel_lemire(longer, out))
@@ -1488,7 +1489,8 @@ TEST_CASE("Eisel-Lemire float conversion")
CHECK(bits_of(json::parse("-65.613616999999977").get<double>()) == bits_of(-65.613616999999977)); CHECK(bits_of(json::parse("-65.613616999999977").get<double>()) == bits_of(-65.613616999999977));
CHECK(bits_of(json::parse("2.2250738585072011e-308").get<double>()) == 0x000FFFFFFFFFFFFFu); CHECK(bits_of(json::parse("2.2250738585072011e-308").get<double>()) == 0x000FFFFFFFFFFFFFu);
CHECK(bits_of(json::parse("4.9406564584124654e-324").get<double>()) == 1u); CHECK(bits_of(json::parse("4.9406564584124654e-324").get<double>()) == 1u);
CHECK_THROWS_WITH_AS(json::parse("1.7976931348623159e308"), json _;
CHECK_THROWS_WITH_AS(_ = json::parse("1.7976931348623159e308"),
"[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&); "[json.exception.out_of_range.406] number overflow parsing '1.7976931348623159e308'", json::out_of_range&);
} }
} }
@@ -1555,16 +1557,23 @@ TEST_CASE("string scanning kernels")
state ^= state << 17u; state ^= state << 17u;
return state; return state;
}; };
// the upper half as a 32-bit value: converts to std::size_t implicitly on
// every platform (a cast of std::uint64_t is useless where both are the
// same type, and required where std::size_t is 32 bits wide)
const auto next_small = [&next]()
{
return static_cast<std::uint32_t>(next() >> 32u);
};
for (int round = 0; round < 100000; ++round) for (int round = 0; round < 100000; ++round)
{ {
// mostly ordinary text, so that runs span several words // mostly ordinary text, so that runs span several words
std::string text(static_cast<std::size_t>(next() % 8), '.'); std::string text(next_small() % 8u, '.');
const auto count = static_cast<std::size_t>(next() % 12); const std::size_t count = next_small() % 12u;
for (std::size_t k = 0; k < count; ++k) for (std::size_t k = 0; k < count; ++k)
{ {
const std::size_t p = (next() % 4 == 0) ? static_cast<std::size_t>(next() % pieces.size()) : 0; const std::size_t p = (next() % 4 == 0) ? next_small() % pieces.size() : 0;
text += pieces[p]; text += pieces[p];
text += std::string(static_cast<std::size_t>(next() % 10), 'x'); text += std::string(next_small() % 10u, 'x');
} }
const auto* data = reinterpret_cast<const unsigned char*>(text.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) const auto* data = reinterpret_cast<const unsigned char*>(text.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
for (std::size_t offset = 0; offset < 3 && offset <= text.size(); ++offset) for (std::size_t offset = 0; offset < 3 && offset <= text.size(); ++offset)
+12 -3
View File
@@ -32,6 +32,7 @@ using nlohmann::ordered_json_view;
namespace namespace
{ {
#if !defined(JSON_NOEXCEPTION)
// the exception parse() throws for a text, or "" if it accepts it // the exception parse() throws for a text, or "" if it accepts it
std::string parse_exception(const std::string& text, bool comments = false, bool trailing_commas = false) std::string parse_exception(const std::string& text, bool comments = false, bool trailing_commas = false)
{ {
@@ -60,6 +61,7 @@ std::string view_exception(const std::string& text, bool comments = false, bool
} }
return ""; return "";
} }
#endif
// a small deterministic generator of documents // a small deterministic generator of documents
struct generator struct generator
@@ -149,7 +151,7 @@ TEST_CASE("json_view")
CHECK(v.materialize() == j); CHECK(v.materialize() == j);
} }
const json_view invalid; const json_view invalid{};
CHECK(invalid.is_discarded()); CHECK(invalid.is_discarded());
CHECK(!static_cast<bool>(invalid)); CHECK(!static_cast<bool>(invalid));
CHECK(invalid.type() == json::value_t::discarded); CHECK(invalid.type() == json::value_t::discarded);
@@ -194,9 +196,11 @@ TEST_CASE("json_view")
}) })
{ {
CAPTURE(text); CAPTURE(text);
#if !defined(JSON_NOEXCEPTION)
const std::string expected = parse_exception(text); const std::string expected = parse_exception(text);
REQUIRE(!expected.empty()); REQUIRE(!expected.empty());
CHECK(view_exception(text) == expected); CHECK(view_exception(text) == expected);
#endif
CHECK(!json_document::accept(text)); CHECK(!json_document::accept(text));
const json_document d = json_document::parse(text, false); const json_document d = json_document::parse(text, false);
CHECK(d.is_discarded()); CHECK(d.is_discarded());
@@ -204,8 +208,9 @@ TEST_CASE("json_view")
CHECK(d.node_count() == 0); CHECK(d.node_count() == 0);
} }
// the exception types // the exception types
CHECK_THROWS_AS(json_document::parse("[1,"), json::parse_error&); json_document _;
CHECK_THROWS_AS(json_document::parse("1e400"), json::out_of_range&); CHECK_THROWS_AS(_ = json_document::parse("[1,"), json::parse_error&);
CHECK_THROWS_AS(_ = json_document::parse("1e400"), json::out_of_range&);
} }
SECTION("parse options") SECTION("parse options")
@@ -220,7 +225,9 @@ TEST_CASE("json_view")
const bool comments = (options & 1) != 0; const bool comments = (options & 1) != 0;
const bool trailing_commas = (options & 2) != 0; const bool trailing_commas = (options & 2) != 0;
CHECK(json_document::accept(text, comments, trailing_commas) == json::accept(text, comments, trailing_commas)); CHECK(json_document::accept(text, comments, trailing_commas) == json::accept(text, comments, trailing_commas));
#if !defined(JSON_NOEXCEPTION)
CHECK(view_exception(text, comments, trailing_commas) == parse_exception(text, comments, trailing_commas)); CHECK(view_exception(text, comments, trailing_commas) == parse_exception(text, comments, trailing_commas));
#endif
} }
} }
} }
@@ -232,7 +239,9 @@ TEST_CASE("json_view")
const std::string nul_in_comment("[1, // c\0\n2]", 12); const std::string nul_in_comment("[1, // c\0\n2]", 12);
CHECK(json_document::accept(nul_in_comment, true) == json::accept(nul_in_comment, true)); CHECK(json_document::accept(nul_in_comment, true) == json::accept(nul_in_comment, true));
CHECK(json_document::parse("\xEF\xBB\xBF[1]").root().materialize() == json::parse("\xEF\xBB\xBF[1]")); CHECK(json_document::parse("\xEF\xBB\xBF[1]").root().materialize() == json::parse("\xEF\xBB\xBF[1]"));
#if !defined(JSON_NOEXCEPTION)
CHECK(view_exception("\xEF\xBB") == parse_exception("\xEF\xBB")); CHECK(view_exception("\xEF\xBB") == parse_exception("\xEF\xBB"));
#endif
} }
SECTION("inputs") SECTION("inputs")
+6 -2
View File
@@ -9,8 +9,12 @@
#include "doctest_compatibility.h" #include "doctest_compatibility.h"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <nlohmann/detail/view/builder.hpp> #if JSON_TEST_USING_MULTIPLE_HEADERS
#include <nlohmann/detail/view/string_ref.hpp> #include <nlohmann/detail/view/builder.hpp>
#include <nlohmann/detail/view/string_ref.hpp>
#else
#include <nlohmann/json_view.hpp> // the single header contains the internal headers
#endif
using nlohmann::json; using nlohmann::json;
#include <cstdint> #include <cstdint>