diff --git a/BUILD.bazel b/BUILD.bazel index f0223e2a9..945ac488e 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -66,6 +66,12 @@ cc_library( "include/nlohmann/detail/string_escape.hpp", "include/nlohmann/detail/string_utils.hpp", "include/nlohmann/detail/value_t.hpp", + "include/nlohmann/detail/view/document_data.hpp", + "include/nlohmann/detail/view/macro_scope.hpp", + "include/nlohmann/detail/view/macro_unscope.hpp", + "include/nlohmann/detail/view/node.hpp", + "include/nlohmann/detail/view/scan.hpp", + "include/nlohmann/detail/view/string_ref.hpp", "include/nlohmann/json.hpp", "include/nlohmann/json_fwd.hpp", "include/nlohmann/ordered_map.hpp", diff --git a/include/nlohmann/detail/view/document_data.hpp b/include/nlohmann/detail/view/document_data.hpp new file mode 100644 index 000000000..294cc7bc4 --- /dev/null +++ b/include/nlohmann/detail/view/document_data.hpp @@ -0,0 +1,128 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +#include // size_t +#include // memcpy +#include // operator new, placement new +#include // string + +#include +#include +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +namespace view +{ + +/// storage of a parsed document; heap-allocated (header and an initial node +/// array in one block) so that views survive moves of the owning document +struct document_data +{ + const char* src = nullptr; + std::size_t size = 0; + node* tape = nullptr; + std::size_t tape_size = 0; + std::size_t tape_cap = 0; + node* inline_tape = nullptr; ///< node array allocated together with this header + std::size_t inline_cap = 0; + std::string arena{}; ///< decoded strings that contained escapes + std::string owned{}; ///< owned copy of the input, if any + const char* base[4] = {nullptr, nullptr, nullptr, nullptr}; ///< string bases: source, arena (indexed by flags & node_flags::storage) + bool discarded = true; + + /// one allocation for the header and room for `nodes` nodes; large + /// documents get a separate node array instead (so it can be trimmed) + static document_data* create(std::size_t nodes) + { + nodes = nodes <= 256 ? nodes : 0; + void* mem = ::operator new (sizeof(document_data) + (nodes * sizeof(node))); + auto* d = new (mem) document_data(); + d->inline_tape = static_cast(static_cast(static_cast(mem) + sizeof(document_data))); // (aligned: sizeof is a multiple of the alignment) + d->inline_cap = nodes; + d->tape = d->inline_tape; + d->tape_cap = nodes; + return d; + } + + struct deleter + { + void operator()(document_data* d) const noexcept + { + d->~document_data(); + ::operator delete (d); + } + }; + + document_data() noexcept = default; + document_data(const document_data&) = delete; + document_data(document_data&&) = delete; + document_data& operator=(const document_data&) = delete; + document_data& operator=(document_data&&) = delete; + ~document_data() + { + release(); + } + + void release() noexcept + { + if (tape != inline_tape) + { + ::operator delete (tape); + } + tape = inline_tape; + tape_cap = inline_cap; + } + + /// make room for n nodes; keeps the first tape_size nodes + void reserve(std::size_t n) + { + if (n <= tape_cap) + { + return; + } + node* fresh = static_cast(::operator new (n * sizeof(node))); + if (tape_size != 0) + { + std::memcpy(fresh, tape, tape_size * sizeof(node)); + } + release(); + tape = fresh; + tape_cap = n; + } + + const char* str(const node& n) const noexcept + { + return base[n.flags & node_flags::storage] + n.off; + } + + /// the node after n's subtree (containers span `next` nodes, scalars one) + static NLOHMANN_VIEW_ALWAYS_INLINE const node* after(const node* n) noexcept + { + return n + (is_container(*n) ? n->next : 1u); + } + + /// first element (array) or first key (object) of a container + static NLOHMANN_VIEW_ALWAYS_INLINE const node* first_child(const node* n) noexcept + { + return n + 1; + } + + /// end of the elements of a container + static NLOHMANN_VIEW_ALWAYS_INLINE const node* child_end(const node* n) noexcept + { + return n + n->next; + } +}; + +} // namespace view +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/view/macro_scope.hpp b/include/nlohmann/detail/view/macro_scope.hpp new file mode 100644 index 000000000..0aad401ac --- /dev/null +++ b/include/nlohmann/detail/view/macro_scope.hpp @@ -0,0 +1,61 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +// Macros of json_view.hpp and its detail headers. json.hpp undefines its own +// macros at its end (macro_unscope.hpp), so the view defines the few it needs +// under its own prefix; json_view.hpp undefines them all at its end +// (detail/view/macro_unscope.hpp). Configuration that json.hpp undefines is +// read from detail::abi_config instead. + +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + #define NLOHMANN_VIEW_HAS_CPP_17 1 +#else + #define NLOHMANN_VIEW_HAS_CPP_17 0 +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define NLOHMANN_VIEW_LIKELY(x) __builtin_expect(!!(x), 1) + #define NLOHMANN_VIEW_UNLIKELY(x) __builtin_expect(!!(x), 0) + #define NLOHMANN_VIEW_ALWAYS_INLINE inline __attribute__((always_inline)) + #define NLOHMANN_VIEW_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) + #define NLOHMANN_VIEW_LIKELY(x) (x) + #define NLOHMANN_VIEW_UNLIKELY(x) (x) + #define NLOHMANN_VIEW_ALWAYS_INLINE __forceinline + #define NLOHMANN_VIEW_NOINLINE __declspec(noinline) +#else + #define NLOHMANN_VIEW_LIKELY(x) (x) + #define NLOHMANN_VIEW_UNLIKELY(x) (x) + #define NLOHMANN_VIEW_ALWAYS_INLINE inline + #define NLOHMANN_VIEW_NOINLINE +#endif + +// exceptions as in json.hpp (JSON_NOEXCEPTION, JSON_THROW_USER) +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define NLOHMANN_VIEW_THROW(exception) throw exception +#else + #include + #define NLOHMANN_VIEW_THROW(exception) std::abort() +#endif +#if defined(JSON_THROW_USER) + #undef NLOHMANN_VIEW_THROW + #define NLOHMANN_VIEW_THROW JSON_THROW_USER +#endif + +// the parser stores a node's first word at once where the layout of `node` is +// known to be little-endian (MSVC targets are); elsewhere field by field +#if (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(_MSC_VER) + #define NLOHMANN_VIEW_LITTLE_ENDIAN 1 +#else + #define NLOHMANN_VIEW_LITTLE_ENDIAN 0 +#endif + +/// sixteen checks at fixed offsets 0..15 +#define NLOHMANN_VIEW_REPEAT16(X) X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) diff --git a/include/nlohmann/detail/view/macro_unscope.hpp b/include/nlohmann/detail/view/macro_unscope.hpp new file mode 100644 index 000000000..c3a6266a8 --- /dev/null +++ b/include/nlohmann/detail/view/macro_unscope.hpp @@ -0,0 +1,20 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +// undefine the macros of detail/view/macro_scope.hpp (at the end of json_view.hpp) + +#undef NLOHMANN_VIEW_HAS_CPP_17 +#undef NLOHMANN_VIEW_LIKELY +#undef NLOHMANN_VIEW_UNLIKELY +#undef NLOHMANN_VIEW_ALWAYS_INLINE +#undef NLOHMANN_VIEW_NOINLINE +#undef NLOHMANN_VIEW_THROW +#undef NLOHMANN_VIEW_LITTLE_ENDIAN +#undef NLOHMANN_VIEW_REPEAT16 diff --git a/include/nlohmann/detail/view/node.hpp b/include/nlohmann/detail/view/node.hpp new file mode 100644 index 000000000..471c1f844 --- /dev/null +++ b/include/nlohmann/detail/view/node.hpp @@ -0,0 +1,97 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy + +#include +#include + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +namespace view +{ + +// the node kinds are value_t values; the tests of is_container() and of the +// number kinds depend on this numbering +static_assert(static_cast(value_t::null) == 0 && static_cast(value_t::object) == 1 + && static_cast(value_t::array) == 2 && static_cast(value_t::string) == 3 + && static_cast(value_t::boolean) == 4 && static_cast(value_t::number_integer) == 5 + && static_cast(value_t::number_unsigned) == 6 && static_cast(value_t::number_float) == 7, + "the node format depends on the numbering of value_t"); + +/// node flags +struct node_flags +{ + static constexpr std::uint8_t escaped = 1; ///< string payload lives in the decode arena, not the source + static constexpr std::uint8_t storage = 3; ///< mask: where a string or number token lives (index into document_data::base) + static constexpr std::uint8_t is_true = 4; ///< boolean value +}; + +/// One entry of the flat index, in document order. An object's members are +/// stored as key node followed by the value's subtree. Integers keep their +/// converted 64-bit value in the len/next bytes (the node after a scalar is +/// always the next one, and the token length follows from `extra`). +struct node +{ + std::uint8_t kind; ///< value_t + std::uint8_t flags; ///< node_flags + std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; otherwise 0 + std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if node_flags::escaped + std::uint32_t len; ///< string: decoded bytes; float: token bytes; array/object: element count + std::uint32_t next; ///< array/object: number of nodes of the subtree (its extent in the enclosing sequence) +}; +static_assert(sizeof(node) == 16, "node must stay 16 bytes"); + +NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept +{ + return static_cast(n.kind) - 1u <= 1u; +} + +/// the converted value of an integer node (stored in len/next) +NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t integer_bits(const node& n) noexcept +{ + std::uint64_t v = 0; + std::memcpy(&v, reinterpret_cast(&n) + 8, 8); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + return v; +} + +NLOHMANN_VIEW_ALWAYS_INLINE void set_integer_bits(node& n, std::uint64_t v) noexcept +{ + std::memcpy(reinterpret_cast(&n) + 8, &v, 8); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) +} + +/// token length of a number node +NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept +{ + return n.kind == static_cast(value_t::number_float) ? n.len + : (n.extra & 0xFFu) + (n.kind == static_cast(value_t::number_integer) ? 1u : 0u); +} + +/// estimated number of nodes for an input of `size` bytes (one node per ~12 +/// bytes covers typical documents without regrowth) +inline std::size_t estimate_nodes(std::size_t size) noexcept +{ + return (size / 12) + 16; +} + +/// estimated number of nodes for the input [src, src + size): pretty-printed +/// input (whitespace after the first byte) needs about a node per 12 bytes, +/// minified input up to one per 4 (yyjson tells the two apart the same way) +inline std::size_t estimate_nodes(const char* src, std::size_t size) noexcept +{ + return size >= 2 && (src[1] == ' ' || src[1] == '\n' || src[1] == '\r' || src[1] == '\t') ? estimate_nodes(size) : (size / 4) + 16; +} + +} // namespace view +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/view/scan.hpp b/include/nlohmann/detail/view/scan.hpp new file mode 100644 index 000000000..fc9b075aa --- /dev/null +++ b/include/nlohmann/detail/view/scan.hpp @@ -0,0 +1,186 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-FileCopyrightText: 2020 YaoYuan +// SPDX-License-Identifier: MIT + +#pragma once + +#include // size_t +#include // uint8_t, uint16_t, uint64_t +#include // memcpy + +#include +#include + +// Scanning primitives of the view's parser. The unrolled checks at fixed +// offsets follow yyjson (https://github.com/ibireme/yyjson, MIT license): the +// loads do not depend on each other, so the CPU can run ahead. Words are read +// with read_eight_bytes(), so nothing here depends on the byte order. + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +namespace view +{ + +/// 1 for bytes that may appear verbatim in a string: 0x20..0x7F except '"' and '\\' +inline const std::uint8_t* string_plain() noexcept +{ + static const std::uint8_t table[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00..0x1F + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20..0x3F ('"') + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 0x40..0x5F ('\\') + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60..0x7F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80..0x9F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA0..0xBF + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC0..0xDF + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE0..0xFF + }; + return table; +} + +NLOHMANN_VIEW_ALWAYS_INLINE bool is_digit(unsigned char c) noexcept +{ + return static_cast(c - '0') <= 9; +} + +/// two bytes as they are in memory (only compared with byte-symmetric patterns) +NLOHMANN_VIEW_ALWAYS_INLINE std::uint16_t load16(const unsigned char* p) noexcept +{ + std::uint16_t w = 0; + std::memcpy(&w, p, 2); + return w; +} + +/// Advance over plain string bytes and well-formed UTF-8. Stops at a quote, +/// a backslash, a control character, ill-formed UTF-8, or the end. The first +/// 16 bytes are checked one by one, so that the position advances by +/// constants in predicted branches (most strings are short); longer runs +/// continue eight bytes at a time. +NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* scan_string_run(const unsigned char* p, const unsigned char* e) noexcept +{ + const std::uint8_t* plain = string_plain(); + for (;;) + { + if (e - p >= 16) + { +#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(plain[p[i]] != 0)) {} else { p += (i); goto stop; } + NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP) +#undef NLOHMANN_VIEW_STEP + p += 16; + while (e - p >= 8) + { + const std::uint64_t special = swar_string_special(read_eight_bytes(p)); + if (special != 0) + { + p += count_trailing_zeros(special) / 8; + goto stop; + } + p += 8; + } + continue; + } + while (p != e && plain[*p] != 0) + { + ++p; + } + if (p == e) + { + return p; + } +stop: + if (*p < 0x80) + { + return p; // quote, backslash, or control character + } + // non-ASCII: a run of well-formed sequences (the library's check, so + // that exactly what json::parse accepts is accepted) + do + { + const std::size_t n = validate_one_utf8(p, static_cast(e - p)); + if (n == 0) + { + return p; + } + p += n; + } + while (p != e && *p >= 0x80); + } +} + +/// advance over ASCII digits +NLOHMANN_VIEW_ALWAYS_INLINE const unsigned char* skip_digits(const unsigned char* p, const unsigned char* e) noexcept +{ + while (e - p >= 16) + { +#define NLOHMANN_VIEW_STEP(i) if (NLOHMANN_VIEW_LIKELY(is_digit(p[i]))) {} else { return p + (i); } + NLOHMANN_VIEW_REPEAT16(NLOHMANN_VIEW_STEP) +#undef NLOHMANN_VIEW_STEP + p += 16; + } + while (p != e && is_digit(*p)) + { + ++p; + } + return p; +} + +/// powers of ten up to 10^19 as integers +inline std::uint64_t int_pow10(unsigned k) noexcept +{ + static const std::uint64_t table[20] = + { + 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, + 10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, + 10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u + }; + return table[k]; +} + +/// value of k <= 8 digits at p in one step if [p, p + 8) lies below limit, +/// else one digit at a time +NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t parse_upto8(const unsigned char* p, unsigned k, const unsigned char* limit) noexcept +{ + if (NLOHMANN_VIEW_LIKELY(limit - p >= 8)) + { + if (k == 8) + { + return parse_eight_digits(read_eight_bytes(p)); + } + // move the k digits to the top and pad the vacated low bytes with '0' + const unsigned shift = 8 * (8 - k); + return parse_eight_digits((read_eight_bytes(p) << shift) | (0x3030303030303030u >> (8 * k))); + } + std::uint64_t v = 0; + for (unsigned i = 0; i < k; ++i) + { + v = (v * 10) + static_cast(p[i] - '0'); + } + return v; +} + +/// value of k <= 19 digits at p +NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t parse_upto19(const unsigned char* p, unsigned k, const unsigned char* limit) noexcept +{ + std::uint64_t w = 0; + while (k >= 8) + { + w = (w * 100000000u) + parse_upto8(p, 8, limit); + p += 8; + k -= 8; + } + if (k != 0) + { + w = (w * int_pow10(k)) + parse_upto8(p, k, limit); + } + return w; +} + +} // namespace view +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END diff --git a/include/nlohmann/detail/view/string_ref.hpp b/include/nlohmann/detail/view/string_ref.hpp new file mode 100644 index 000000000..bb9605bf6 --- /dev/null +++ b/include/nlohmann/detail/view/string_ref.hpp @@ -0,0 +1,111 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#pragma once + +#include // min +#include // size_t +#include // memcmp, strlen +#include // basic_string + +#include +#include + +#if NLOHMANN_VIEW_HAS_CPP_17 + #include // string_view +#endif +#ifndef JSON_NO_IO + #include // ostream +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +namespace detail +{ +namespace view +{ + +#if NLOHMANN_VIEW_HAS_CPP_17 +using string_ref = std::string_view; +#else +/// minimal C++11 stand-in for std::string_view +class string_ref +{ + public: + using size_type = std::size_t; + using const_iterator = const char*; + + string_ref() noexcept = default; + string_ref(const char* s) : m_data(s), m_size(std::strlen(s)) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) + string_ref(const char* s, std::size_t n) noexcept : m_data(s), m_size(n) {} + template + string_ref(const std::basic_string& s) noexcept : m_data(s.data()), m_size(s.size()) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions) + + const char* data() const noexcept + { + return m_data; + } + std::size_t size() const noexcept + { + return m_size; + } + std::size_t length() const noexcept + { + return m_size; + } + bool empty() const noexcept + { + return m_size == 0; + } + const char* begin() const noexcept + { + return m_data; + } + const char* end() const noexcept + { + return m_data + m_size; + } + char operator[](std::size_t i) const noexcept + { + return m_data[i]; + } + + template + explicit operator std::basic_string() const + { + return std::basic_string(m_data, m_size); + } + + friend bool operator==(string_ref a, string_ref b) noexcept + { + return a.m_size == b.m_size && (a.m_size == 0 || std::memcmp(a.m_data, b.m_data, a.m_size) == 0); + } + friend bool operator!=(string_ref a, string_ref b) noexcept + { + return !(a == b); + } + friend bool operator<(string_ref a, string_ref b) noexcept + { + const int c = std::memcmp(a.m_data, b.m_data, (std::min)(a.m_size, b.m_size)); + return c != 0 ? c < 0 : a.m_size < b.m_size; + } +#ifndef JSON_NO_IO + friend std::ostream& operator<<(std::ostream& o, string_ref s) + { + return o.write(s.m_data, static_cast(s.m_size)); + } +#endif + + private: + const char* m_data = ""; + std::size_t m_size = 0; +}; +#endif + +} // namespace view +} // namespace detail +NLOHMANN_JSON_NAMESPACE_END