123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- //
- // 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_IPP
- #define BOOST_JSON_IMPL_OBJECT_IPP
- #include <boost/json/object.hpp>
- #include <boost/json/detail/digest.hpp>
- #include <boost/json/detail/except.hpp>
- #include <algorithm>
- #include <cmath>
- #include <cstdlib>
- #include <cstring>
- #include <new>
- #include <stdexcept>
- #include <type_traits>
- BOOST_JSON_NS_BEGIN
- //----------------------------------------------------------
- constexpr object::table::table() = default;
- // empty objects point here
- BOOST_JSON_REQUIRE_CONST_INIT
- object::table object::empty_;
- std::size_t
- object::table::
- digest(string_view key) const noexcept
- {
- BOOST_ASSERT(salt != 0);
- return detail::digest(
- key.data(), key.size(), salt);
- }
- auto
- object::table::
- bucket(std::size_t hash) noexcept ->
- index_t&
- {
- return reinterpret_cast<
- index_t*>(&(*this)[capacity])[
- hash % capacity];
- }
- auto
- object::table::
- bucket(string_view key) noexcept ->
- index_t&
- {
- return bucket(digest(key));
- }
- void
- object::table::
- clear() noexcept
- {
- BOOST_ASSERT(! is_small());
- // initialize buckets
- std::memset(
- reinterpret_cast<index_t*>(
- &(*this)[capacity]),
- 0xff, // null_index_
- capacity * sizeof(index_t));
- }
- object::table*
- object::table::
- allocate(
- std::size_t capacity,
- std::uintptr_t salt,
- storage_ptr const& sp)
- {
- BOOST_STATIC_ASSERT(
- alignof(key_value_pair) >=
- alignof(index_t));
- BOOST_ASSERT(capacity > 0);
- BOOST_ASSERT(capacity <= max_size());
- table* p;
- if(capacity <= detail::small_object_size_)
- {
- p = reinterpret_cast<
- table*>(sp->allocate(
- sizeof(table) + capacity *
- sizeof(key_value_pair)));
- p->capacity = static_cast<
- std::uint32_t>(capacity);
- }
- else
- {
- p = reinterpret_cast<
- table*>(sp->allocate(
- sizeof(table) + capacity * (
- sizeof(key_value_pair) +
- sizeof(index_t))));
- p->capacity = static_cast<
- std::uint32_t>(capacity);
- p->clear();
- }
- if(salt)
- {
- p->salt = salt;
- }
- else
- {
- // VFALCO This would be better if it
- // was random, but maybe this
- // is good enough.
- p->salt = reinterpret_cast<
- std::uintptr_t>(p);
- }
- return p;
- }
- //----------------------------------------------------------
- void
- object::
- revert_construct::
- destroy() noexcept
- {
- obj_->destroy();
- }
- //----------------------------------------------------------
- void
- object::
- revert_insert::
- destroy() noexcept
- {
- obj_->destroy(
- &(*obj_->t_)[size_],
- obj_->end());
- }
- //----------------------------------------------------------
- //
- // Construction
- //
- //----------------------------------------------------------
- object::
- object(detail::unchecked_object&& uo)
- : sp_(uo.storage())
- {
- if(uo.size() == 0)
- {
- t_ = &empty_;
- return;
- }
- // should already be checked
- BOOST_ASSERT(
- uo.size() <= max_size());
- t_ = table::allocate(
- uo.size(), 0, sp_);
- // insert all elements, keeping
- // the last of any duplicate keys.
- auto dest = begin();
- auto src = uo.release();
- auto const end = src + 2 * uo.size();
- if(t_->is_small())
- {
- t_->size = 0;
- while(src != end)
- {
- access::construct_key_value_pair(
- dest, pilfer(src[0]), pilfer(src[1]));
- src += 2;
- auto result = find_impl(dest->key());
- if(! result.first)
- {
- ++dest;
- ++t_->size;
- continue;
- }
- // handle duplicate
- auto& v = *result.first;
- // don't bother to check if
- // storage deallocate is trivial
- v.~key_value_pair();
- // trivial relocate
- std::memcpy(
- static_cast<void*>(&v),
- dest, sizeof(v));
- }
- return;
- }
- while(src != end)
- {
- access::construct_key_value_pair(
- dest, pilfer(src[0]), pilfer(src[1]));
- src += 2;
- auto& head = t_->bucket(dest->key());
- auto i = head;
- for(;;)
- {
- if(i == null_index_)
- {
- // end of bucket
- access::next(
- *dest) = head;
- head = static_cast<index_t>(
- dest - begin());
- ++dest;
- break;
- }
- auto& v = (*t_)[i];
- if(v.key() != dest->key())
- {
- i = access::next(v);
- continue;
- }
- // handle duplicate
- access::next(*dest) =
- access::next(v);
- // don't bother to check if
- // storage deallocate is trivial
- v.~key_value_pair();
- // trivial relocate
- std::memcpy(
- static_cast<void*>(&v),
- dest, sizeof(v));
- break;
- }
- }
- t_->size = static_cast<
- index_t>(dest - begin());
- }
- object::
- ~object()
- {
- if(sp_.is_not_shared_and_deallocate_is_trivial())
- return;
- if(t_->capacity == 0)
- return;
- destroy();
- }
- object::
- object(
- std::size_t min_capacity,
- storage_ptr sp)
- : sp_(std::move(sp))
- , t_(&empty_)
- {
- reserve(min_capacity);
- }
- object::
- object(object&& other) noexcept
- : sp_(other.sp_)
- , t_(detail::exchange(
- other.t_, &empty_))
- {
- }
- object::
- object(
- object&& other,
- storage_ptr sp)
- : sp_(std::move(sp))
- {
- if(*sp_ == *other.sp_)
- {
- t_ = detail::exchange(
- other.t_, &empty_);
- return;
- }
- t_ = &empty_;
- object(other, sp_).swap(*this);
- }
- object::
- object(
- object const& other,
- storage_ptr sp)
- : sp_(std::move(sp))
- , t_(&empty_)
- {
- reserve(other.size());
- revert_construct r(*this);
- if(t_->is_small())
- {
- for(auto const& v : other)
- {
- ::new(end())
- key_value_pair(v, sp_);
- ++t_->size;
- }
- r.commit();
- return;
- }
- for(auto const& v : other)
- {
- // skip duplicate checking
- auto& head =
- t_->bucket(v.key());
- auto pv = ::new(end())
- key_value_pair(v, sp_);
- access::next(*pv) = head;
- head = t_->size;
- ++t_->size;
- }
- r.commit();
- }
- object::
- object(
- std::initializer_list<std::pair<
- string_view, value_ref>> init,
- std::size_t min_capacity,
- storage_ptr sp)
- : sp_(std::move(sp))
- , t_(&empty_)
- {
- if( min_capacity < init.size())
- min_capacity = init.size();
- reserve(min_capacity);
- revert_construct r(*this);
- insert(init);
- r.commit();
- }
- //----------------------------------------------------------
- //
- // Assignment
- //
- //----------------------------------------------------------
- object&
- object::
- operator=(object const& other)
- {
- object tmp(other, sp_);
- this->~object();
- ::new(this) object(pilfer(tmp));
- return *this;
- }
- object&
- object::
- operator=(object&& other)
- {
- object tmp(std::move(other), sp_);
- this->~object();
- ::new(this) object(pilfer(tmp));
- return *this;
- }
- object&
- object::
- operator=(
- std::initializer_list<std::pair<
- string_view, value_ref>> init)
- {
- object tmp(init, sp_);
- this->~object();
- ::new(this) object(pilfer(tmp));
- return *this;
- }
- //----------------------------------------------------------
- //
- // Modifiers
- //
- //----------------------------------------------------------
- void
- object::
- clear() noexcept
- {
- if(empty())
- return;
- if(! sp_.is_not_shared_and_deallocate_is_trivial())
- destroy(begin(), end());
- if(! t_->is_small())
- t_->clear();
- t_->size = 0;
- }
- void
- object::
- insert(
- std::initializer_list<std::pair<
- string_view, value_ref>> init)
- {
- auto const n0 = size();
- if(init.size() > max_size() - n0)
- detail::throw_length_error(
- "object too large",
- BOOST_JSON_SOURCE_POS);
- reserve(n0 + init.size());
- revert_insert r(*this);
- if(t_->is_small())
- {
- for(auto& iv : init)
- {
- auto result =
- find_impl(iv.first);
- if(result.first)
- {
- // ignore duplicate
- continue;
- }
- ::new(end()) key_value_pair(
- iv.first,
- iv.second.make_value(sp_));
- ++t_->size;
- }
- r.commit();
- return;
- }
- for(auto& iv : init)
- {
- auto& head = t_->bucket(iv.first);
- auto i = head;
- for(;;)
- {
- if(i == null_index_)
- {
- // VFALCO value_ref should construct
- // a key_value_pair using placement
- auto& v = *::new(end())
- key_value_pair(
- iv.first,
- iv.second.make_value(sp_));
- access::next(v) = head;
- head = static_cast<index_t>(
- t_->size);
- ++t_->size;
- break;
- }
- auto& v = (*t_)[i];
- if(v.key() == iv.first)
- {
- // ignore duplicate
- break;
- }
- i = access::next(v);
- }
- }
- r.commit();
- }
- auto
- object::
- erase(const_iterator pos) noexcept ->
- iterator
- {
- auto p = begin() + (pos - begin());
- if(t_->is_small())
- {
- p->~value_type();
- --t_->size;
- auto const pb = end();
- if(p != end())
- {
- // the casts silence warnings
- std::memcpy(
- static_cast<void*>(p),
- static_cast<void const*>(pb),
- sizeof(*p));
- }
- return p;
- }
- remove(t_->bucket(p->key()), *p);
- p->~value_type();
- --t_->size;
- auto const pb = end();
- if(p != end())
- {
- auto& head = t_->bucket(pb->key());
- remove(head, *pb);
- // the casts silence warnings
- std::memcpy(
- static_cast<void*>(p),
- static_cast<void const*>(pb),
- sizeof(*p));
- access::next(*p) = head;
- head = static_cast<
- index_t>(p - begin());
- }
- return p;
- }
- auto
- object::
- erase(string_view key) noexcept ->
- std::size_t
- {
- auto it = find(key);
- if(it == end())
- return 0;
- erase(it);
- return 1;
- }
- void
- object::
- swap(object& other)
- {
- if(*sp_ == *other.sp_)
- {
- t_ = detail::exchange(
- other.t_, t_);
- return;
- }
- object temp1(
- std::move(*this),
- other.storage());
- object temp2(
- std::move(other),
- this->storage());
- other.~object();
- ::new(&other) object(pilfer(temp1));
- this->~object();
- ::new(this) object(pilfer(temp2));
- }
- //----------------------------------------------------------
- //
- // Lookup
- //
- //----------------------------------------------------------
- auto
- object::
- operator[](string_view key) ->
- value&
- {
- auto const result =
- emplace(key, nullptr);
- return result.first->value();
- }
- auto
- object::
- count(string_view key) const noexcept ->
- std::size_t
- {
- if(find(key) == end())
- return 0;
- return 1;
- }
- auto
- object::
- find(string_view key) noexcept ->
- iterator
- {
- if(empty())
- return end();
- auto const p =
- find_impl(key).first;
- if(p)
- return p;
- return end();
- }
- auto
- object::
- find(string_view key) const noexcept ->
- const_iterator
- {
- if(empty())
- return end();
- auto const p =
- find_impl(key).first;
- if(p)
- return p;
- return end();
- }
- bool
- object::
- contains(
- string_view key) const noexcept
- {
- if(empty())
- return false;
- return find_impl(
- key).first != nullptr;
- }
- value const*
- object::
- if_contains(
- string_view key) const noexcept
- {
- auto const it = find(key);
- if(it != end())
- return &it->value();
- return nullptr;
- }
- value*
- object::
- if_contains(
- string_view key) noexcept
- {
- auto const it = find(key);
- if(it != end())
- return &it->value();
- return nullptr;
- }
- //----------------------------------------------------------
- //
- // (private)
- //
- //----------------------------------------------------------
- auto
- object::
- find_impl(
- string_view key) const noexcept ->
- std::pair<
- key_value_pair*,
- std::size_t>
- {
- BOOST_ASSERT(t_->capacity > 0);
- if(t_->is_small())
- {
- auto it = &(*t_)[0];
- auto const last =
- &(*t_)[t_->size];
- for(;it != last; ++it)
- if(key == it->key())
- return { it, 0 };
- return { nullptr, 0 };
- }
- std::pair<
- key_value_pair*,
- std::size_t> result;
- result.second = t_->digest(key);
- auto i = t_->bucket(
- result.second);
- while(i != null_index_)
- {
- auto& v = (*t_)[i];
- if(v.key() == key)
- {
- result.first = &v;
- return result;
- }
- i = access::next(v);
- }
- result.first = nullptr;
- return result;
- }
- auto
- object::
- insert_impl(
- pilfered<key_value_pair> p) ->
- std::pair<iterator, bool>
- {
- // caller is responsible
- // for preventing aliasing.
- reserve(size() + 1);
- auto const result =
- find_impl(p.get().key());
- if(result.first)
- return { result.first, false };
- return { insert_impl(
- p, result.second), true };
- }
- key_value_pair*
- object::
- insert_impl(
- pilfered<key_value_pair> p,
- std::size_t hash)
- {
- BOOST_ASSERT(
- capacity() > size());
- if(t_->is_small())
- {
- auto const pv = ::new(end())
- key_value_pair(p);
- ++t_->size;
- return pv;
- }
- auto& head =
- t_->bucket(hash);
- auto const pv = ::new(end())
- key_value_pair(p);
- access::next(*pv) = head;
- head = t_->size;
- ++t_->size;
- return pv;
- }
- // rehash to at least `n` buckets
- void
- object::
- rehash(std::size_t new_capacity)
- {
- BOOST_ASSERT(
- new_capacity > t_->capacity);
- auto t = table::allocate(
- growth(new_capacity),
- t_->salt, sp_);
- if(! empty())
- std::memcpy(
- static_cast<
- void*>(&(*t)[0]),
- begin(),
- size() * sizeof(
- key_value_pair));
- t->size = t_->size;
- table::deallocate(t_, sp_);
- t_ = t;
- if(! t_->is_small())
- {
- // rebuild hash table,
- // without dup checks
- auto p = end();
- index_t i = t_->size;
- while(i-- > 0)
- {
- --p;
- auto& head =
- t_->bucket(p->key());
- access::next(*p) = head;
- head = i;
- }
- }
- }
- bool
- object::
- equal(object const& other) const noexcept
- {
- if(size() != other.size())
- return false;
- auto const end_ = other.end();
- for(auto e : *this)
- {
- auto it = other.find(e.key());
- if(it == end_)
- return false;
- if(it->value() != e.value())
- return false;
- }
- return true;
- }
- std::size_t
- object::
- growth(
- std::size_t new_size) const
- {
- if(new_size > max_size())
- detail::throw_length_error(
- "object too large",
- BOOST_JSON_SOURCE_POS);
- std::size_t const old = capacity();
- if(old > max_size() - old / 2)
- return new_size;
- std::size_t const g =
- old + old / 2; // 1.5x
- if(g < new_size)
- return new_size;
- return g;
- }
- void
- object::
- remove(
- index_t& head,
- key_value_pair& v) noexcept
- {
- BOOST_ASSERT(! t_->is_small());
- auto const i = static_cast<
- index_t>(&v - begin());
- if(head == i)
- {
- head = access::next(v);
- return;
- }
- auto* pn =
- &access::next((*t_)[head]);
- while(*pn != i)
- pn = &access::next((*t_)[*pn]);
- *pn = access::next(v);
- }
- void
- object::
- destroy() noexcept
- {
- BOOST_ASSERT(t_->capacity > 0);
- BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
- destroy(begin(), end());
- table::deallocate(t_, sp_);
- }
- void
- object::
- destroy(
- key_value_pair* first,
- key_value_pair* last) noexcept
- {
- BOOST_ASSERT(! sp_.is_not_shared_and_deallocate_is_trivial());
- while(last != first)
- (--last)->~key_value_pair();
- }
- BOOST_JSON_NS_END
- #endif
|