mirror of
https://github.com/nlohmann/json.git
synced 2026-10-25 15:28:54 +00:00
basic_json_document and basic_json_view take a view_mode (read_only, the default, or editable) instead of a bool: basic_json_document<json, true> did not say what true means. The shortcut aliases are unchanged. The kind of a node is a node_kind (the value_t values plus link, 10 as before), which removes the static_cast<std::uint8_t>(value_t::...) of every comparison. The node and image layouts are unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
154 lines
6.9 KiB
C++
154 lines
6.9 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | 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 largest input a document accepts, in bytes. Offsets and node counts are
|
|
/// 32 bits wide; the limit keeps 16 bytes (the width of the scanner's steps)
|
|
/// below 2^32, so that a position one step past the end of the text fits.
|
|
constexpr std::size_t max_input_size() noexcept
|
|
{
|
|
return 0xFFFFFFEFu;
|
|
}
|
|
|
|
/// 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 edited = 2; ///< string or number token lives in the edit arena (editable documents)
|
|
static constexpr std::uint8_t storage = escaped | edited; ///< mask: where a string or number token lives (index into document_data::base)
|
|
static constexpr std::uint8_t is_true = 4; ///< boolean value
|
|
static constexpr std::uint8_t moved = 8; ///< array/object: the elements live in a separate sequence (editable documents)
|
|
static constexpr std::uint8_t is_new = 16; ///< written by an edit: no source position
|
|
static constexpr std::uint8_t linked = 32; ///< an entry of a moved sequence links to this value (editable documents): its extent in the parsed layout no longer matters
|
|
};
|
|
|
|
/// the kind of a node: the value_t of its value, or link
|
|
enum class node_kind : std::uint8_t
|
|
{
|
|
null = static_cast<std::uint8_t>(value_t::null),
|
|
object = static_cast<std::uint8_t>(value_t::object),
|
|
array = static_cast<std::uint8_t>(value_t::array),
|
|
string = static_cast<std::uint8_t>(value_t::string),
|
|
boolean = static_cast<std::uint8_t>(value_t::boolean),
|
|
number_integer = static_cast<std::uint8_t>(value_t::number_integer),
|
|
number_unsigned = static_cast<std::uint8_t>(value_t::number_unsigned),
|
|
number_float = static_cast<std::uint8_t>(value_t::number_float),
|
|
/// an entry of an edited sequence that stands for a value stored elsewhere
|
|
/// (the address of the value's node is kept in the len/next bytes)
|
|
link = static_cast<std::uint8_t>(value_t::discarded) + 1,
|
|
};
|
|
|
|
/// 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
|
|
{
|
|
node_kind kind; ///< value_t, or link
|
|
std::uint8_t flags; ///< node_flags
|
|
std::uint16_t extra; ///< numbers: integer digits (low byte) and fraction digits (high byte), 255 = "many"; objects: number of the object's key hash index (1-based, 0 = none); otherwise 0
|
|
std::uint32_t off; ///< source offset (string content, number token, literal, bracket); arena offset if escaped/edited; number of the element sequence if moved
|
|
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");
|
|
|
|
// the node kinds are value_t values, and images store them as bytes; the test
|
|
// below (object and array are 1 and 2) and those 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");
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE bool is_container(const node& n) noexcept
|
|
{
|
|
return static_cast<unsigned>(n.kind) - 1u <= 1u;
|
|
}
|
|
|
|
/// the value a link node stands for
|
|
NLOHMANN_VIEW_ALWAYS_INLINE const node* link_target(const node& n) noexcept
|
|
{
|
|
const node* t = nullptr;
|
|
std::memcpy(static_cast<void*>(&t), reinterpret_cast<const unsigned char*>(&n) + 8, sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
return t;
|
|
}
|
|
|
|
/// let the entry n stand for the value at target (and mark the value)
|
|
inline void make_link(node& n, node* target) noexcept
|
|
{
|
|
target->flags = static_cast<std::uint8_t>(target->flags | node_flags::linked);
|
|
n = node{};
|
|
n.kind = node_kind::link;
|
|
std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, static_cast<const void*>(&target), sizeof(const node*)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
}
|
|
|
|
/// the converted value of an integer node: len is its low half, next its high
|
|
/// half (on little-endian targets the two words are the value in memory)
|
|
NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t integer_bits(const node& n) noexcept
|
|
{
|
|
#if NLOHMANN_VIEW_LITTLE_ENDIAN
|
|
std::uint64_t v = 0;
|
|
std::memcpy(&v, reinterpret_cast<const unsigned char*>(&n) + 8, 8); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
return v;
|
|
#else
|
|
return static_cast<std::uint64_t>(n.len) | (static_cast<std::uint64_t>(n.next) << 32);
|
|
#endif
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE void set_integer_bits(node& n, std::uint64_t v) noexcept
|
|
{
|
|
#if NLOHMANN_VIEW_LITTLE_ENDIAN
|
|
std::memcpy(reinterpret_cast<unsigned char*>(&n) + 8, &v, 8); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
#else
|
|
n.len = static_cast<std::uint32_t>(v);
|
|
n.next = static_cast<std::uint32_t>(v >> 32);
|
|
#endif
|
|
}
|
|
|
|
/// token length of a number node
|
|
NLOHMANN_VIEW_ALWAYS_INLINE std::uint32_t number_length(const node& n) noexcept
|
|
{
|
|
return n.kind == node_kind::number_float ? n.len
|
|
: (n.extra & 0xFFu) + (n.kind == node_kind::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
|