diff --git a/docs/mkdocs/docs/api/basic_json_document/accept.md b/docs/mkdocs/docs/api/basic_json_document/accept.md index c3cbf0f75..766fd20fa 100644 --- a/docs/mkdocs/docs/api/basic_json_document/accept.md +++ b/docs/mkdocs/docs/api/basic_json_document/accept.md @@ -13,12 +13,16 @@ function never throws an exception for invalid input, and the returned `#!cpp bo is returned. The result is the same as that of `BasicJsonType::accept()` by design; the difference is how the check is made. This -function runs the document's builder, which checks the text as [`parse()`](parse.md) does but writes no index (for -input without comments and trailing commas; with either option it builds the index and discards it), while +function runs the document's builder, which checks the text as [`parse()`](parse.md) does, while `BasicJsonType::accept()` runs the SAX parser with a handler that ignores every event. Use this one if you go on to parse the same input with [`parse()`](parse.md) anyway, so that both use the same code; either is fine for a plain yes/no answer. +When both `ignore_comments` and `ignore_trailing_commas` are `#!cpp false` (the default), the builder writes no index. +With either option set, it builds the index as [`parse()`](parse.md) does and discards it: the result is the same, only +the time differs. (Neither option needs the index; the variant that skips it exists only for the default options to +keep the code size down.) + ## Template parameters `InputType` diff --git a/include/nlohmann/detail/view/builder.hpp b/include/nlohmann/detail/view/builder.hpp index 9f96cbe00..90d435ae6 100644 --- a/include/nlohmann/detail/view/builder.hpp +++ b/include/nlohmann/detail/view/builder.hpp @@ -1051,11 +1051,6 @@ indent_done: { // 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(k); - static_cast(flags); - static_cast(extra); - static_cast(off); - static_cast(second); return out; } if (NLOHMANN_VIEW_UNLIKELY(out == cap)) @@ -1296,7 +1291,7 @@ indent_done: }; /// run the builder with compile-time options -template +template inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure) { if (sentinel) @@ -1313,22 +1308,27 @@ inline bool build_with(document_data& d, const char* src, std::size_t size, bool } /// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the -/// number_float_t of the document +/// number_float_t of the document; validate_only: accept(), only the result is +/// used template -inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure) +inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only, + parse_failure& failure) { - if (d.validate_only && !comments && !trailing_commas) + if (validate_only && !comments && !trailing_commas) { - // accept() of plain JSON: nothing is stored (the other option sets keep the builder that stores) + // accept() of plain JSON: nothing is stored. The other option sets + // could skip the stores just as well; they keep the builder that + // stores only so that there are two more instantiations of the parse + // loop, not eight. return build_with(d, src, size, sentinel, failure); } if (comments) { - return trailing_commas ? build_with(d, src, size, sentinel, failure) - : build_with(d, src, size, sentinel, failure); + return trailing_commas ? build_with(d, src, size, sentinel, failure) + : build_with(d, src, size, sentinel, failure); } - return trailing_commas ? build_with(d, src, size, sentinel, failure) - : build_with(d, src, size, sentinel, failure); + return trailing_commas ? build_with(d, src, size, sentinel, failure) + : build_with(d, src, size, sentinel, failure); } } // namespace view diff --git a/include/nlohmann/detail/view/document_data.hpp b/include/nlohmann/detail/view/document_data.hpp index 7cf902237..2c5f97767 100644 --- a/include/nlohmann/detail/view/document_data.hpp +++ b/include/nlohmann/detail/view/document_data.hpp @@ -58,7 +58,6 @@ struct document_data std::vector large_objects; ///< positions of the objects to index (noted while parsing) std::array base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena, edit arena, unused (indexed by flags & node_flags::storage) bool discarded = true; - bool validate_only = false; ///< accept(): build() checks the text and stores no nodes (next to `discarded`: no member moves) /// The storage of edits (editable documents only; see edit_storage.hpp). /// No edit moves or resizes the parsed index, and the nodes of values diff --git a/include/nlohmann/json_view.hpp b/include/nlohmann/json_view.hpp index 23e43f10c..097e489a4 100644 --- a/include/nlohmann/json_view.hpp +++ b/include/nlohmann/json_view.hpp @@ -1060,7 +1060,7 @@ class basic_json_document const bool ignore_trailing_commas = false) { basic_json_document d; - d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas); + d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas); return d; } @@ -1075,7 +1075,7 @@ class basic_json_document basic_json_document d; std::string buf; append_input(buf, std::forward(input)); - d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas); + d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas); return d; } @@ -1088,8 +1088,8 @@ 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(input), false, ignore_comments, ignore_trailing_commas); + d.read_kind(std::forward(input), on_error::validate, ignore_comments, ignore_trailing_commas, + std::integral_constant::value> {}); return !d.is_discarded(); } @@ -1105,7 +1105,7 @@ class basic_json_document const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - read_kind(std::forward(input), allow_exceptions, ignore_comments, ignore_trailing_commas, + read_kind(std::forward(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas, std::integral_constant::value> {}); } @@ -1464,6 +1464,22 @@ class basic_json_document return idx; } + /// what a failed parse does + enum class on_error : std::uint8_t + { + discard, ///< the document is discarded + raise, ///< parse_error is thrown + validate ///< accept(): the document is discarded, and no node is stored while parsing + }; + + static on_error error_mode(bool allow_exceptions) noexcept + { + return allow_exceptions ? on_error::raise : on_error::discard; + } + + /// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling + static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling; + /// create the storage (sized for the input) on first use void ensure_data(const char* src, std::size_t size) { @@ -1475,7 +1491,7 @@ class basic_json_document /// parse a buffer the document takes ownership of (it replaces the /// previous owned copy) - void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas) + void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas) { ensure_data(buf.data(), buf.size()); m_data->owned = std::move(buf); @@ -1485,7 +1501,7 @@ class basic_json_document /// parse a copy of the input; the copy is appended to the cleared owned /// buffer, so that its capacity is reused template - void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas) + void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas) { if (!m_data) { @@ -1506,7 +1522,7 @@ class basic_json_document } /// sentinel: src[size] is readable and 0 (std::string, C strings) - void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel) + void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel) { ensure_data(src, size); document_data& d = *m_data; @@ -1533,9 +1549,8 @@ 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; + ok = detail::view::build(d, src, size, comments, trailing_commas, sentinel, + allow_exceptions == on_error::validate, failure); } if (NLOHMANN_VIEW_LIKELY(ok)) { @@ -1548,7 +1563,7 @@ class basic_json_document return; } std::vector().swap(d.large_objects); - if (allow_exceptions) + if (allow_exceptions == on_error::raise) { detail::view::throw_parse_failure(failure, src, size, comments, trailing_commas); } @@ -1556,13 +1571,13 @@ class basic_json_document // --- input dispatch (see detail::view::input_kind) --- - void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::move(s), ae, c, tc); } template - void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { if (s == nullptr) { @@ -1576,7 +1591,7 @@ class basic_json_document } template - void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { using CharT = typename std::remove_cv::type>::type; const std::size_t n = std::extent::value; @@ -1587,7 +1602,7 @@ class basic_json_document } template - void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel const auto size = static_cast(s.size()); @@ -1596,13 +1611,13 @@ class basic_json_document } template - void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_copy(s, ae, c, tc); } template - void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { if (s == nullptr) { @@ -1614,13 +1629,13 @@ class basic_json_document } template - void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_copy(std::forward(input), ae, c, tc); } template - void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc) + void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc) { using value_type = typename std::remove_cv::value_type>::type; // borrow the range where the library's input adapter scans it as one @@ -1631,14 +1646,14 @@ class basic_json_document } template - void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/) + void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/) { const auto size = static_cast(std::distance(first, last)); build(size == 0 ? "" : reinterpret_cast(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template - void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/) + void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/) { build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc); } @@ -1696,7 +1711,6 @@ class basic_json_document } std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) - bool m_validate_only = false; ///< set by accept(): build() stores no nodes }; /*! diff --git a/single_include/nlohmann/json_view.hpp b/single_include/nlohmann/json_view.hpp index 24938d0b3..e410ef3a5 100644 --- a/single_include/nlohmann/json_view.hpp +++ b/single_include/nlohmann/json_view.hpp @@ -389,7 +389,6 @@ struct document_data std::vector large_objects; ///< positions of the objects to index (noted while parsing) std::array base = {{nullptr, nullptr, nullptr, nullptr}}; ///< string bases: source, arena, edit arena, unused (indexed by flags & node_flags::storage) bool discarded = true; - bool validate_only = false; ///< accept(): build() checks the text and stores no nodes (next to `discarded`: no member moves) /// The storage of edits (editable documents only; see edit_storage.hpp). /// No edit moves or resizes the parsed index, and the nodes of values @@ -2294,11 +2293,6 @@ indent_done: { // 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(k); - static_cast(flags); - static_cast(extra); - static_cast(off); - static_cast(second); return out; } if (NLOHMANN_VIEW_UNLIKELY(out == cap)) @@ -2539,7 +2533,7 @@ indent_done: }; /// run the builder with compile-time options -template +template inline bool build_with(document_data& d, const char* src, std::size_t size, bool sentinel, parse_failure& failure) { if (sentinel) @@ -2556,22 +2550,27 @@ inline bool build_with(document_data& d, const char* src, std::size_t size, bool } /// sentinel: src[size] is readable and 0 (e.g. std::string); FloatType: the -/// number_float_t of the document +/// number_float_t of the document; validate_only: accept(), only the result is +/// used template -inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, parse_failure& failure) +inline bool build(document_data& d, const char* src, std::size_t size, bool comments, bool trailing_commas, bool sentinel, bool validate_only, + parse_failure& failure) { - if (d.validate_only && !comments && !trailing_commas) + if (validate_only && !comments && !trailing_commas) { - // accept() of plain JSON: nothing is stored (the other option sets keep the builder that stores) + // accept() of plain JSON: nothing is stored. The other option sets + // could skip the stores just as well; they keep the builder that + // stores only so that there are two more instantiations of the parse + // loop, not eight. return build_with(d, src, size, sentinel, failure); } if (comments) { - return trailing_commas ? build_with(d, src, size, sentinel, failure) - : build_with(d, src, size, sentinel, failure); + return trailing_commas ? build_with(d, src, size, sentinel, failure) + : build_with(d, src, size, sentinel, failure); } - return trailing_commas ? build_with(d, src, size, sentinel, failure) - : build_with(d, src, size, sentinel, failure); + return trailing_commas ? build_with(d, src, size, sentinel, failure) + : build_with(d, src, size, sentinel, failure); } } // namespace view @@ -10211,7 +10210,7 @@ class basic_json_document const bool ignore_trailing_commas = false) { basic_json_document d; - d.read_range(first, last, allow_exceptions, ignore_comments, ignore_trailing_commas); + d.read_range(first, last, error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas); return d; } @@ -10226,7 +10225,7 @@ class basic_json_document basic_json_document d; std::string buf; append_input(buf, std::forward(input)); - d.build_owned(std::move(buf), allow_exceptions, ignore_comments, ignore_trailing_commas); + d.build_owned(std::move(buf), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas); return d; } @@ -10239,8 +10238,8 @@ 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(input), false, ignore_comments, ignore_trailing_commas); + d.read_kind(std::forward(input), on_error::validate, ignore_comments, ignore_trailing_commas, + std::integral_constant::value> {}); return !d.is_discarded(); } @@ -10256,7 +10255,7 @@ class basic_json_document const bool ignore_comments = false, const bool ignore_trailing_commas = false) { - read_kind(std::forward(input), allow_exceptions, ignore_comments, ignore_trailing_commas, + read_kind(std::forward(input), error_mode(allow_exceptions), ignore_comments, ignore_trailing_commas, std::integral_constant::value> {}); } @@ -10615,6 +10614,22 @@ class basic_json_document return idx; } + /// what a failed parse does + enum class on_error : std::uint8_t + { + discard, ///< the document is discarded + raise, ///< parse_error is thrown + validate ///< accept(): the document is discarded, and no node is stored while parsing + }; + + static on_error error_mode(bool allow_exceptions) noexcept + { + return allow_exceptions ? on_error::raise : on_error::discard; + } + + /// NulIsEnd of the builder: a null byte ends the input unless strict_nul_handling + static constexpr bool nul_is_end = !detail::abi_config::strict_nul_handling; + /// create the storage (sized for the input) on first use void ensure_data(const char* src, std::size_t size) { @@ -10626,7 +10641,7 @@ class basic_json_document /// parse a buffer the document takes ownership of (it replaces the /// previous owned copy) - void build_owned(std::string&& buf, bool allow_exceptions, bool comments, bool trailing_commas) + void build_owned(std::string&& buf, on_error allow_exceptions, bool comments, bool trailing_commas) { ensure_data(buf.data(), buf.size()); m_data->owned = std::move(buf); @@ -10636,7 +10651,7 @@ class basic_json_document /// parse a copy of the input; the copy is appended to the cleared owned /// buffer, so that its capacity is reused template - void build_copy(T&& input, bool allow_exceptions, bool comments, bool trailing_commas) + void build_copy(T&& input, on_error allow_exceptions, bool comments, bool trailing_commas) { if (!m_data) { @@ -10657,7 +10672,7 @@ class basic_json_document } /// sentinel: src[size] is readable and 0 (std::string, C strings) - void build(const char* src, std::size_t size, bool allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel) + void build(const char* src, std::size_t size, on_error allow_exceptions, bool comments, bool trailing_commas, bool owned, bool sentinel) { ensure_data(src, size); document_data& d = *m_data; @@ -10684,9 +10699,8 @@ 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; + ok = detail::view::build(d, src, size, comments, trailing_commas, sentinel, + allow_exceptions == on_error::validate, failure); } if (NLOHMANN_VIEW_LIKELY(ok)) { @@ -10699,7 +10713,7 @@ class basic_json_document return; } std::vector().swap(d.large_objects); - if (allow_exceptions) + if (allow_exceptions == on_error::raise) { detail::view::throw_parse_failure(failure, src, size, comments, trailing_commas); } @@ -10707,13 +10721,13 @@ class basic_json_document // --- input dispatch (see detail::view::input_kind) --- - void read_kind(std::string&& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(std::string&& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_owned(std::move(s), ae, c, tc); } template - void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { if (s == nullptr) { @@ -10727,7 +10741,7 @@ class basic_json_document } template - void read_kind(Array& a, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(Array& a, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { using CharT = typename std::remove_cv::type>::type; const std::size_t n = std::extent::value; @@ -10738,7 +10752,7 @@ class basic_json_document } template - void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { // std::basic_string guarantees data()[size()] == 0: use it as sentinel const auto size = static_cast(s.size()); @@ -10747,13 +10761,13 @@ class basic_json_document } template - void read_kind(const T& s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(const T& s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_copy(s, ae, c, tc); } template - void read_kind(CharT* s, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(CharT* s, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { if (s == nullptr) { @@ -10765,13 +10779,13 @@ class basic_json_document } template - void read_kind(T&& input, bool ae, bool c, bool tc, std::integral_constant /*unused*/) + void read_kind(T&& input, on_error ae, bool c, bool tc, std::integral_constant /*unused*/) { build_copy(std::forward(input), ae, c, tc); } template - void read_range(IteratorType first, IteratorType last, bool ae, bool c, bool tc) + void read_range(IteratorType first, IteratorType last, on_error ae, bool c, bool tc) { using value_type = typename std::remove_cv::value_type>::type; // borrow the range where the library's input adapter scans it as one @@ -10782,14 +10796,14 @@ class basic_json_document } template - void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::true_type /*contiguous bytes*/) + void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::true_type /*contiguous bytes*/) { const auto size = static_cast(std::distance(first, last)); build(size == 0 ? "" : reinterpret_cast(&*first), size, ae, c, tc, false, size == 0); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } template - void read_range_impl(IteratorType first, IteratorType last, bool ae, bool c, bool tc, std::false_type /*other*/) + void read_range_impl(IteratorType first, IteratorType last, on_error ae, bool c, bool tc, std::false_type /*other*/) { build_copy(detail::view::make_adapter_input(detail::input_adapter(first, last)), ae, c, tc); } @@ -10847,7 +10861,6 @@ class basic_json_document } std::unique_ptr m_data{}; // NOLINT(readability-redundant-member-init) - bool m_validate_only = false; ///< set by accept(): build() stores no nodes }; /*! diff --git a/tests/src/unit-json_view_builder.cpp b/tests/src/unit-json_view_builder.cpp index c0968d59e..c41b5022f 100644 --- a/tests/src/unit-json_view_builder.cpp +++ b/tests/src/unit-json_view_builder.cpp @@ -59,7 +59,7 @@ built build(const std::string& text, bool comments, bool trailing_commas, bool s r.copy.assign(text.begin(), text.end()); src = r.copy.data(); } - r.ok = nlohmann::detail::view::build < FloatType, !nlohmann::detail::abi_config::strict_nul_handling > (*r.data, src, text.size(), comments, trailing_commas, sentinel, r.failure); + r.ok = nlohmann::detail::view::build < FloatType, !nlohmann::detail::abi_config::strict_nul_handling > (*r.data, src, text.size(), comments, trailing_commas, sentinel, false, r.failure); r.data->src = src; r.data->base[0] = src; r.data->base[1] = r.data->arena.data();