libcarla/include/system/boost/json/impl/object.hpp
2024-10-18 13:19:59 +08:00

531 lines
9.2 KiB
C++

//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/json
//
#ifndef BOOST_JSON_IMPL_OBJECT_HPP
#define BOOST_JSON_IMPL_OBJECT_HPP
#include <boost/json/value.hpp>
#include <iterator>
#include <cmath>
#include <type_traits>
#include <utility>
BOOST_JSON_NS_BEGIN
namespace detail {
// Objects with size less than or equal
// to this number will use a linear search
// instead of the more expensive hash function.
static
constexpr
std::size_t
small_object_size_ = 18;
BOOST_STATIC_ASSERT(
small_object_size_ <
BOOST_JSON_MAX_STRUCTURED_SIZE);
} // detail
//----------------------------------------------------------
struct alignas(key_value_pair)
object::table
{
std::uint32_t size = 0;
std::uint32_t capacity = 0;
std::uintptr_t salt = 0;
#if defined(_MSC_VER) && BOOST_JSON_ARCH == 32
// VFALCO If we make key_value_pair smaller,
// then we might want to revisit this
// padding.
BOOST_STATIC_ASSERT(
sizeof(key_value_pair) == 32);
char pad[4] = {}; // silence warnings
#endif
constexpr table();
// returns true if we use a linear
// search instead of the hash table.
bool is_small() const noexcept
{
return capacity <=
detail::small_object_size_;
}
key_value_pair&
operator[](
std::size_t pos) noexcept
{
return reinterpret_cast<
key_value_pair*>(
this + 1)[pos];
}
// VFALCO This is exported for tests
BOOST_JSON_DECL
std::size_t
digest(string_view key) const noexcept;
inline
index_t&
bucket(std::size_t hash) noexcept;
inline
index_t&
bucket(string_view key) noexcept;
inline
void
clear() noexcept;
static
inline
table*
allocate(
std::size_t capacity,
std::uintptr_t salt,
storage_ptr const& sp);
static
void
deallocate(
table* p,
storage_ptr const& sp) noexcept
{
if(p->capacity == 0)
return;
if(! p->is_small())
sp->deallocate(p,
sizeof(table) + p->capacity * (
sizeof(key_value_pair) +
sizeof(index_t)));
else
sp->deallocate(p,
sizeof(table) + p->capacity *
sizeof(key_value_pair));
}
};
//----------------------------------------------------------
class object::revert_construct
{
object* obj_;
BOOST_JSON_DECL
void
destroy() noexcept;
public:
explicit
revert_construct(
object& obj) noexcept
: obj_(&obj)
{
}
~revert_construct()
{
if(! obj_)
return;
destroy();
}
void
commit() noexcept
{
obj_ = nullptr;
}
};
//----------------------------------------------------------
class object::revert_insert
{
object* obj_;
std::size_t size_;
BOOST_JSON_DECL
void
destroy() noexcept;
public:
explicit
revert_insert(
object& obj) noexcept
: obj_(&obj)
, size_(obj_->size())
{
}
~revert_insert()
{
if(! obj_)
return;
destroy();
obj_->t_->size = static_cast<
index_t>(size_);
}
void
commit() noexcept
{
obj_ = nullptr;
}
};
//----------------------------------------------------------
//
// Iterators
//
//----------------------------------------------------------
auto
object::
begin() noexcept ->
iterator
{
return &(*t_)[0];
}
auto
object::
begin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
object::
cbegin() const noexcept ->
const_iterator
{
return &(*t_)[0];
}
auto
object::
end() noexcept ->
iterator
{
return &(*t_)[t_->size];
}
auto
object::
end() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
object::
cend() const noexcept ->
const_iterator
{
return &(*t_)[t_->size];
}
auto
object::
rbegin() noexcept ->
reverse_iterator
{
return reverse_iterator(end());
}
auto
object::
rbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
object::
crbegin() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(end());
}
auto
object::
rend() noexcept ->
reverse_iterator
{
return reverse_iterator(begin());
}
auto
object::
rend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
auto
object::
crend() const noexcept ->
const_reverse_iterator
{
return const_reverse_iterator(begin());
}
//----------------------------------------------------------
//
// Capacity
//
//----------------------------------------------------------
bool
object::
empty() const noexcept
{
return t_->size == 0;
}
auto
object::
size() const noexcept ->
std::size_t
{
return t_->size;
}
constexpr
std::size_t
object::
max_size() noexcept
{
// max_size depends on the address model
using min = std::integral_constant<std::size_t,
(std::size_t(-1) - sizeof(table)) /
(sizeof(key_value_pair) + sizeof(index_t))>;
return min::value < BOOST_JSON_MAX_STRUCTURED_SIZE ?
min::value : BOOST_JSON_MAX_STRUCTURED_SIZE;
}
auto
object::
capacity() const noexcept ->
std::size_t
{
return t_->capacity;
}
//----------------------------------------------------------
//
// Lookup
//
//----------------------------------------------------------
auto
object::
at(string_view key) ->
value&
{
auto it = find(key);
if(it == end())
detail::throw_out_of_range(
BOOST_JSON_SOURCE_POS);
return it->value();
}
auto
object::
at(string_view key) const ->
value const&
{
auto it = find(key);
if(it == end())
detail::throw_out_of_range(
BOOST_JSON_SOURCE_POS);
return it->value();
}
//----------------------------------------------------------
template<class P, class>
auto
object::
insert(P&& p) ->
std::pair<iterator, bool>
{
key_value_pair v(
std::forward<P>(p), sp_);
return insert_impl(pilfer(v));
}
template<class M>
auto
object::
insert_or_assign(
string_view key, M&& m) ->
std::pair<iterator, bool>
{
reserve(size() + 1);
auto const result = detail::find_in_object(*this, key);
if(result.first)
{
value(std::forward<M>(m),
sp_).swap(result.first->value());
return { result.first, false };
}
key_value_pair kv(key,
std::forward<M>(m), sp_);
return { insert_impl(pilfer(kv),
result.second), true };
}
template<class Arg>
auto
object::
emplace(
string_view key,
Arg&& arg) ->
std::pair<iterator, bool>
{
reserve(size() + 1);
auto const result = detail::find_in_object(*this, key);
if(result.first)
return { result.first, false };
key_value_pair kv(key,
std::forward<Arg>(arg), sp_);
return { insert_impl(pilfer(kv),
result.second), true };
}
//----------------------------------------------------------
//
// (private)
//
//----------------------------------------------------------
template<class InputIt>
void
object::
construct(
InputIt first,
InputIt last,
std::size_t min_capacity,
std::input_iterator_tag)
{
reserve(min_capacity);
revert_construct r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
template<class InputIt>
void
object::
construct(
InputIt first,
InputIt last,
std::size_t min_capacity,
std::forward_iterator_tag)
{
auto n = static_cast<
std::size_t>(std::distance(
first, last));
if( n < min_capacity)
n = min_capacity;
reserve(n);
revert_construct r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
template<class InputIt>
void
object::
insert(
InputIt first,
InputIt last,
std::input_iterator_tag)
{
// Since input iterators cannot be rewound,
// we keep inserted elements on an exception.
//
while(first != last)
{
insert(*first);
++first;
}
}
template<class InputIt>
void
object::
insert(
InputIt first,
InputIt last,
std::forward_iterator_tag)
{
auto const n =
static_cast<std::size_t>(
std::distance(first, last));
auto const n0 = size();
if(n > max_size() - n0)
detail::throw_length_error(
"object too large",
BOOST_JSON_SOURCE_POS);
reserve(n0 + n);
revert_insert r(*this);
while(first != last)
{
insert(*first);
++first;
}
r.commit();
}
//----------------------------------------------------------
namespace detail {
unchecked_object::
~unchecked_object()
{
if(! data_)
return;
if(sp_.is_not_shared_and_deallocate_is_trivial())
return;
value* p = data_;
while(size_--)
{
p[0].~value();
p[1].~value();
p += 2;
}
}
} // detail
BOOST_JSON_NS_END
#endif