mirror of
https://github.com/nlohmann/json.git
synced 2026-10-11 08:57:15 +00:00
Merge branch 'json-view/11-view-access' into json-view/13-view-dump
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
54 files changed
+1229
-628
No files matched your search
@@ -9,7 +9,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm> // find, find_if, max
|
||||
#include <algorithm> // find, find_if, max, min
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t, ptrdiff_t
|
||||
#include <cstdint> // int64_t, uint8_t, uint16_t, uint32_t, uint64_t
|
||||
@@ -195,8 +195,18 @@ class builder
|
||||
const std::uint64_t done = static_cast<std::uint64_t>(at - b) + 1;
|
||||
const std::uint64_t guess = static_cast<std::uint64_t>(n) * static_cast<std::uint64_t>(e - b + 1) / done;
|
||||
const std::uint64_t grown = guess + (guess / 4) + 64; // a variable: GCC calls a cast of the sum useless where std::uint64_t is std::size_t
|
||||
// (n is below 2^32: the input is smaller than 4 GiB; the sum cannot wrap)
|
||||
const std::uint64_t wanted = (std::max)(grown, static_cast<std::uint64_t>(n) + (n / 2) + 64);
|
||||
const std::uint64_t limit = document_data::max_nodes();
|
||||
doc.tape_size = n;
|
||||
doc.reserve((std::max)(static_cast<std::size_t>(grown), n + (n / 2) + 64));
|
||||
// LCOV_EXCL_START (a node array that fills the address space)
|
||||
if (NLOHMANN_VIEW_UNLIKELY(n >= limit))
|
||||
{
|
||||
document_data::throw_bad_alloc(); // no room for another node
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
// (a count beyond the limit is cut: the index does not grow beyond what can be addressed)
|
||||
doc.reserve(static_cast<std::size_t>((std::min)(wanted, limit)));
|
||||
return doc.tape;
|
||||
}
|
||||
|
||||
@@ -801,7 +811,10 @@ indent_done:
|
||||
n->flags = flags;
|
||||
n->extra = extra;
|
||||
n->off = static_cast<std::uint32_t>(off);
|
||||
set_integer_bits(*n, second);
|
||||
// len is the low half of the second word, next the high half
|
||||
// (not a native word over both, which swaps them on big-endian)
|
||||
n->len = static_cast<std::uint32_t>(second);
|
||||
n->next = static_cast<std::uint32_t>(second >> 32);
|
||||
#endif
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstring> // memcpy
|
||||
#include <new> // operator new, placement new
|
||||
#include <limits> // numeric_limits
|
||||
#include <new> // bad_alloc, operator new, placement new
|
||||
#include <string> // string
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -84,13 +85,30 @@ struct document_data
|
||||
tape_cap = inline_cap;
|
||||
}
|
||||
|
||||
/// make room for n nodes; keeps the first tape_size nodes
|
||||
/// the largest node count whose size in bytes fits a std::size_t
|
||||
static constexpr std::size_t max_nodes() noexcept
|
||||
{
|
||||
return (std::numeric_limits<std::size_t>::max)() / sizeof(node);
|
||||
}
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE static void throw_bad_alloc()
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(std::bad_alloc());
|
||||
}
|
||||
|
||||
/// make room for n nodes; keeps the first tape_size nodes (throws
|
||||
/// std::bad_alloc for a count that does not fit the address space,
|
||||
/// instead of wrapping around in n * sizeof(node))
|
||||
void reserve(std::size_t n)
|
||||
{
|
||||
if (n <= tape_cap)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(n > max_nodes()))
|
||||
{
|
||||
throw_bad_alloc();
|
||||
}
|
||||
node* fresh = static_cast<node*>(::operator new (n * sizeof(node)));
|
||||
if (tape_size != 0)
|
||||
{
|
||||
|
||||
@@ -61,9 +61,8 @@ template<typename BasicJsonType>
|
||||
{
|
||||
if (f.code == error_code::input_too_large)
|
||||
{
|
||||
// LCOV_EXCL_START (4 GiB)
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4 GiB or more is not supported by json_document", nullptr));
|
||||
// LCOV_EXCL_STOP
|
||||
// (the limit is detail::view::max_input_size: 4 GiB minus 16 bytes)
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(416, "input of 4294967280 bytes or more is not supported by json_document", nullptr));
|
||||
}
|
||||
const BasicJsonType accepted = BasicJsonType::parse(src, src + size, nullptr, true, ignore_comments, ignore_trailing_commas);
|
||||
// LCOV_EXCL_START (only if parse() accepts what the view rejects: a bug)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string> // basic_string, char_traits, string
|
||||
#include <type_traits> // decay, integral_constant, is_array, is_lvalue_reference, is_pointer, is_same, remove_reference
|
||||
#include <type_traits> // decay, integral_constant, is_array, is_const, is_integral, is_lvalue_reference, is_pointer, is_same, remove_reference
|
||||
#include <utility> // forward
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
@@ -28,12 +28,12 @@ namespace view
|
||||
/// how a document takes its input
|
||||
enum class input_kind
|
||||
{
|
||||
move_string, ///< rvalue std::string: owned without a copy
|
||||
move_string, ///< non-const rvalue std::string: owned without a copy
|
||||
c_string, ///< const char* (NUL-terminated): borrowed
|
||||
char_array, ///< char array (e.g. a string literal): borrowed
|
||||
borrow_range, ///< lvalue contiguous byte container, or std::string_view: borrowed
|
||||
copy_range, ///< rvalue contiguous byte container: copied
|
||||
adapter, ///< anything else parse() accepts (streams, wide strings, ...): read into a buffer
|
||||
copy_range, ///< rvalue contiguous byte container (a const rvalue std::string too): copied
|
||||
adapter, ///< streams, wide strings, and the rest of what the library's input adapter reads: read into a buffer
|
||||
};
|
||||
|
||||
template<typename InputType>
|
||||
@@ -52,13 +52,20 @@ struct classify_input
|
||||
static constexpr input_kind value =
|
||||
std::is_array<R>::value ? input_kind::char_array
|
||||
: std::is_pointer<D>::value ? input_kind::c_string
|
||||
: (is_rvalue && std::is_same<D, std::string>::value) ? input_kind::move_string
|
||||
: (is_rvalue && !std::is_const<R>::value && std::is_same<D, std::string>::value) ? input_kind::move_string
|
||||
: (is_bytes && (!is_rvalue || is_string_view)) ? input_kind::borrow_range
|
||||
: is_bytes ? input_kind::copy_range
|
||||
: input_kind::adapter;
|
||||
// NOLINTEND(readability-avoid-nested-conditional-operator)
|
||||
};
|
||||
|
||||
/// an integer type other than bool: a length passed where a flag is expected
|
||||
template<typename T>
|
||||
struct is_integer_not_bool : std::is_integral<T> {};
|
||||
|
||||
template<>
|
||||
struct is_integer_not_bool<bool> : std::false_type {};
|
||||
|
||||
/// std::basic_string guarantees a NUL at data()[size()] (the parser's sentinel)
|
||||
template<typename T>
|
||||
struct is_std_string : std::false_type {};
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint16_t, uint32_t, uint64_t
|
||||
#include <cstring> // memcmp, memcpy
|
||||
#include <limits> // numeric_limits
|
||||
#include <type_traits> // integral_constant, is_integral, is_same
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
@@ -81,12 +83,14 @@ class short_key
|
||||
std::uint64_t m_b = 0;
|
||||
};
|
||||
|
||||
/// the key node of the first member of an object with the given key, or
|
||||
/// nullptr; most keys are rejected by their length, from the index alone
|
||||
/// the key node of the last member of an object with the given key, or
|
||||
/// nullptr (the last one, as materialize() and parse() keep it); most keys are
|
||||
/// rejected by their length, from the index alone
|
||||
inline const node* find_member(const document_data& d, const node* object, const char* key, std::size_t n) noexcept
|
||||
{
|
||||
const node* const end = document_data::child_end(object);
|
||||
const auto* const k = reinterpret_cast<const unsigned char*>(key); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
const node* last = nullptr;
|
||||
if (NLOHMANN_VIEW_LIKELY(n <= 16))
|
||||
{
|
||||
const short_key probe(k, n);
|
||||
@@ -94,19 +98,37 @@ inline const node* find_member(const document_data& d, const node* object, const
|
||||
{
|
||||
if (m->len == n && probe.matches(reinterpret_cast<const unsigned char*>(d.str(*m)))) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||
{
|
||||
return m;
|
||||
last = m;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return last;
|
||||
}
|
||||
for (const node* m = document_data::first_child(object); m != end; m = document_data::after(m + 1))
|
||||
{
|
||||
if (m->len == n && std::memcmp(d.str(*m), key, n) == 0)
|
||||
{
|
||||
return m;
|
||||
last = m;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
return last;
|
||||
}
|
||||
|
||||
/// whether an integer type is accepted as an array index by the view's
|
||||
/// operator[] and at(): every integer type but bool and size_t, which has its
|
||||
/// own overload
|
||||
template<typename T>
|
||||
struct is_index_type : std::integral_constant < bool,
|
||||
std::is_integral<T>::value && !std::is_same<T, bool>::value && !std::is_same<T, std::size_t>::value >
|
||||
{};
|
||||
|
||||
/// an integer as an index: negative values, and values that do not fit a
|
||||
/// size_t, map to the largest size_t (out of range for every array)
|
||||
template<typename SizeType, typename IntegerType>
|
||||
SizeType to_index(IntegerType idx) noexcept
|
||||
{
|
||||
const IntegerType zero = 0;
|
||||
const auto result = static_cast<SizeType>(idx);
|
||||
return (idx < zero || static_cast<IntegerType>(result) != idx) ? (std::numeric_limits<SizeType>::max)() : result;
|
||||
}
|
||||
|
||||
/// the element of an array at an index below its size
|
||||
|
||||
@@ -29,6 +29,11 @@ static_assert(static_cast<std::uint8_t>(value_t::null) == 0 && static_cast<std::
|
||||
&& 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");
|
||||
|
||||
/// 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.
|
||||
static constexpr std::size_t max_input_size = 0xFFFFFFEFu;
|
||||
|
||||
/// node flags
|
||||
struct node_flags
|
||||
{
|
||||
@@ -57,17 +62,27 @@ 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)
|
||||
/// 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
|
||||
|
||||
Reference in new issue
Block a user