123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587 |
- //
- // Copyright (c) 2019 Vinnie Falco ([email protected])
- //
- // 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>
- namespace boost {
- namespace json {
- 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_;
- table* t_ = nullptr;
- std::size_t size_;
- BOOST_JSON_DECL
- void
- destroy() noexcept;
- public:
- explicit
- revert_insert(
- object& obj,
- std::size_t capacity)
- : obj_(&obj)
- , size_(obj_->size())
- {
- if( capacity > obj_->capacity() )
- t_ = obj_->reserve_impl(capacity);
- }
- ~revert_insert()
- {
- if(! obj_)
- return;
- destroy();
- if( t_ )
- {
- table::deallocate( obj_->t_, obj_->sp_ );
- obj_->t_ = t_;
- }
- else
- {
- obj_->t_->size = static_cast<index_t>(size_);
- }
- }
- void
- commit() noexcept
- {
- BOOST_ASSERT(obj_);
- if( t_ )
- table::deallocate( t_, obj_->sp_ );
- 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;
- }
- void
- object::
- reserve(std::size_t new_capacity)
- {
- if( new_capacity <= capacity() )
- return;
- table* const old_table = reserve_impl(new_capacity);
- table::deallocate( old_table, sp_ );
- }
- //----------------------------------------------------------
- //
- // Lookup
- //
- //----------------------------------------------------------
- auto
- object::
- at(string_view key) & ->
- value&
- {
- auto const& self = *this;
- return const_cast< value& >( self.at(key) );
- }
- auto
- object::
- at(string_view key) && ->
- value&&
- {
- return std::move( at(key) );
- }
- auto
- object::
- at(string_view key) const& ->
- value const&
- {
- auto it = find(key);
- if(it == end())
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::out_of_range, &loc );
- }
- 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 emplace_impl( v.key(), pilfer(v) );
- }
- template<class M>
- auto
- object::
- insert_or_assign(
- string_view key, M&& m) ->
- std::pair<iterator, bool>
- {
- std::pair<iterator, bool> result = emplace_impl(
- key, key, static_cast<M&&>(m) );
- if( !result.second )
- {
- value(static_cast<M>(m), sp_).swap(
- result.first->value());
- }
- return result;
- }
- template<class Arg>
- auto
- object::
- emplace(
- string_view key,
- Arg&& arg) ->
- std::pair<iterator, bool>
- {
- return emplace_impl( key, key, static_cast<Arg&&>(arg) );
- }
- //----------------------------------------------------------
- //
- // (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)
- {
- BOOST_STATIC_CONSTEXPR source_location loc = BOOST_CURRENT_LOCATION;
- detail::throw_system_error( error::object_too_large, &loc );
- }
- revert_insert r( *this, n0 + n );
- while(first != last)
- {
- insert(*first);
- ++first;
- }
- r.commit();
- }
- template< class... Args >
- std::pair<object::iterator, bool>
- object::
- emplace_impl( string_view key, Args&& ... args )
- {
- std::pair<iterator, std::size_t> search_result(nullptr, 0);
- if( !empty() )
- {
- search_result = detail::find_in_object(*this, key);
- if( search_result.first )
- return { search_result.first, false };
- }
- // we create the new value before reserving, in case it is a reference to
- // a subobject of the current object
- key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
- // the key might get deallocated too
- key = kv.key();
- std::size_t const old_capacity = capacity();
- reserve(size() + 1);
- if( (empty() && capacity() > detail::small_object_size_)
- || (capacity() != old_capacity) )
- search_result.second = detail::digest(
- key.begin(), key.end(), t_->salt);
- BOOST_ASSERT(
- t_->is_small() ||
- (search_result.second ==
- detail::digest(key.begin(), key.end(), t_->salt)) );
- return { insert_impl(pilfer(kv), search_result.second), true };
- }
- //----------------------------------------------------------
- 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
- } // namespace json
- } // namespace boost
- #endif
|