Add the node index and scanning primitives of json_view (internal)

Internal parts of the zero-copy view (#5295), under detail/view and not
included by json.hpp, so that users of json.hpp compile nothing of it:

- macro_scope.hpp/macro_unscope.hpp: the few macros the view needs, under
  its own prefix (json.hpp undefines its own at its end); the throw macro
  honors JSON_NOEXCEPTION and JSON_THROW_USER like JSON_THROW
- string_ref.hpp: std::string_view from C++17 on, else a small stand-in
- node.hpp: the 16-byte node of the index; its kinds are value_t values
  (checked by a static_assert)
- document_data.hpp: the storage of a parsed document (node array, decode
  arena, owned input)
- scan.hpp: string and digit scanning with unrolled checks at fixed offsets
  (after yyjson) and the library's SWAR and UTF-8 checks, independent of the
  byte order

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-29 03:49:29 +02:00
parent 6936b95a65
commit 1ab017bbfe
7 changed files with 609 additions and 0 deletions
+6
View File
@@ -66,6 +66,12 @@ cc_library(
"include/nlohmann/detail/string_escape.hpp", "include/nlohmann/detail/string_escape.hpp",
"include/nlohmann/detail/string_utils.hpp", "include/nlohmann/detail/string_utils.hpp",
"include/nlohmann/detail/value_t.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.hpp",
"include/nlohmann/json_fwd.hpp", "include/nlohmann/json_fwd.hpp",
"include/nlohmann/ordered_map.hpp", "include/nlohmann/ordered_map.hpp",
@@ -0,0 +1,128 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // size_t
#include <cstring> // memcpy
#include <new> // operator new, placement new
#include <string> // string
#include <nlohmann/json.hpp>
#include <nlohmann/detail/view/macro_scope.hpp>
#include <nlohmann/detail/view/node.hpp>
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<node*>(static_cast<void*>(static_cast<char*>(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<node*>(::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
@@ -0,0 +1,61 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// 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 <cstdlib>
#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)
@@ -0,0 +1,20 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// 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
+97
View File
@@ -0,0 +1,97 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
#include <cstring> // memcpy
#include <nlohmann/json.hpp>
#include <nlohmann/detail/view/macro_scope.hpp>
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<std::uint8_t>(value_t::null) == 0 && static_cast<std::uint8_t>(value_t::object) == 1
&& static_cast<std::uint8_t>(value_t::array) == 2 && static_cast<std::uint8_t>(value_t::string) == 3
&& static_cast<std::uint8_t>(value_t::boolean) == 4 && static_cast<std::uint8_t>(value_t::number_integer) == 5
&& static_cast<std::uint8_t>(value_t::number_unsigned) == 6 && static_cast<std::uint8_t>(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<unsigned>(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<const unsigned char*>(&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<unsigned char*>(&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<std::uint8_t>(value_t::number_float) ? n.len
: (n.extra & 0xFFu) + (n.kind == static_cast<std::uint8_t>(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
+186
View File
@@ -0,0 +1,186 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-FileCopyrightText: 2020 YaoYuan <https://github.com/ibireme/yyjson>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // size_t
#include <cstdint> // uint8_t, uint16_t, uint64_t
#include <cstring> // memcpy
#include <nlohmann/json.hpp>
#include <nlohmann/detail/view/macro_scope.hpp>
// 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<unsigned char>(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<std::size_t>(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<std::uint64_t>(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
+111
View File
@@ -0,0 +1,111 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.12.0
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <algorithm> // min
#include <cstddef> // size_t
#include <cstring> // memcmp, strlen
#include <string> // basic_string
#include <nlohmann/json.hpp>
#include <nlohmann/detail/view/macro_scope.hpp>
#if NLOHMANN_VIEW_HAS_CPP_17
#include <string_view> // string_view
#endif
#ifndef JSON_NO_IO
#include <ostream> // 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<typename Traits, typename Alloc>
string_ref(const std::basic_string<char, Traits, Alloc>& 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<typename Traits, typename Alloc>
explicit operator std::basic_string<char, Traits, Alloc>() const
{
return std::basic_string<char, Traits, Alloc>(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<std::streamsize>(s.m_size));
}
#endif
private:
const char* m_data = "";
std::size_t m_size = 0;
};
#endif
} // namespace view
} // namespace detail
NLOHMANN_JSON_NAMESPACE_END