From b113017930e653d66496c43a111e3e384d8cfab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Thu, 10 Mar 2022 10:30:24 +0100 Subject: [PATCH] Add minimal support for limited and experimental hash table --- include/boost/container/detail/hash_table.hpp | 1250 +++++++++++++++++ .../container/detail/node_alloc_holder.hpp | 51 +- 2 files changed, 1282 insertions(+), 19 deletions(-) create mode 100644 include/boost/container/detail/hash_table.hpp diff --git a/include/boost/container/detail/hash_table.hpp b/include/boost/container/detail/hash_table.hpp new file mode 100644 index 0000000..f857e0c --- /dev/null +++ b/include/boost/container/detail/hash_table.hpp @@ -0,0 +1,1250 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2018-2022. 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) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_CONTAINER_HASH_TABLE_HPP +#define BOOST_CONTAINER_HASH_TABLE_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include +// container +#include +#include +#include +#include + +// container/detail +#include //algo_equal(), algo_lexicographical_compare +#include +#include +#include +#include +#include +#include +#include + +// intrusive +#include +#include +// intrusive/detail +#include //pair +#include //tree_value_compare +// move +#include +// move/detail +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +#include +#endif +#include +#include +// other +#include + + + +#include + +namespace boost { +namespace container { +namespace dtl { + +static const unsigned hash_default_bucket = 11; + +using boost::intrusive::tree_value_compare; + +template +struct intrusive_hash_table_hook +{ + typedef typename dtl::bi::make_unordered_set_base_hook + < dtl::bi::void_pointer + , dtl::bi::link_mode + , dtl::bi::store_hash + >::type type; +}; + + +//This trait is used to type-pun std::pair because in C++03 +//compilers std::pair is useless for C++11 features +template +struct hash_table_internal_data_type +{ + typedef T type; +}; + +template +struct hash_table_internal_data_type< std::pair > +{ + typedef pair::type, T2> type; +}; + +template +struct iiterator_node_value_type< base_node > > +{ + typedef T type; +}; + + +template +struct hash_key_of_value +{ + typedef KeyOfValue type; +}; + +template +struct hash_key_of_value +{ + typedef dtl::identity type; +}; + +template +struct hash_key_of_value, int> +{ + typedef dtl::select1st type; +}; + +template +struct hash_key_of_value, int> +{ + typedef dtl::select1st type; +}; + +template +class hash_insert_equal_end_hint_functor +{ + Icont &icont_; + + public: + BOOST_CONTAINER_FORCEINLINE hash_insert_equal_end_hint_functor(Icont &icont) + : icont_(icont) + {} + + BOOST_CONTAINER_FORCEINLINE void operator()(Node &n) + { this->icont_.insert_equal(n); } +}; + +}//namespace dtl { + +namespace dtl { + +template< class NodeType, class NodeHashType, class NodeCompareType + , class SizeType, class HookType> +struct intrusive_hash_table_dispatch +{ + typedef typename dtl::bi::make_hashtable + + ,dtl::bi::equal + ,dtl::bi::base_hook + ,dtl::bi::constant_time_size + ,dtl::bi::size_type + >::type type; +}; + +template +struct intrusive_hash_table_type +{ + private: + typedef typename boost::container:: + allocator_traits::value_type value_type; + typedef typename boost::container:: + allocator_traits::void_pointer void_pointer; + typedef typename boost::container:: + allocator_traits::size_type size_type; + typedef base_node > node_t; + typedef value_to_node_compare + node_hash_type; + typedef value_to_node_compare + node_equal_type; + //Deducing the hook type from node_t (e.g. node_t::hook_type) would + //provoke an early instantiation of node_t that could ruin recursive + //hash_table definitions, so retype the complete type to avoid any problem. + typedef typename intrusive_hash_table_hook + ::type hook_type; + public: + typedef typename intrusive_hash_table_dispatch + < node_t, node_hash_type, node_equal_type + , size_type, hook_type>::type type; +}; + +} //namespace dtl { + +namespace dtl { + +//This functor will be used with Intrusive clone functions to obtain +//already allocated nodes from a intrusive container instead of +//allocating new ones. When the intrusive container runs out of nodes +//the node holder is used instead. +template +class HashRecyclingCloner +{ + typedef typename AllocHolder::intrusive_container intrusive_container; + typedef typename AllocHolder::Node node_t; + typedef typename AllocHolder::NodePtr node_ptr_type; + typedef dtl::allocator_node_destroyer + Destroyer; + typedef typename AllocHolder::alloc_version alloc_version; + + public: + HashRecyclingCloner(AllocHolder &holder, intrusive_container &itree) + : m_holder(holder), m_icont(itree) + {} + + BOOST_CONTAINER_FORCEINLINE static void do_assign(node_ptr_type &p, const node_t &other, bool_) + { p->do_move_assign(const_cast(other).m_data); } + + BOOST_CONTAINER_FORCEINLINE static void do_assign(node_ptr_type &p, const node_t &other, bool_) + { p->do_assign(other.m_data); } + + node_ptr_type operator()(const node_t &other) const + { + if(!m_icont.empty()) + { + typename intrusive_container::const_iterator iit + = m_icont.iterator_to(other); + node_ptr_type p = iit.unconst().operator->(); + m_icont.erase(iit); + //First recycle a node (this can't throw) + BOOST_TRY{ + //This can throw + this->do_assign(p, other, bool_()); + return p; + } + BOOST_CATCH(...){ + //If there is an exception destroy the whole source + m_holder.destroy_node(p); + m_holder.clear(alloc_version()); + BOOST_RETHROW + } + BOOST_CATCH_END + } + else{ + return m_holder.create_node(other.m_data); + } + } + + AllocHolder &m_holder; + intrusive_container &m_icont; +}; + + +template +struct get_hash_table_opt +{ + typedef Options type; +}; + +template<> +struct get_hash_table_opt +{ + typedef hash_assoc_defaults type; +}; + +template +struct hash_table_types +{ + typedef typename hash_key_of_value + < typename allocator_traits::value_type + , KeyOfValue>::type key_of_value_t; + typedef tree_value_compare + < typename allocator_traits::pointer + , KeyHash, key_of_value_t, std::size_t> ValHash; + typedef tree_value_compare + < typename allocator_traits::pointer + , KeyEqual, key_of_value_t, bool> ValEqual; + typedef typename get_hash_table_opt::type options_type; + typedef typename dtl::intrusive_hash_table_type + < Allocator, ValHash, ValEqual + , options_type::store_hash + >::type Icont; + typedef Icont::bucket_type bucket_type; + typedef Icont::bucket_traits bucket_traits; + typedef dtl::node_alloc_holder + AllocHolder; + + typedef typename boost::container:: + allocator_traits::template + portable_rebind_alloc::type bucket_allocator; + typedef std::vector bucket_holder_t; + +}; + +template +class hash_table + : public hash_table_types::bucket_holder_t + , public hash_table_types::AllocHolder +{ + typedef hash_table_types hash_table_types_t; + typedef typename hash_table_types_t::ValHash ValHash; + typedef typename hash_table_types_t::ValEqual ValEqual; + typedef typename hash_table_types_t::options_type options_type; + typedef typename hash_table_types_t::Icont Icont; + typedef typename hash_table_types_t::AllocHolder AllocHolder; + typedef typename AllocHolder::NodePtr NodePtr; + typedef hash_table< T, KeyOfValue, KeyHash + , KeyEqual, Allocator, Options> ThisType; + typedef typename AllocHolder::NodeAlloc NodeAlloc; + typedef boost::container:: + allocator_traits allocator_traits_type; + typedef typename AllocHolder::ValAlloc ValAlloc; + typedef typename AllocHolder::Node Node; + typedef typename Icont::iterator iiterator; + typedef typename Icont::const_iterator iconst_iterator; + typedef typename Icont::local_iterator ilocal_iterator; + typedef typename Icont::const_local_iterator iconst_local_iterator; + typedef dtl::allocator_node_destroyer Destroyer; + typedef typename AllocHolder::alloc_version alloc_version; + + typedef Icont::bucket_type bucket_type; + typedef Icont::bucket_traits bucket_traits; + typedef typename hash_table_types + + ::bucket_holder_t bucket_holder_t; + + + BOOST_COPYABLE_AND_MOVABLE(hash_table) + + public: + + typedef typename hash_table_types_t:: + key_of_value_t key_of_value_type; + typedef typename hash_table_types_t:: + key_of_value_t::type key_type; + typedef T value_type; + typedef Allocator allocator_type; + typedef KeyHash hasher; + typedef ValHash value_hasher; + typedef KeyEqual key_equal; + typedef ValEqual value_equal; + typedef typename boost::container:: + allocator_traits::pointer pointer; + typedef typename boost::container:: + allocator_traits::const_pointer const_pointer; + typedef typename boost::container:: + allocator_traits::reference reference; + typedef typename boost::container:: + allocator_traits::const_reference const_reference; + typedef typename boost::container:: + allocator_traits::size_type size_type; + typedef typename boost::container:: + allocator_traits::difference_type difference_type; + typedef dtl::iterator_from_iiterator + iterator; + typedef dtl::iterator_from_iiterator + const_iterator; + typedef dtl::iterator_from_iiterator + local_iterator; + typedef dtl::iterator_from_iiterator + const_local_iterator; + typedef node_handle + < NodeAlloc, void> node_type; + typedef insert_return_type_base + insert_return_type; + typedef NodeAlloc stored_allocator_type; + + + private: + + typedef key_node_pred KeyNodeEqual; + typedef key_node_pred KeyNodeHash; + + public: + + BOOST_CONTAINER_FORCEINLINE hash_table() + : AllocHolder() + { this->reserve(0); } + + BOOST_CONTAINER_FORCEINLINE explicit hash_table(const allocator_type& a) + : bucket_holder_t(a), AllocHolder(a) + { this->reserve(0); } + + BOOST_CONTAINER_FORCEINLINE explicit hash_table(size_type n) + : AllocHolder() + { this->reserve(n); } + + BOOST_CONTAINER_FORCEINLINE hash_table(size_type n, const hasher& hf) + : AllocHolder(value_hasher(hf)) + { this->reserve(n); } + + BOOST_CONTAINER_FORCEINLINE hash_table(size_type n, const hasher& hf, const key_equal& eql) + : AllocHolder(value_hasher(hf), value_equal(eql)) + { this->reserve(n); } + + BOOST_CONTAINER_FORCEINLINE hash_table(size_type n, const hasher& hf, const key_equal& eql, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(value_hasher(hf), value_equal(eql), a) + { this->reserve(n); } + + BOOST_CONTAINER_FORCEINLINE hash_table(size_type n, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(a) + { this->reserve(n); } + + BOOST_CONTAINER_FORCEINLINE hash_table(size_type n, const hasher& hf, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(value_hasher(hf), value_equal(), a) + { this->reserve(n); } + + template + hash_table(bool unique_insertion, InputIterator first, InputIterator last) + : AllocHolder() + { + this->reserve(0); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + template + hash_table(bool unique_insertion, InputIterator first, InputIterator last, size_type n) + : AllocHolder() + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + template + hash_table(bool unique_insertion, InputIterator first, InputIterator last, size_type n, const hasher& hf) + : AllocHolder(value_hasher(hf)) + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + template + hash_table(bool unique_insertion, InputIterator first, InputIterator last, size_type n, const hasher& hf, const key_equal& eql) + : AllocHolder(value_hasher(hf), value_equal(eql)) + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + template + hash_table + (bool unique_insertion, InputIterator first, InputIterator last + ,size_type n, const hasher& hf, const key_equal& eql, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(value_hasher(hf), value_equal(eql), a) + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + template + hash_table + (bool unique_insertion, InputIterator first, InputIterator last, size_type n, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(a) + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + //! Effects: Constructs an empty hash_table using at least n buckets + //! and specified allocator and hash function. + //! Then, inserts elements from the range [first ,last ). + //! + //! Complexity: Average case linear, worst case quadratic. + template + hash_table + (bool unique_insertion, InputIterator first, InputIterator last, size_type n, const hasher& hf, const allocator_type& a) + : bucket_holder_t(a), AllocHolder(value_hasher(hf), a) + { + this->reserve(n); + this->hash_table_construct(unique_insertion, first, last); + //AllocHolder clears in case of exception + } + + private: + + template + void hash_table_construct(bool unique_insertion, InputIterator first, InputIterator last) + { + //Use cend() as hint to achieve linear time for + //ordered ranges as required by the standard + //for the constructor + if(unique_insertion){ + const const_iterator end_it(this->cend()); + for ( ; first != last; ++first){ + this->insert_unique_convertible(end_it, *first); + } + } + else{ + this->hash_table_construct_non_unique(first, last); + } + } + + template + void hash_table_construct_non_unique(InputIterator first, InputIterator last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename dtl::enable_if_or + < void + , dtl::is_same + , dtl::is_input_iterator + >::type * = 0 + #endif + ) + { + //Use cend() as hint to achieve linear time for + //ordered ranges as required by the standard + //for the constructor + const const_iterator end_it(this->cend()); + for ( ; first != last; ++first){ + this->insert_equal_convertible(end_it, *first); + } + } + + template + void hash_table_construct_non_unique(InputIterator first, InputIterator last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename dtl::disable_if_or + < void + , dtl::is_same + , dtl::is_input_iterator + >::type * = 0 + #endif + ) + { + //Optimized allocation and construction + this->allocate_many_and_construct + ( first, boost::container::iterator_udistance(first, last) + , hash_insert_equal_end_hint_functor(this->icont())); + } + + public: + + BOOST_CONTAINER_FORCEINLINE hash_table(const hash_table& x) + : AllocHolder(x, x.value_hash_function(), x.value_eq()) + { + this->icont().clone_from + (x.icont(), typename AllocHolder::cloner(*this), Destroyer(this->node_alloc())); + } + + BOOST_CONTAINER_FORCEINLINE hash_table(BOOST_RV_REF(hash_table) x) + BOOST_NOEXCEPT_IF(boost::container::dtl::is_nothrow_move_constructible::value && + boost::container::dtl::is_nothrow_move_constructible::value) + : AllocHolder(BOOST_MOVE_BASE(AllocHolder, x), x.value_hash_function(), x.value_eq()) + {} + + BOOST_CONTAINER_FORCEINLINE hash_table(const hash_table& x, const allocator_type &a) + : AllocHolder(x.value_hash_function(), x.value_eq(), a) + { + this->icont().clone_from + (x.icont(), typename AllocHolder::cloner(*this), Destroyer(this->node_alloc())); + //AllocHolder clears in case of exception + } + + hash_table(BOOST_RV_REF(hash_table) x, const allocator_type &a) + : AllocHolder(x.value_hash_function(), x.value_eq(), a) + { + if(this->node_alloc() == x.node_alloc()){ + this->icont().swap(x.icont()); + } + else{ + this->icont().clone_from + (boost::move(x.icont()), typename AllocHolder::move_cloner(*this), Destroyer(this->node_alloc())); + } + //AllocHolder clears in case of exception + } + + BOOST_CONTAINER_FORCEINLINE ~hash_table() + { + } //AllocHolder clears the hash_table + + hash_table& operator=(BOOST_COPY_ASSIGN_REF(hash_table) x) + { + if (&x != this){ + NodeAlloc &this_alloc = this->get_stored_allocator(); + const NodeAlloc &x_alloc = x.get_stored_allocator(); + dtl::bool_:: + propagate_on_container_copy_assignment::value> flag; + if(flag && this_alloc != x_alloc){ + this->clear(); + } + this->AllocHolder::copy_assign_alloc(x); + //Transfer all the nodes to a temporary hash_table + //If anything goes wrong, all the nodes will be destroyed + //automatically + Icont other_hash_table(::boost::move(this->icont())); + + //Now recreate the source hash_table reusing nodes stored by other_hash_table + this->icont().clone_from + (x.icont() + , HashRecyclingCloner(*this, other_hash_table) + , Destroyer(this->node_alloc())); + + //If there are remaining nodes, destroy them + other_hash_table.clear_and_dispose(Destroyer(this->node_alloc())); + } + return *this; + } + + hash_table& operator=(BOOST_RV_REF(hash_table) x) + BOOST_NOEXCEPT_IF( (allocator_traits_type::propagate_on_container_move_assignment::value || + allocator_traits_type::is_always_equal::value) && + boost::container::dtl::is_nothrow_move_assignable::value && + boost::container::dtl::is_nothrow_move_assignable::value) + { + BOOST_ASSERT(this != &x); + NodeAlloc &this_alloc = this->node_alloc(); + NodeAlloc &x_alloc = x.node_alloc(); + const bool propagate_alloc = allocator_traits:: + propagate_on_container_move_assignment::value; + const bool allocators_equal = this_alloc == x_alloc; (void)allocators_equal; + //Resources can be transferred if both allocators are + //going to be equal after this function (either propagated or already equal) + if(propagate_alloc || allocators_equal){ + //Destroy + this->clear(); + //Move allocator if needed + this->AllocHolder::move_assign_alloc(x); + //Obtain resources + this->icont() = boost::move(x.icont()); + } + //Else do a one by one move + else{ + //Transfer all the nodes to a temporary hash_table + //If anything goes wrong, all the nodes will be destroyed + //automatically + Icont other_hash_table(::boost::move(this->icont())); + + //Now recreate the source hash_table reusing nodes stored by other_hash_table + this->icont().clone_from + (::boost::move(x.icont()) + , HashRecyclingCloner(*this, other_hash_table) + , Destroyer(this->node_alloc())); + + //If there are remaining nodes, destroy them + other_hash_table.clear_and_dispose(Destroyer(this->node_alloc())); + } + return *this; + } + + public: + // accessors: + BOOST_CONTAINER_FORCEINLINE key_equal key_eq() const + { return this->icont().key_eq().predicate().key_comp(); } + + BOOST_CONTAINER_FORCEINLINE value_equal value_eq() const + { return value_equal(this->key_eq()); } + + BOOST_CONTAINER_FORCEINLINE hasher hash_function() const + { return this->icont().hash_function().predicate().key_comp(); } + + BOOST_CONTAINER_FORCEINLINE value_hasher value_hash_function() const + { return value_hasher(this->hash_function()); } + + BOOST_CONTAINER_FORCEINLINE allocator_type get_allocator() const + { return allocator_type(this->node_alloc()); } + + BOOST_CONTAINER_FORCEINLINE const stored_allocator_type &get_stored_allocator() const + { return this->node_alloc(); } + + BOOST_CONTAINER_FORCEINLINE stored_allocator_type &get_stored_allocator() + { return this->node_alloc(); } + + BOOST_CONTAINER_FORCEINLINE iterator begin() + { return iterator(this->icont().begin()); } + + BOOST_CONTAINER_FORCEINLINE const_iterator begin() const + { return this->cbegin(); } + + BOOST_CONTAINER_FORCEINLINE iterator end() + { return iterator(this->icont().end()); } + + BOOST_CONTAINER_FORCEINLINE const_iterator end() const + { return this->cend(); } + + //! Effects: Returns a const_iterator to the first element contained in the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + BOOST_CONTAINER_FORCEINLINE const_iterator cbegin() const + { return const_iterator(this->non_const_icont().begin()); } + + //! Effects: Returns a const_iterator to the end of the container. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + BOOST_CONTAINER_FORCEINLINE const_iterator cend() const + { return const_iterator(this->non_const_icont().end()); } + + BOOST_CONTAINER_FORCEINLINE bool empty() const + { return !this->size(); } + + BOOST_CONTAINER_FORCEINLINE size_type size() const + { return this->icont().size(); } + + BOOST_CONTAINER_FORCEINLINE size_type max_size() const + { return AllocHolder::max_size(); } + + BOOST_CONTAINER_FORCEINLINE void swap(ThisType& x) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::dtl::is_nothrow_swappable::value + && boost::container::dtl::is_nothrow_swappable::value ) + { AllocHolder::swap(x); } + + public: + + typedef typename Icont::insert_commit_data insert_commit_data; + + // insert/erase + std::pair insert_unique_check + (const key_type& key, insert_commit_data &data) + { + std::pair ret = + this->icont().insert_unique_check + (key, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data); + return std::pair(iterator(ret.first), ret.second); + } + + std::pair insert_unique_check + (const_iterator hint, const key_type& key, insert_commit_data &data) + { + //to-do: take advantage of hint: just check for equality and insert after if equal + (void)hint; + BOOST_ASSERT((priv_is_linked)(hint)); + std::pair ret = + this->icont().insert_unique_check + (key, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data); + return std::pair(iterator(ret.first), ret.second); + } + + template + iterator insert_unique_commit + (BOOST_FWD_REF(MovableConvertible) v, insert_commit_data &data) + { + NodePtr tmp = AllocHolder::create_node(boost::forward(v)); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + iterator ret(this->icont().insert_unique_commit(*tmp, data)); + destroy_deallocator.release(); + return ret; + } + + template + std::pair insert_unique(BOOST_FWD_REF(MovableConvertible) v) + { + insert_commit_data data; + std::pair ret = + this->insert_unique_check(key_of_value_type()(v), data); + if(ret.second){ + ret.first = this->insert_unique_commit(boost::forward(v), data); + } + return ret; + } + + private: + + template + iiterator priv_insert_or_assign_commit + (BOOST_FWD_REF(KeyConvertible) key, BOOST_FWD_REF(M) obj, insert_commit_data &data) + { + NodePtr tmp = AllocHolder::create_node(boost::forward(key), boost::forward(obj)); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + iiterator ret(this->icont().insert_unique_commit(*tmp, data)); + destroy_deallocator.release(); + return ret; + } + + bool priv_is_linked(const_iterator const position) const + { + iiterator const cur(position.get()); + return cur == this->icont().end() || + (++iiterator(cur) != cur && + ++iiterator(cur) != iiterator()); + } + + template + void push_back_impl(BOOST_FWD_REF(MovableConvertible) v) + { + NodePtr tmp(AllocHolder::create_node(boost::forward(v))); + //push_back has no-throw guarantee so avoid any deallocator/destroyer + this->icont().push_back(*tmp); + } + + std::pair emplace_unique_impl(NodePtr p) + { + value_type &v = p->get_data(); + insert_commit_data data; + scoped_node_destroy_deallocator destroy_deallocator(p, this->node_alloc()); + std::pair ret = + this->insert_unique_check(key_of_value_type()(v), data); + if(!ret.second){ + return ret; + } + //No throw insertion part, release rollback + destroy_deallocator.release(); + return std::pair + ( iterator(this->icont().insert_unique_commit(*p, data)) + , true ); + } + + iterator emplace_unique_hint_impl(const_iterator hint, NodePtr p) + { + BOOST_ASSERT((priv_is_linked)(hint)); + value_type &v = p->get_data(); + insert_commit_data data; + std::pair ret = + this->insert_unique_check(hint, key_of_value_type()(v), data); + if(!ret.second){ + Destroyer(this->node_alloc())(p); + return ret.first; + } + return iterator(this->icont().insert_unique_commit(*p, data)); + } + + public: + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + BOOST_CONTAINER_FORCEINLINE std::pair emplace_unique(BOOST_FWD_REF(Args)... args) + { return this->emplace_unique_impl(AllocHolder::create_node(boost::forward(args)...)); } + + template + BOOST_CONTAINER_FORCEINLINE iterator emplace_hint_unique(const_iterator hint, BOOST_FWD_REF(Args)... args) + { return this->emplace_unique_hint_impl(hint, AllocHolder::create_node(boost::forward(args)...)); } + + template + iterator emplace_equal(BOOST_FWD_REF(Args)... args) + { + NodePtr tmp(AllocHolder::create_node(boost::forward(args)...)); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + iterator ret(this->icont().insert_equal(*tmp)); + destroy_deallocator.release(); + return ret; + } + + template + iterator emplace_hint_equal(const_iterator hint, BOOST_FWD_REF(Args)... args) + { + BOOST_ASSERT((priv_is_linked)(hint)); + NodePtr tmp(AllocHolder::create_node(boost::forward(args)...)); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + //to-do: take advantage of hint: just check for equality and insert after if equal + iterator ret(this->icont().insert_equal(*tmp)); + destroy_deallocator.release(); + return ret; + } + + template + BOOST_CONTAINER_FORCEINLINE std::pair try_emplace + (const_iterator hint, BOOST_FWD_REF(KeyType) key, BOOST_FWD_REF(Args)... args) + { + insert_commit_data data; + const key_type & k = key; //Support emulated rvalue references + std::pair ret = + hint == const_iterator() ? this->icont().insert_unique_check\ + (k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data) + : this->icont().insert_unique_check\ + (hint.get(), k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data); + if(ret.second){ + ret.first = this->icont().insert_unique_commit + (*AllocHolder::create_node(try_emplace_t(), boost::forward(key), boost::forward(args)...), data); + } + return std::pair(iterator(ret.first), ret.second); + } + + #else // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + #define BOOST_CONTAINER_TREE_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + std::pair emplace_unique(BOOST_MOVE_UREF##N)\ + { return this->emplace_unique_impl(AllocHolder::create_node(BOOST_MOVE_FWD##N)); }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint_unique(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + { return this->emplace_unique_hint_impl(hint, AllocHolder::create_node(BOOST_MOVE_FWD##N)); }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_equal(BOOST_MOVE_UREF##N)\ + {\ + NodePtr tmp(AllocHolder::create_node(BOOST_MOVE_FWD##N));\ + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc());\ + iterator ret(this->icont().insert_equal(this->icont().end(), *tmp));\ + destroy_deallocator.release();\ + return ret;\ + }\ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + iterator emplace_hint_equal(const_iterator hint BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + BOOST_ASSERT((priv_is_linked)(hint));\ + NodePtr tmp(AllocHolder::create_node(BOOST_MOVE_FWD##N));\ + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc());\ + iterator ret(this->icont().insert_equal(hint.get(), *tmp));\ + destroy_deallocator.release();\ + return ret;\ + }\ + \ + template \ + BOOST_CONTAINER_FORCEINLINE std::pair\ + try_emplace(const_iterator hint, BOOST_FWD_REF(KeyType) key BOOST_MOVE_I##N BOOST_MOVE_UREF##N)\ + {\ + insert_commit_data data;\ + const key_type & k = key;\ + std::pair ret =\ + hint == const_iterator() ? this->icont().insert_unique_check\ + ( k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data)\ + : this->icont().insert_unique_check\ + (hint.get(), k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data);\ + if(ret.second){\ + ret.first = this->icont().insert_unique_commit\ + (*AllocHolder::create_node(try_emplace_t(), boost::forward(key) BOOST_MOVE_I##N BOOST_MOVE_FWD##N), data);\ + }\ + return std::pair(iterator(ret.first), ret.second);\ + }\ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_TREE_EMPLACE_CODE) + #undef BOOST_CONTAINER_TREE_EMPLACE_CODE + + #endif // !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + template + iterator insert_unique_convertible(const_iterator hint, BOOST_FWD_REF(MovableConvertible) v) + { + BOOST_ASSERT((priv_is_linked)(hint)); + insert_commit_data data; + std::pair ret = + this->insert_unique_check(hint, key_of_value_type()(v), data); + if(!ret.second) + return ret.first; + return this->insert_unique_commit(boost::forward(v), data); + } + + BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert_unique, value_type, iterator, this->insert_unique_convertible, const_iterator, const_iterator) + + template + void insert_unique(InputIterator first, InputIterator last) + { + for( ; first != last; ++first) + this->insert_unique(*first); + } + + iterator insert_equal(const value_type& v) + { + NodePtr tmp(AllocHolder::create_node(v)); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + //to-do: take advantage of hint: just check for equality and insert after if equal + iterator ret(this->icont().insert_equal(*tmp)); // + destroy_deallocator.release(); + return ret; + } + + //IOG temp + BOOST_CONTAINER_FORCEINLINE iterator insert(const value_type& v) + { + this->reserve(this->size() + 1u); + return this->insert_unique(v).first; + } + + template + iterator insert_equal(BOOST_FWD_REF(MovableConvertible) v) + { + NodePtr tmp(AllocHolder::create_node(boost::forward(v))); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + iterator ret(this->icont().insert_equal(*tmp)); + destroy_deallocator.release(); + return ret; + } + + template + iterator insert_equal_convertible(const_iterator hint, BOOST_FWD_REF(MovableConvertible) v) + { + BOOST_ASSERT((priv_is_linked)(hint)); + NodePtr tmp(AllocHolder::create_node(boost::forward(v))); + scoped_node_destroy_deallocator destroy_deallocator(tmp, this->node_alloc()); + //to-do: take advantage of hint: just check for equality and insert after if equal + (void)hint; + iterator ret(this->icont().insert_equal(*tmp)); + destroy_deallocator.release(); + return ret; + } + + BOOST_MOVE_CONVERSION_AWARE_CATCH_1ARG(insert_equal, value_type, iterator, this->insert_equal_convertible, const_iterator, const_iterator) + + template + void insert_equal(InputIterator first, InputIterator last) + { + for( ; first != last; ++first) + this->insert_equal(*first); + } + + template + std::pair insert_or_assign(const_iterator hint, BOOST_FWD_REF(KeyType) key, BOOST_FWD_REF(M) obj) + { + insert_commit_data data; + const key_type & k = key; //Support emulated rvalue references + std::pair ret = + hint == const_iterator() ? this->icont().insert_unique_check\ + (k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data) + : this->icont().insert_unique_check\ + (hint.get(), k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), data); + if(ret.second){ + ret.first = this->priv_insert_or_assign_commit(boost::forward(key), boost::forward(obj), data); + } + else{ + ret.first->get_data().second = boost::forward(obj); + } + return std::pair(iterator(ret.first), ret.second); + } + + void erase(const_iterator position) + { + BOOST_ASSERT(position != this->cend() && (priv_is_linked)(position)); + return this->icont().erase_and_dispose(position.get(), Destroyer(this->node_alloc())); + } + + BOOST_CONTAINER_FORCEINLINE size_type erase(const key_type& k) + { return AllocHolder::erase_key(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()), alloc_version()); } + + iterator erase(const_iterator first, const_iterator last) + { + BOOST_ASSERT(first == last || (first != this->cend() && (priv_is_linked)(first))); + BOOST_ASSERT(first == last || (priv_is_linked)(last)); + return iterator(AllocHolder::erase_range(first.get(), last.get(), alloc_version())); + } + + node_type extract(const key_type& k) + { + iterator const it = this->find(k); + if(this->end() != it){ + return this->extract(it); + } + return node_type(); + } + + node_type extract(const_iterator position) + { + BOOST_ASSERT(position != this->cend() && (priv_is_linked)(position)); + iiterator const iit(position.get()); + this->icont().erase(iit); + return node_type(iit.operator->(), this->node_alloc()); + } + + insert_return_type insert_unique_node(BOOST_RV_REF_BEG_IF_CXX11 node_type BOOST_RV_REF_END_IF_CXX11 nh) + { + return this->insert_unique_node(this->end(), boost::move(nh)); + } + + insert_return_type insert_unique_node(const_iterator hint, BOOST_RV_REF_BEG_IF_CXX11 node_type BOOST_RV_REF_END_IF_CXX11 nh) + { + insert_return_type irt; //inserted == false, node.empty() + if(!nh.empty()){ + insert_commit_data data; + std::pair ret = + this->insert_unique_check(hint, key_of_value_type()(nh.value()), data); + if(ret.second){ + irt.inserted = true; + irt.position = iterator(this->icont().insert_unique_commit(*nh.get(), data)); + nh.release(); + } + else{ + irt.position = ret.first; + irt.node = boost::move(nh); + } + } + else{ + irt.position = this->end(); + } + return BOOST_MOVE_RET(insert_return_type, irt); + } + + iterator insert_equal_node(BOOST_RV_REF_BEG_IF_CXX11 node_type BOOST_RV_REF_END_IF_CXX11 nh) + { + if(nh.empty()){ + return this->end(); + } + else{ + NodePtr const p(nh.release()); + return iterator(this->icont().insert_equal(*p)); + } + } + + iterator insert_equal_node(const_iterator hint, BOOST_RV_REF_BEG_IF_CXX11 node_type BOOST_RV_REF_END_IF_CXX11 nh) + { + if(nh.empty()){ + return this->end(); + } + else{ + NodePtr const p(nh.release()); + //to-do: take advantage of hint: just check for equality and insert after if equal + return iterator(this->icont().insert_equal(*p)); (void)hint; + } + } + + template + BOOST_CONTAINER_FORCEINLINE void merge_unique(hash_table& ) + { assert(0); } //TODO + + template + BOOST_CONTAINER_FORCEINLINE void merge_equal(hash_table& ) + { assert(0); } //TODO + + BOOST_CONTAINER_FORCEINLINE void clear() + { this->AllocHolder::clear(alloc_version()); } + + // search operations. Const and non-const overloads even if no iterator is returned + // so splay implementations can to their rebalancing when searching in non-const versions + BOOST_CONTAINER_FORCEINLINE iterator find(const key_type& k) + { return iterator(this->icont().find(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()))); } + + BOOST_CONTAINER_FORCEINLINE const_iterator find(const key_type& k) const + { return const_iterator(this->non_const_icont().find(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()))); } + + BOOST_CONTAINER_FORCEINLINE size_type count(const key_type& k) const + { return size_type(this->icont().count(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq()))); } + + BOOST_CONTAINER_FORCEINLINE bool contains(const key_type& x) const + { return this->find(x) != this->cend(); } + + std::pair equal_range(const key_type& k) + { + std::pair ret = + this->icont().equal_range(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq())); + return std::pair(iterator(ret.first), iterator(ret.second)); + } + + std::pair equal_range(const key_type& k) const + { + std::pair ret = + this->non_const_icont().equal_range(k, KeyNodeHash(hash_function()), KeyNodeEqual(key_eq())); + return std::pair + (const_iterator(ret.first), const_iterator(ret.second)); + } + + BOOST_CONTAINER_FORCEINLINE std::pair equal_range_unique(const key_type& k) + { return priv_equal_range_unique(*this, k); } + + BOOST_CONTAINER_FORCEINLINE std::pair equal_range_unique(const key_type& k) const + { return priv_equal_range_unique(*this, k); } + + BOOST_CONTAINER_FORCEINLINE size_type bucket_count() const BOOST_NOEXCEPT + { return this->icont().bucket_count(); } + + BOOST_CONTAINER_FORCEINLINE size_type max_bucket_count() const BOOST_NOEXCEPT + { return this->max_size(); } + + BOOST_CONTAINER_FORCEINLINE size_type bucket_size(size_type n) const + { return this->icont().bucket_size(n); } + + BOOST_CONTAINER_FORCEINLINE size_type bucket(const key_type& k) const + { return this->icont().bucket(k, KeyNodeHash(hash_function())); } + + BOOST_CONTAINER_FORCEINLINE local_iterator begin(size_type n) + { return local_iterator(this->icont().begin(n)); } + + BOOST_CONTAINER_FORCEINLINE const_local_iterator begin(size_type n) const + { return const_local_iterator(this->non_const_icont().begin(n)); } + + BOOST_CONTAINER_FORCEINLINE const_local_iterator cbegin(size_type n) const + { return this->cbegin(n); } + + BOOST_CONTAINER_FORCEINLINE local_iterator end(size_type n) + { return local_iterator(this->icont().end(n)); } + + BOOST_CONTAINER_FORCEINLINE const_local_iterator end(size_type n) const + { return const_local_iterator(this->non_const_icont().end(n)); } + + BOOST_CONTAINER_FORCEINLINE const_local_iterator cend(size_type n) const + { return this->cend(n); } + + float load_factor() const BOOST_NOEXCEPT + { return this->icont().load_factor(); } + + float max_load_factor() const BOOST_NOEXCEPT + { return this->icont().max_load_factor(); } + + void max_load_factor(float z) + { + assert(0); + } + + void rehash(size_type n) + { + (void)n; + //! Effects: bucket_count() >= size() / max_load_factor() and bucket_count() >= n. + assert(0); + } + + void reserve(size_type n) + { + if (!n || this->bucket_count() < n) { + std::size_t sc = Icont::suggested_upper_bucket_count(n); + bucket_holder_t& this_buckets = *this; + bucket_holder_t new_buckets(sc, this_buckets.get_allocator()); + this->icont().rehash(bucket_traits(new_buckets.data(), sc)); + this_buckets.swap(new_buckets); + } + } + + BOOST_CONTAINER_FORCEINLINE friend bool operator==(const hash_table& x, const hash_table& y) + { return x.size() == y.size() && ::boost::container::algo_equal(x.begin(), x.end(), y.begin()); } + + BOOST_CONTAINER_FORCEINLINE friend bool operator!=(const hash_table& x, const hash_table& y) + { return !(x == y); } + + BOOST_CONTAINER_FORCEINLINE friend void swap(hash_table& x, hash_table& y) + BOOST_NOEXCEPT_IF( allocator_traits_type::is_always_equal::value + && boost::container::dtl::is_nothrow_swappable::value + && boost::container::dtl::is_nothrow_swappable::value ) + { x.swap(y); } + + BOOST_CONTAINER_FORCEINLINE friend std::size_t hash_value(const hash_table& x) + { return boost::hash_range(x.cbegin(), x.end()); } + + private: + template + BOOST_CONTAINER_FORCEINLINE + static std::pair priv_equal_range_unique(C &c, const K& k) + { + I i = c.find(k); + I j = i; + if (i != c.end()) + ++j; + return std::pair(i, j); + } +}; + +} //namespace dtl { +} //namespace container { + +template +struct has_trivial_destructor_after_move; + +//!has_trivial_destructor_after_move<> == true_type +//!specialization for optimizations +template +struct has_trivial_destructor_after_move + < + ::boost::container::dtl::hash_table + + > +{ + typedef typename ::boost::container::allocator_traits::pointer pointer; + static const bool value = ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value && + ::boost::has_trivial_destructor_after_move::value; +}; + +} //namespace boost { + +#include + +#endif //BOOST_CONTAINER_HASH_TABLE_HPP diff --git a/include/boost/container/detail/node_alloc_holder.hpp b/include/boost/container/detail/node_alloc_holder.hpp index bb2cdc9..c2830d4 100644 --- a/include/boost/container/detail/node_alloc_holder.hpp +++ b/include/boost/container/detail/node_alloc_holder.hpp @@ -256,7 +256,7 @@ struct node_alloc_holder public: //Constructors for sequence containers - node_alloc_holder() + BOOST_CONTAINER_FORCEINLINE node_alloc_holder() {} explicit node_alloc_holder(const ValAlloc &a) @@ -330,30 +330,30 @@ struct node_alloc_holder , typename ICont::key_equal(eql)) { this->icont().swap(BOOST_MOVE_TO_LV(x).icont()); } - void copy_assign_alloc(const node_alloc_holder &x) + BOOST_CONTAINER_FORCEINLINE void copy_assign_alloc(const node_alloc_holder &x) { dtl::bool_ flag; dtl::assign_alloc( static_cast(*this) , static_cast(x), flag); } - void move_assign_alloc( node_alloc_holder &x) + BOOST_CONTAINER_FORCEINLINE void move_assign_alloc( node_alloc_holder &x) { dtl::bool_ flag; dtl::move_alloc( static_cast(*this) , static_cast(x), flag); } - ~node_alloc_holder() + BOOST_CONTAINER_FORCEINLINE ~node_alloc_holder() { this->clear(alloc_version()); } - size_type max_size() const + BOOST_CONTAINER_FORCEINLINE size_type max_size() const { return allocator_traits_type::max_size(this->node_alloc()); } - NodePtr allocate_one() + BOOST_CONTAINER_FORCEINLINE NodePtr allocate_one() { return AllocVersionTraits::allocate_one(this->node_alloc()); } - void deallocate_one(const NodePtr &p) + BOOST_CONTAINER_FORCEINLINE void deallocate_one(const NodePtr &p) { AllocVersionTraits::deallocate_one(this->node_alloc(), p); } #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) @@ -488,7 +488,7 @@ struct node_alloc_holder } } - void clear(version_1) + BOOST_CONTAINER_FORCEINLINE void clear(version_1) { this->icont().clear_and_dispose(Destroyer(this->node_alloc())); } void clear(version_2) @@ -515,24 +515,37 @@ struct node_alloc_holder } template - size_type erase_key(const Key& k, const Comparator &comp, version_1) + BOOST_CONTAINER_FORCEINLINE size_type erase_key(const Key& k, const Comparator &comp, version_1) { return this->icont().erase_and_dispose(k, comp, Destroyer(this->node_alloc())); } template - size_type erase_key(const Key& k, const Comparator &comp, version_2) + BOOST_CONTAINER_FORCEINLINE size_type erase_key(const Key& k, const Comparator &comp, version_2) { allocator_multialloc_chain_node_deallocator chain_holder(this->node_alloc()); return this->icont().erase_and_dispose(k, comp, chain_holder.get_chain_builder()); } + template + BOOST_CONTAINER_FORCEINLINE size_type erase_key(const Key& k, const Equality& eq, const Hasher &h, version_1) + { + return this->icont().erase_and_dispose(k, eq, h, Destroyer(this->node_alloc())); + } + + template + size_type erase_key(const Key& k, const Equality& eq, const Hasher& h, version_2) + { + allocator_multialloc_chain_node_deallocator chain_holder(this->node_alloc()); + return this->icont().erase_and_dispose(k, eq, h, chain_holder.get_chain_builder()); + } + protected: struct cloner { - explicit cloner(node_alloc_holder &holder) + BOOST_CONTAINER_FORCEINLINE explicit cloner(node_alloc_holder &holder) : m_holder(holder) {} - NodePtr operator()(const Node &other) const + BOOST_CONTAINER_FORCEINLINE NodePtr operator()(const Node &other) const { return m_holder.create_node(other.get_real_data()); } node_alloc_holder &m_holder; @@ -540,11 +553,11 @@ struct node_alloc_holder struct move_cloner { - move_cloner(node_alloc_holder &holder) + BOOST_CONTAINER_FORCEINLINE move_cloner(node_alloc_holder &holder) : m_holder(holder) {} - NodePtr operator()(Node &other) + BOOST_CONTAINER_FORCEINLINE NodePtr operator()(Node &other) { //Use get_real_data() instead of get_real_data to allow moving const key in [multi]map return m_holder.create_node(::boost::move(other.get_real_data())); } @@ -552,20 +565,20 @@ struct node_alloc_holder node_alloc_holder &m_holder; }; - ICont &non_const_icont() const + BOOST_CONTAINER_FORCEINLINE ICont &non_const_icont() const { return const_cast(this->m_icont); } - NodeAlloc &node_alloc() + BOOST_CONTAINER_FORCEINLINE NodeAlloc &node_alloc() { return static_cast(*this); } - const NodeAlloc &node_alloc() const + BOOST_CONTAINER_FORCEINLINE const NodeAlloc &node_alloc() const { return static_cast(*this); } public: - ICont &icont() + BOOST_CONTAINER_FORCEINLINE ICont &icont() { return this->m_icont; } - const ICont &icont() const + BOOST_CONTAINER_FORCEINLINE const ICont &icont() const { return this->m_icont; } private: