mirror of
https://github.com/nlohmann/json.git
synced 2026-10-22 14:08:13 +00:00
basic_json_view::items() on an array formatted every element's index with std::to_string when the item was made, whether or not the loop asks for the key. The item now keeps the position and makes the text on the first key() call. Objects are not affected. Iterating over the items of an array of 3000 numbers and reading only the values: Apple M1: 25.5 us -> 4.6 us (0.45 M -> ~0 net instructions) Linux x86-64 Ir (whole run), GCC 13 / Clang 18: 14.9 M -> 4.1 M / 15.3 M -> 4.6 M Reading key() costs what it did (24.5 us). Belongs to the PR "json_view: read-side speedups". Signed-off-by: Niels Lohmann <mail@nlohmann.me>
266 lines
6.6 KiB
C++
266 lines
6.6 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> // ptrdiff_t, size_t
|
|
#include <iterator> // forward_iterator_tag
|
|
#include <string> // string, to_string
|
|
#include <type_traits> // enable_if
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/detail/view/document_data.hpp>
|
|
#include <nlohmann/detail/view/errors.hpp>
|
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
|
#include <nlohmann/detail/view/node.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
namespace view
|
|
{
|
|
|
|
/// the result of view_iterator::operator->: keeps the view alive for the
|
|
/// duration of the member access
|
|
template<typename View>
|
|
class arrow_proxy
|
|
{
|
|
public:
|
|
explicit arrow_proxy(const View& v) noexcept
|
|
: m_view(v)
|
|
{}
|
|
|
|
const View* operator->() const noexcept
|
|
{
|
|
return &m_view;
|
|
}
|
|
|
|
private:
|
|
View m_view;
|
|
};
|
|
|
|
/*!
|
|
@brief forward iterator over the elements of a basic_json_view
|
|
|
|
Iterates over the elements of an array or the member values of an object, in
|
|
document order; key() gives the key of an object member. As for basic_json, a
|
|
primitive value iterates as a range of one element (itself), and null as an
|
|
empty range.
|
|
*/
|
|
template<typename View>
|
|
class view_iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::forward_iterator_tag;
|
|
using value_type = View;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = arrow_proxy<View>;
|
|
using reference = View;
|
|
using string_view_t = typename View::string_view_t;
|
|
|
|
view_iterator() noexcept = default;
|
|
|
|
/// @param[in] pos the element, or the key of the member
|
|
/// @param[in] object whether pos is a key (its value is the next node)
|
|
view_iterator(const document_data* d, const node* pos, bool object) noexcept
|
|
: m_doc(d), m_pos(pos), m_value_offset(object ? 1 : 0)
|
|
{}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE View operator*() const noexcept
|
|
{
|
|
return View(m_doc, View::navigation::value(m_pos + m_value_offset));
|
|
}
|
|
|
|
pointer operator->() const noexcept
|
|
{
|
|
return pointer(**this);
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE view_iterator& operator++() noexcept
|
|
{
|
|
m_pos = document_data::after(m_pos + m_value_offset);
|
|
return *this;
|
|
}
|
|
|
|
view_iterator operator++(int) noexcept
|
|
{
|
|
const view_iterator r = *this;
|
|
++*this;
|
|
return r;
|
|
}
|
|
|
|
friend bool operator==(const view_iterator& a, const view_iterator& b) noexcept
|
|
{
|
|
return a.m_pos == b.m_pos;
|
|
}
|
|
|
|
friend bool operator!=(const view_iterator& a, const view_iterator& b) noexcept
|
|
{
|
|
return a.m_pos != b.m_pos;
|
|
}
|
|
|
|
/// the key of the current object member; throws invalid_iterator.207 for
|
|
/// other iterators, like basic_json's iterators
|
|
string_view_t key() const
|
|
{
|
|
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
|
{
|
|
throw_invalid_iterator(207, "cannot use key() for non-object iterators");
|
|
}
|
|
return string_view_t(m_doc->str(*m_pos), m_pos->len);
|
|
}
|
|
|
|
View value() const noexcept
|
|
{
|
|
return **this;
|
|
}
|
|
|
|
/// whether the iterator runs over the members of an object
|
|
bool is_object_iterator() const noexcept
|
|
{
|
|
return m_value_offset != 0;
|
|
}
|
|
|
|
private:
|
|
const document_data* m_doc = nullptr;
|
|
const node* m_pos = nullptr;
|
|
std::size_t m_value_offset = 0; ///< 1 for objects: the value follows its key
|
|
};
|
|
|
|
/*!
|
|
@brief a (key, value) item of basic_json_view::items()
|
|
|
|
The key of an array element is its index, as for basic_json::items().
|
|
Supports structured bindings: for (const auto [key, value] : view.items())
|
|
*/
|
|
template<typename View>
|
|
class view_item
|
|
{
|
|
public:
|
|
using string_view_t = typename View::string_view_t;
|
|
using iterator = view_iterator<View>;
|
|
|
|
view_item(const iterator& it, std::size_t index)
|
|
: m_it(it)
|
|
, m_position(index)
|
|
{}
|
|
|
|
/// the member key, or the element index for arrays (as text made on the
|
|
/// first call, so that iterating over the values pays nothing for it)
|
|
string_view_t key() const
|
|
{
|
|
if (m_it.is_object_iterator())
|
|
{
|
|
return m_it.key();
|
|
}
|
|
if (m_index.empty())
|
|
{
|
|
m_index = std::to_string(m_position);
|
|
}
|
|
return string_view_t(m_index.data(), m_index.size());
|
|
}
|
|
|
|
View value() const noexcept
|
|
{
|
|
return *m_it;
|
|
}
|
|
|
|
template<std::size_t N, typename std::enable_if<N == 0, int>::type = 0>
|
|
string_view_t get() const
|
|
{
|
|
return key();
|
|
}
|
|
|
|
template<std::size_t N, typename std::enable_if<N == 1, int>::type = 0>
|
|
View get() const noexcept
|
|
{
|
|
return value();
|
|
}
|
|
|
|
private:
|
|
iterator m_it;
|
|
std::size_t m_position; ///< the index of an array element
|
|
mutable std::string m_index{}; ///< the text of m_position, once key() was called // NOLINT(readability-redundant-member-init)
|
|
};
|
|
|
|
/// the range returned by basic_json_view::items()
|
|
template<typename View>
|
|
class view_items
|
|
{
|
|
public:
|
|
using item = view_item<View>;
|
|
|
|
class iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::forward_iterator_tag;
|
|
using value_type = item;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = void;
|
|
using reference = item;
|
|
|
|
explicit iterator(const view_iterator<View>& it) noexcept
|
|
: m_it(it)
|
|
{}
|
|
|
|
item operator*() const
|
|
{
|
|
return item(m_it, m_index);
|
|
}
|
|
|
|
iterator& operator++() noexcept
|
|
{
|
|
++m_it;
|
|
++m_index;
|
|
return *this;
|
|
}
|
|
|
|
iterator operator++(int) noexcept
|
|
{
|
|
const iterator r = *this;
|
|
++*this;
|
|
return r;
|
|
}
|
|
|
|
friend bool operator==(const iterator& a, const iterator& b) noexcept
|
|
{
|
|
return a.m_it == b.m_it;
|
|
}
|
|
|
|
friend bool operator!=(const iterator& a, const iterator& b) noexcept
|
|
{
|
|
return a.m_it != b.m_it;
|
|
}
|
|
|
|
private:
|
|
view_iterator<View> m_it;
|
|
std::size_t m_index = 0;
|
|
};
|
|
|
|
explicit view_items(const View& v) noexcept
|
|
: m_view(v)
|
|
{}
|
|
|
|
iterator begin() const noexcept
|
|
{
|
|
return iterator(m_view.begin());
|
|
}
|
|
|
|
iterator end() const noexcept
|
|
{
|
|
return iterator(m_view.end());
|
|
}
|
|
|
|
private:
|
|
View m_view;
|
|
};
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|