mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 22:47:13 +00:00
Give basic_json_view the read-only access functions of basic_json: operator[] and at() with keys and indices, front()/back(), find(), contains(), count(), begin()/end() and cbegin()/cend(), items() with structured bindings from C++17 on, and type_name(). Exceptions have the ids and messages of the const functions of basic_json. Where basic_json has undefined behavior the view answers safely: operator[] with a missing key or an out-of-range index returns a discarded view, and front()/back() of an empty container throw invalid_iterator.214. Objects are iterated in document order, and all members are visited; duplicate-key lookups find the first member (as yyjson and simdjson do), while parse(), materialize(), and the map conversions keep the last value, as parse() does. Keys of up to 16 bytes are compared with two overlapping loads. Add value conversions: get<T>()/get_to() for arithmetic types, bool, nullptr_t, strings (std::basic_string copied, string_view_t without a copy), BasicJsonType, views, std::vector, and maps with string keys; get_string() for the string without a copy; number_token() for the number exactly as written in the source; value() with keys and JSON pointers; and operator[]/at()/ contains() with JSON pointers. Everything else, including types with from_json(), goes through materialize() of that subtree. get<T>() of arithmetic types is inlined down to the conversion, so reading an integer needs no call. detail::json_pointer_access exposes a pointer's reference tokens to code outside basic_json. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
140 lines
4.2 KiB
C++
140 lines
4.2 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> // uint16_t, uint32_t, uint64_t
|
|
#include <cstring> // memcmp, memcpy
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/detail/view/document_data.hpp>
|
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
|
#include <nlohmann/detail/view/node.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
namespace view
|
|
{
|
|
|
|
/// equality test for strings of one length n <= 16: two overlapping loads per
|
|
/// string (the first and the last 8, 4, or 2 bytes) replace a memcmp, and no
|
|
/// byte outside [s, s + n) is read
|
|
class short_key
|
|
{
|
|
public:
|
|
short_key(const unsigned char* k, std::size_t n) noexcept
|
|
: m_n(n)
|
|
{
|
|
load(k, m_a, m_b);
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE bool matches(const unsigned char* s) const noexcept
|
|
{
|
|
std::uint64_t a = 0;
|
|
std::uint64_t b = 0;
|
|
load(s, a, b);
|
|
return a == m_a && b == m_b;
|
|
}
|
|
|
|
private:
|
|
template<typename T>
|
|
static NLOHMANN_VIEW_ALWAYS_INLINE std::uint64_t load_word(const unsigned char* s) noexcept
|
|
{
|
|
T w = 0;
|
|
std::memcpy(&w, s, sizeof(T));
|
|
return w;
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE void load(const unsigned char* s, std::uint64_t& a, std::uint64_t& b) const noexcept
|
|
{
|
|
if (m_n >= 8)
|
|
{
|
|
a = load_word<std::uint64_t>(s);
|
|
b = load_word<std::uint64_t>(s + m_n - 8);
|
|
}
|
|
else if (m_n >= 4)
|
|
{
|
|
a = load_word<std::uint32_t>(s);
|
|
b = load_word<std::uint32_t>(s + m_n - 4);
|
|
}
|
|
else if (m_n >= 2)
|
|
{
|
|
a = load_word<std::uint16_t>(s);
|
|
b = load_word<std::uint16_t>(s + m_n - 2);
|
|
}
|
|
else
|
|
{
|
|
a = m_n == 1 ? s[0] : 0;
|
|
b = 0;
|
|
}
|
|
}
|
|
|
|
std::size_t m_n;
|
|
std::uint64_t m_a = 0;
|
|
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
|
|
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)
|
|
if (NLOHMANN_VIEW_LIKELY(n <= 16))
|
|
{
|
|
const short_key probe(k, n);
|
|
for (const node* m = document_data::first_child(object); m != end; m = document_data::after(m + 1))
|
|
{
|
|
if (m->len == n && probe.matches(reinterpret_cast<const unsigned char*>(d.str(*m)))) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
{
|
|
return m;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// the element of an array at an index below its size
|
|
inline const node* element_at(const node* array, std::size_t idx) noexcept
|
|
{
|
|
const node* e = document_data::first_child(array);
|
|
for (std::size_t i = 0; i < idx; ++i)
|
|
{
|
|
e = document_data::after(e);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/// the last element of a non-empty array, or the key of the last member of a
|
|
/// non-empty object
|
|
inline const node* last_child(const node* container) noexcept
|
|
{
|
|
const std::size_t value_offset = container->kind == static_cast<std::uint8_t>(value_t::object) ? 1 : 0;
|
|
const node* const end = document_data::child_end(container);
|
|
const node* last = document_data::first_child(container);
|
|
for (const node* c = document_data::after(last + value_offset); c != end; c = document_data::after(c + value_offset))
|
|
{
|
|
last = c;
|
|
}
|
|
return last;
|
|
}
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|