json_view: accept() checks the text without writing an index
json_document::accept() ran the builder that writes the node index and discarded the index. For input without comments and trailing commas it now runs a second instantiation of the builder, with a template parameter Store that is false: the grammar, number, string, UTF-8 and nesting checks are the same code, but no node is written, the container nodes are not patched when they close, the node array is not reserved, and no list of large objects is kept. What only builds the arguments of a node (integer conversion, the digit counts of floats) is not run either. accept() with comments or trailing commas and parse() are not changed; the builder that stores is the one parse() always used. A flag of the document passes the request from accept() to build(). accept() on twitter, citm_catalog, canada and the two minified files, A/B in one binary, 6 code layouts, time after / before (geometric mean of the five; the other 14 files of the set are faster too, none is slower): - Apple M1 Max, Apple clang 21: 0.89 - x86-64 Haswell, GCC 16.2: 0.75 - x86-64 Haswell, Clang 23: 0.78 (semanticscholar-corpus.json: 0.52 on both x86 compilers.) parse() runs the same instructions as before; its timings moved by up to 3 % on a few small files with unchanged instruction counts (code placement). Tests: accept() against json::accept() for the same inputs with all four option combinations and with and without a terminating NUL: 2,000 generated documents and damaged copies of them, nesting from 63 to 1000 levels (closed and not), objects with 300 members, a null byte in the text, and float overflow for a float document. Checked against a deliberately broken validator. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
6 files changed
+179
-27
No files matched your search
@@ -69,7 +69,8 @@ struct parse_failure
|
||||
};
|
||||
|
||||
/// FloatType: the number_float_t of the document, whose overflow parse() rejects
|
||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel>
|
||||
/// Store: false for accept(): the grammar, number, string and UTF-8 checks run as for parse(), but no node is written
|
||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Sentinel, bool Store>
|
||||
class builder
|
||||
{
|
||||
public:
|
||||
@@ -610,10 +611,13 @@ run_found:
|
||||
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE bool run()
|
||||
{
|
||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||
base = cold.m_doc.nodes;
|
||||
out = base;
|
||||
cap = base + cold.m_doc.node_capacity;
|
||||
if (enabled(Store))
|
||||
{
|
||||
cold.m_doc.reserve(estimate_nodes(reinterpret_cast<const char*>(b), static_cast<std::size_t>(e - b)));
|
||||
base = cold.m_doc.nodes;
|
||||
out = base;
|
||||
cap = base + cold.m_doc.node_capacity;
|
||||
}
|
||||
|
||||
if (e - p >= 3 && p[0] == 0xEF && p[1] == 0xBB && p[2] == 0xBF)
|
||||
{
|
||||
@@ -797,7 +801,10 @@ close_object:
|
||||
// an array pays nothing for this)
|
||||
if (NLOHMANN_VIEW_UNLIKELY(cur_count >= document_data::index_min_members))
|
||||
{
|
||||
cold.note_large_object(cur_idx);
|
||||
if (enabled(Store))
|
||||
{
|
||||
cold.note_large_object(cur_idx);
|
||||
}
|
||||
}
|
||||
|
||||
close_container:
|
||||
@@ -957,6 +964,17 @@ indent_done:
|
||||
/// an integer's value); two stores on little-endian targets
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE node* push_node(node_kind k, std::uint8_t flags, std::uint16_t extra, std::size_t off, std::uint64_t second)
|
||||
{
|
||||
if (!enabled(Store))
|
||||
{
|
||||
// accept(): nothing is stored, so what computes the arguments is not run either. base and out stay
|
||||
// null: their difference, which open() takes, is 0.
|
||||
static_cast<void>(k);
|
||||
static_cast<void>(flags);
|
||||
static_cast<void>(extra);
|
||||
static_cast<void>(off);
|
||||
static_cast<void>(second);
|
||||
return out;
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(out == cap))
|
||||
{
|
||||
const auto n = static_cast<std::size_t>(out - base);
|
||||
@@ -1015,9 +1033,12 @@ indent_done:
|
||||
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE void close()
|
||||
{
|
||||
node& n = base[cur_idx];
|
||||
n.len = cur_count;
|
||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||
if (enabled(Store))
|
||||
{
|
||||
node& n = base[cur_idx];
|
||||
n.len = cur_count;
|
||||
n.next = static_cast<std::uint32_t>(out - base) - cur_idx;
|
||||
}
|
||||
if (--depth != 0)
|
||||
{
|
||||
if (NLOHMANN_VIEW_LIKELY(depth <= 64))
|
||||
@@ -1192,17 +1213,17 @@ indent_done:
|
||||
};
|
||||
|
||||
/// run the builder with compile-time options
|
||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd>
|
||||
template<typename FloatType, bool Comments, bool TrailingCommas, bool NulIsEnd, bool Store = true>
|
||||
inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure)
|
||||
{
|
||||
if (sentinel)
|
||||
{
|
||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true> bld(d, src, size);
|
||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, true, Store> bld(d, src, size);
|
||||
const bool ok = bld.run();
|
||||
failure = bld.failure();
|
||||
return ok;
|
||||
}
|
||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false> bld(d, src, size);
|
||||
builder<FloatType, Comments, TrailingCommas, NulIsEnd, false, Store> bld(d, src, size);
|
||||
const bool ok = bld.run();
|
||||
failure = bld.failure();
|
||||
return ok;
|
||||
@@ -1213,6 +1234,11 @@ inline bool build_with(document_data& d, const char* src, std::size_t size, bool
|
||||
template<typename FloatType, bool NulIsEnd>
|
||||
inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure)
|
||||
{
|
||||
if (d.validate_only && !comments && !trailing_commas)
|
||||
{
|
||||
// accept() of plain JSON: nothing is stored (the other option sets keep the builder that stores)
|
||||
return build_with<FloatType, false, false, NulIsEnd, false>(d, src, size, sentinel, failure);
|
||||
}
|
||||
if (comments)
|
||||
{
|
||||
return trailing_commas ? build_with<FloatType, true, true, NulIsEnd>(d, src, size, sentinel, failure)
|
||||
|
||||
@@ -36,6 +36,7 @@ struct document_data
|
||||
const char* src = nullptr;
|
||||
std::size_t size = 0;
|
||||
node* nodes = nullptr; ///< the node index (see node.hpp)
|
||||
bool validate_only = false; ///< accept(): build() checks the text and stores no nodes
|
||||
std::size_t node_count = 0;
|
||||
std::size_t node_capacity = 0;
|
||||
node* inline_nodes = nullptr; ///< node array allocated together with this header
|
||||
|
||||
@@ -1088,6 +1088,7 @@ class basic_json_document
|
||||
static bool accept(InputType&& input, const bool ignore_comments = false, const bool ignore_trailing_commas = false)
|
||||
{
|
||||
basic_json_document d;
|
||||
d.m_validate_only = true; // (nothing is stored: only the result is used)
|
||||
d.read(std::forward<InputType>(input), false, ignore_comments, ignore_trailing_commas);
|
||||
return !d.is_discarded();
|
||||
}
|
||||
@@ -1532,7 +1533,9 @@ class basic_json_document
|
||||
}
|
||||
else
|
||||
{
|
||||
d.validate_only = m_validate_only;
|
||||
ok = detail::view::build < typename BasicJsonType::number_float_t, !detail::abi_config::strict_nul_handling > (d, src, size, comments, trailing_commas, sentinel, failure);
|
||||
d.validate_only = false;
|
||||
}
|
||||
if (NLOHMANN_VIEW_LIKELY(ok))
|
||||
{
|
||||
@@ -1693,6 +1696,7 @@ class basic_json_document
|
||||
}
|
||||
|
||||
std::unique_ptr<document_data, document_data::deleter> m_data{}; // NOLINT(readability-redundant-member-init)
|
||||
bool m_validate_only = false; ///< set by accept(): build() stores no nodes
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
Reference in new issue
Block a user