diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp new file mode 100644 index 00000000..29fedd29 --- /dev/null +++ b/include/boost/unordered/detail/buckets.hpp @@ -0,0 +1,262 @@ + +// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. +// Copyright (C) 2005-2009 Daniel James +// 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) + +#ifndef BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED +#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED + +#include +#include +#include + +namespace boost { namespace unordered_detail { + + //////////////////////////////////////////////////////////////////////////// + // Explicitly call a destructor + +#if defined(BOOST_MSVC) +# define BOOST_UNORDERED_DESTRUCT(x, type) (x)->~type(); +#else +# define BOOST_UNORDERED_DESTRUCT(x, type) boost::unordered_detail::destroy(x) + template + void destroy(T* x) { + x->~T(); + } +#endif + + // Constructors + + template + hash_buckets::hash_buckets(node_allocator const& a, std::size_t bucket_count) + : buckets_(), allocators_(a,a) + { + // The array constructor will clean up in the event of an + // exception. + allocator_array_constructor + constructor(bucket_alloc()); + + // Creates an extra bucket to act as a sentinel. + constructor.construct(bucket(), bucket_count + 1); + + // Set up the sentinel (node_ptr cast) + bucket_ptr sentinel = constructor.get() + static_cast(bucket_count); + sentinel->next_ = sentinel; + + // Only release the buckets once everything is successfully + // done. + this->buckets_ = constructor.release(); + this->bucket_count_ = bucket_count; + } + + template + hash_buckets::hash_buckets(hash_buckets& x, move_tag) + : buckets_(), allocators_(x.allocators_) + { + this->buckets_ = x.buckets_; + this->bucket_count_ = x.bucket_count_; + x.buckets_ = bucket_ptr(); + x.bucket_count_ = 0; + } + + template + hash_buckets::hash_buckets(hash_buckets& x, value_allocator const& a, move_tag) : + buckets_(), allocators_(a,a) + { + if(this->node_alloc() == x.node_alloc()) { + this->buckets_ = x.buckets_; + this->bucket_count_ = x.bucket_count_; + x.buckets_ = bucket_ptr(); + x.bucket_count_ = 0; + } + } + + template + hash_buckets::~hash_buckets() + { + if(this->buckets_) { delete_buckets(); } + } + + // no throw + template + inline void hash_buckets::move(hash_buckets& other) + { + BOOST_ASSERT(node_alloc() == other.node_alloc()); + delete_buckets(); + this->buckets_ = other.buckets_; + this->bucket_count_ = other.bucket_count_; + other.buckets_ = bucket_ptr(); + other.bucket_count_ = 0; + } + + template + inline void hash_buckets::swap(hash_buckets& other) + { + BOOST_ASSERT(node_alloc() == other.node_alloc()); + std::swap(buckets_, other.buckets_); + std::swap(bucket_count_, other.bucket_count_); + } + + // Buckets + + template + inline std::size_t hash_buckets::bucket_count() const + { + return bucket_count_; + } + + template + inline std::size_t hash_buckets::bucket_from_hash(std::size_t hashed) const + { + return hashed % bucket_count_; + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr + hash_buckets::bucket_ptr_from_hash(std::size_t hashed) const + { + return buckets_ + static_cast( + bucket_from_hash(hashed)); + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr + hash_buckets::buckets_begin() const + { + return buckets_; + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr + hash_buckets::buckets_end() const + { + return buckets_ + static_cast(bucket_count_); + } + + template + inline std::size_t hash_buckets::bucket_size(std::size_t index) const + { + bucket_ptr ptr = (buckets_ + static_cast(index))->next_; + std::size_t count = 0; + while(ptr) { + ++count; + ptr = next_node(ptr); + } + return count; + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr + hash_buckets::get_bucket(std::size_t n) const + { + return buckets_ + static_cast(n); + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::node_ptr + hash_buckets::bucket_begin(std::size_t n) const + { + return (buckets_ + static_cast(n))->next_; + } + + template + inline BOOST_DEDUCED_TYPENAME hash_buckets::node_ptr + hash_buckets::bucket_end(std::size_t) const + { + return node_ptr(); + } + + // Construct/destruct + + template + inline void hash_buckets::destruct_node(node_ptr b) + { + node* raw_ptr = static_cast(&*b); + BOOST_UNORDERED_DESTRUCT(&raw_ptr->value(), value_type); + real_node_ptr n(node_alloc().address(*raw_ptr)); + node_alloc().destroy(n); + node_alloc().deallocate(n, 1); + } + + // Delete and clear buckets + + template + inline void hash_buckets::delete_group(node_ptr first_node) + { + delete_nodes(first_node, node::next_group(first_node)); + } + + template + inline void hash_buckets::delete_nodes(node_ptr begin, node_ptr end) + { + while(begin != end) { + node_ptr node = begin; + begin = next_node(begin); + destruct_node(node); + } + } + + template + inline void hash_buckets::delete_to_bucket_end(node_ptr begin) + { + while(BOOST_UNORDERED_BORLAND_BOOL(begin)) { + node_ptr node = begin; + begin = next_node(begin); + destruct_node(node); + } + } + + template + inline void hash_buckets::clear_bucket(bucket_ptr b) + { + node_ptr node_it = b->next_; + b->next_ = node_ptr(); + + while(node_it) { + node_ptr node_to_destruct = node_it; + node_it = next_node(node_it); + destruct_node(node_to_destruct); + } + } + + template + inline void hash_buckets::delete_buckets() + { + for(bucket_ptr begin = this->buckets_begin(), end = this->buckets_end(); begin != end; ++begin) { + clear_bucket(begin); + } + + // Destroy the buckets (including the sentinel bucket). + bucket_ptr end = this->buckets_end(); + ++end; + for(bucket_ptr begin = this->buckets_begin(); begin != end; ++begin) { + bucket_alloc().destroy(begin); + } + + bucket_alloc().deallocate(this->buckets_begin(), this->bucket_count() + 1); + + this->buckets_ = bucket_ptr(); + } + + //////////////////////////////////////////////////////////////////////////// + // hash_iterator_base implementation + + template + inline void hash_iterator_base::increment(node_ptr node) { + while(!node) { + ++bucket_; + node = bucket_->next_; + } + + node_ = node; + } + + template + inline void hash_iterator_base::increment() + { + increment(next_node(node_)); + } +}} + +#endif diff --git a/include/boost/unordered/detail/fwd.hpp b/include/boost/unordered/detail/fwd.hpp index 2d18775a..7cd56174 100644 --- a/include/boost/unordered/detail/fwd.hpp +++ b/include/boost/unordered/detail/fwd.hpp @@ -183,7 +183,7 @@ namespace boost { namespace unordered_detail { void increment(); }; - // hash_table_manager + // hash_buckets // // This is responsible for allocating and deallocating buckets and nodes. // @@ -193,8 +193,11 @@ namespace boost { namespace unordered_detail { // methods (other than getters and setters). template - struct hash_table_manager + class hash_buckets { + hash_buckets(hash_buckets const&); + hash_buckets& operator=(hash_buckets const&); + public: // Types typedef A value_allocator; @@ -217,8 +220,6 @@ namespace boost { namespace unordered_detail { // Members bucket_ptr buckets_; - bucket_ptr cached_begin_bucket_; - std::size_t size_; std::size_t bucket_count_; boost::compressed_pair allocators_; @@ -228,8 +229,6 @@ namespace boost { namespace unordered_detail { node_allocator const& node_alloc() const { return allocators_.second(); } bucket_allocator& bucket_alloc() { return allocators_.first(); } node_allocator& node_alloc() { return allocators_.second(); } - iterator_base begin() const { return iterator_base(this->cached_begin_bucket_); } - iterator_base end() const { return iterator_base(this->buckets_end()); } std::size_t max_bucket_count() const { // -1 to account for the sentinel. return prev_prime(this->bucket_alloc().max_size() - 1); @@ -239,20 +238,17 @@ namespace boost { namespace unordered_detail { // // The copy constructor doesn't copy the buckets. - hash_table_manager(); - explicit hash_table_manager(value_allocator const& a); - explicit hash_table_manager(hash_table_manager const& h); - hash_table_manager(hash_table_manager& x, move_tag m); - hash_table_manager(hash_table_manager& x, value_allocator const& a, move_tag m); - ~hash_table_manager(); + hash_buckets(node_allocator const& a, std::size_t n); + hash_buckets(hash_buckets& x, move_tag m); + hash_buckets(hash_buckets& x, value_allocator const& a, move_tag m); + ~hash_buckets(); // no throw - void swap(hash_table_manager& other); - void move(hash_table_manager& other); + void swap(hash_buckets& other); + void move(hash_buckets& other); // Buckets - void create_buckets(std::size_t bucket_count); std::size_t bucket_count() const; std::size_t bucket_from_hash(std::size_t hashed) const; bucket_ptr bucket_ptr_from_hash(std::size_t hashed) const; @@ -269,42 +265,15 @@ namespace boost { namespace unordered_detail { // void delete_buckets(); - void clear(); void clear_bucket(bucket_ptr); void delete_group(node_ptr first_node); void delete_nodes(node_ptr begin, node_ptr end); void delete_to_bucket_end(node_ptr begin); - - // Erase - // - // no throw - - iterator_base erase(iterator_base r); - std::size_t erase_group(node_ptr* it, bucket_ptr bucket); - iterator_base erase_range(iterator_base r1, iterator_base r2); - - // recompute_begin_bucket - // - // After an erase cached_begin_bucket_ might be left pointing to - // an empty bucket, so this is called to update it - // - // no throw - - void recompute_begin_bucket(bucket_ptr b); - - // This is called when a range has been erased - // - // no throw - - void recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2); - - // no throw - float load_factor() const; }; template class hash_table : - public hash_table_manager + public hash_buckets { public: @@ -313,19 +282,19 @@ namespace boost { namespace unordered_detail { typedef A value_allocator; typedef G grouped; typedef K key_extractor; - typedef hash_table_manager manager; + typedef hash_buckets buckets; typedef BOOST_DEDUCED_TYPENAME value_allocator::value_type value_type; typedef BOOST_DEDUCED_TYPENAME key_extractor::BOOST_NESTED_TEMPLATE apply extractor; typedef BOOST_DEDUCED_TYPENAME extractor::key_type key_type; - typedef BOOST_DEDUCED_TYPENAME manager::node node; - typedef BOOST_DEDUCED_TYPENAME manager::bucket bucket; - typedef BOOST_DEDUCED_TYPENAME manager::node_ptr node_ptr; - typedef BOOST_DEDUCED_TYPENAME manager::bucket_ptr bucket_ptr; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; + typedef BOOST_DEDUCED_TYPENAME buckets::bucket bucket; + typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr node_ptr; + typedef BOOST_DEDUCED_TYPENAME buckets::bucket_ptr bucket_ptr; - typedef BOOST_DEDUCED_TYPENAME manager::iterator_base iterator_base; + typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base iterator_base; // Types for storing functions @@ -340,6 +309,8 @@ namespace boost { namespace unordered_detail { bool func_; // The currently active functions. aligned_function funcs_[2]; + bucket_ptr cached_begin_bucket_; + std::size_t size_; float mlf_; std::size_t max_load_; @@ -395,6 +366,11 @@ namespace boost { namespace unordered_detail { ~hash_table(); hash_table& operator=(hash_table const&); + // Iterators + + iterator_base begin() const { return iterator_base(this->cached_begin_bucket_); } + iterator_base end() const { return iterator_base(this->buckets_end()); } + // Swap & Move void swap(hash_table& x); @@ -409,16 +385,45 @@ namespace boost { namespace unordered_detail { // Move/copy buckets - void move_buckets_to(manager& dst); - void copy_buckets_to(manager& dst) const; + void move_buckets_to(buckets& dst); + void copy_buckets_to(buckets& dst) const; // Misc. key methods - std::size_t erase_key(key_type const& k); std::size_t count(key_type const& k) const; iterator_base find(key_type const& k) const; value_type& at(key_type const& k) const; std::pair equal_range(key_type const& k) const; + + // Erase + // + // no throw + + void clear(); + std::size_t erase_key(key_type const& k); + iterator_base erase(iterator_base r); + std::size_t erase_group(node_ptr* it, bucket_ptr bucket); + iterator_base erase_range(iterator_base r1, iterator_base r2); + + // recompute_begin_bucket + + void recompute_begin_bucket(); + + // After an erase cached_begin_bucket_ might be left pointing to + // an empty bucket, so this is called to update it + // + // no throw + + void recompute_begin_bucket(bucket_ptr b); + + // This is called when a range has been erased + // + // no throw + + void recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2); + + // no throw + float load_factor() const; }; // Iterator Access @@ -456,9 +461,9 @@ namespace boost { namespace unordered_detail { typedef BOOST_DEDUCED_TYPENAME A::value_type value_type; private: - typedef hash_table_manager manager; - typedef BOOST_DEDUCED_TYPENAME manager::node_ptr ptr; - typedef BOOST_DEDUCED_TYPENAME manager::node node; + typedef hash_buckets buckets; + typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr ptr; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; typedef hash_const_local_iterator const_local_iterator; friend class hash_const_local_iterator; @@ -491,9 +496,9 @@ namespace boost { namespace unordered_detail { typedef BOOST_DEDUCED_TYPENAME A::value_type value_type; private: - typedef hash_table_manager manager; - typedef BOOST_DEDUCED_TYPENAME manager::node_ptr ptr; - typedef BOOST_DEDUCED_TYPENAME manager::node node; + typedef hash_buckets buckets; + typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr ptr; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; typedef hash_local_iterator local_iterator; friend class hash_local_iterator; ptr ptr_; @@ -531,9 +536,9 @@ namespace boost { namespace unordered_detail { typedef BOOST_DEDUCED_TYPENAME A::value_type value_type; private: - typedef hash_table_manager manager; - typedef BOOST_DEDUCED_TYPENAME manager::node node; - typedef BOOST_DEDUCED_TYPENAME manager::iterator_base base; + typedef hash_buckets buckets; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; + typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base; typedef hash_const_iterator const_iterator; friend class hash_const_iterator; base base_; @@ -566,9 +571,9 @@ namespace boost { namespace unordered_detail { typedef BOOST_DEDUCED_TYPENAME A::value_type value_type; private: - typedef hash_table_manager manager; - typedef BOOST_DEDUCED_TYPENAME manager::node node; - typedef BOOST_DEDUCED_TYPENAME manager::iterator_base base; + typedef hash_buckets buckets; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; + typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base; typedef hash_iterator iterator; friend class hash_iterator; friend class iterator_access; diff --git a/include/boost/unordered/detail/manager.hpp b/include/boost/unordered/detail/manager.hpp deleted file mode 100644 index 5b9d7383..00000000 --- a/include/boost/unordered/detail/manager.hpp +++ /dev/null @@ -1,418 +0,0 @@ - -// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. -// Copyright (C) 2005-2009 Daniel James -// 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) - -#ifndef BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED -#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED - -#include -#include -#include - -namespace boost { namespace unordered_detail { - - //////////////////////////////////////////////////////////////////////////// - // Explicitly call a destructor - -#if defined(BOOST_MSVC) -# define BOOST_UNORDERED_DESTRUCT(x, type) (x)->~type(); -#else -# define BOOST_UNORDERED_DESTRUCT(x, type) boost::unordered_detail::destroy(x) - template - void destroy(T* x) { - x->~T(); - } -#endif - - // Constructors - - template - hash_table_manager::hash_table_manager() - : buckets_(), cached_begin_bucket_(), size_(), allocators_() {} - - template - hash_table_manager::hash_table_manager(value_allocator const& a) - : buckets_(), cached_begin_bucket_(), size_(), allocators_(a,a) {} - - template - hash_table_manager::hash_table_manager(hash_table_manager const& h) - : buckets_(), cached_begin_bucket_(), size_(), allocators_(h.allocators_) {} - - template - hash_table_manager::hash_table_manager(hash_table_manager& x, move_tag) - : buckets_(), cached_begin_bucket_(), size_(), allocators_(x.allocators_) - { - this->buckets_ = x.buckets_; - this->cached_begin_bucket_ = x.cached_begin_bucket_; - this->size_ = x.size_; - this->bucket_count_ = x.bucket_count_; - x.buckets_ = bucket_ptr(); - x.cached_begin_bucket_ = bucket_ptr(); - x.size_ = 0; - x.bucket_count_ = 0; - } - - template - hash_table_manager::hash_table_manager(hash_table_manager& x, value_allocator const& a, move_tag) : - buckets_(), cached_begin_bucket_(), size_(), allocators_(a,a) - { - if(this->node_alloc() == x.node_alloc()) { - this->buckets_ = x.buckets_; - this->cached_begin_bucket_ = x.cached_begin_bucket_; - this->size_ = x.size_; - this->bucket_count_ = x.bucket_count_; - x.buckets_ = bucket_ptr(); - x.cached_begin_bucket_ = bucket_ptr(); - x.size_ = 0; - x.bucket_count_ = 0; - } - } - - template - hash_table_manager::~hash_table_manager() - { - if(this->buckets_) { delete_buckets(); } - } - - // no throw - template - inline void hash_table_manager::move(hash_table_manager& other) - { - BOOST_ASSERT(node_alloc() == other.node_alloc()); - delete_buckets(); - this->buckets_ = other.buckets_; - this->cached_begin_bucket_ = other.cached_begin_bucket_; - this->size_ = other.size_; - this->bucket_count_ = other.bucket_count_; - other.buckets_ = bucket_ptr(); - other.cached_begin_bucket_ = bucket_ptr(); - other.size_ = 0; - other.bucket_count_ = 0; - } - - template - inline void hash_table_manager::swap(hash_table_manager& other) - { - BOOST_ASSERT(node_alloc() == other.node_alloc()); - std::swap(buckets_, other.buckets_); - std::swap(cached_begin_bucket_, other.cached_begin_bucket_); - std::swap(size_, other.size_); - std::swap(bucket_count_, other.bucket_count_); - } - - // Buckets - - template - inline std::size_t hash_table_manager::bucket_count() const - { - return bucket_count_; - } - - template - inline std::size_t hash_table_manager::bucket_from_hash(std::size_t hashed) const - { - return hashed % bucket_count_; - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::bucket_ptr - hash_table_manager::bucket_ptr_from_hash(std::size_t hashed) const - { - return buckets_ + static_cast( - bucket_from_hash(hashed)); - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::bucket_ptr - hash_table_manager::buckets_begin() const - { - return buckets_; - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::bucket_ptr - hash_table_manager::buckets_end() const - { - return buckets_ + static_cast(bucket_count_); - } - - template - inline std::size_t hash_table_manager::bucket_size(std::size_t index) const - { - bucket_ptr ptr = (buckets_ + static_cast(index))->next_; - std::size_t count = 0; - while(ptr) { - ++count; - ptr = next_node(ptr); - } - return count; - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::bucket_ptr - hash_table_manager::get_bucket(std::size_t n) const - { - return buckets_ + static_cast(n); - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::node_ptr - hash_table_manager::bucket_begin(std::size_t n) const - { - return (buckets_ + static_cast(n))->next_; - } - - template - inline BOOST_DEDUCED_TYPENAME hash_table_manager::node_ptr - hash_table_manager::bucket_end(std::size_t) const - { - return node_ptr(); - } - - // recompute_begin_bucket - // - // After an erase cached_begin_bucket_ might be left pointing to - // an empty bucket, so this is called to update it - // - // no throw - - template - inline void hash_table_manager::recompute_begin_bucket(bucket_ptr b) - { - BOOST_ASSERT(!(b < cached_begin_bucket_)); - - if(b == cached_begin_bucket_) - { - if (size_ != 0) { - while (!cached_begin_bucket_->next_) - ++cached_begin_bucket_; - } else { - cached_begin_bucket_ = buckets_end(); - } - } - } - - // This is called when a range has been erased - // - // no throw - - template - inline void hash_table_manager::recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2) - { - BOOST_ASSERT(!(b1 < cached_begin_bucket_) && !(b2 < b1)); - BOOST_ASSERT(BOOST_UNORDERED_BORLAND_BOOL(b2->next_)); - - if(b1 == cached_begin_bucket_ && !b1->next_) - cached_begin_bucket_ = b2; - } - - // no throw - template - inline float hash_table_manager::load_factor() const - { - BOOST_ASSERT(bucket_count_ != 0); - return static_cast(size_) - / static_cast(bucket_count_); - } - - // Construct/destruct - - template - void hash_table_manager::create_buckets(std::size_t bucket_count) { - BOOST_ASSERT(!this->buckets_ && !this->size_); - - // The array constructor will clean up in the event of an - // exception. - allocator_array_constructor - constructor(bucket_alloc()); - - // Creates an extra bucket to act as a sentinel. - constructor.construct(bucket(), bucket_count + 1); - - this->cached_begin_bucket_ = constructor.get() + static_cast(bucket_count); - - // Set up the sentinel (node_ptr cast) - this->cached_begin_bucket_->next_ = this->cached_begin_bucket_; - - // Only release the buckets once everything is successfully - // done. - this->buckets_ = constructor.release(); - this->bucket_count_ = bucket_count; - } - - template - inline void hash_table_manager::destruct_node(node_ptr b) - { - node* raw_ptr = static_cast(&*b); - BOOST_UNORDERED_DESTRUCT(&raw_ptr->value(), value_type); - real_node_ptr n(node_alloc().address(*raw_ptr)); - node_alloc().destroy(n); - node_alloc().deallocate(n, 1); - } - - // Delete and clear buckets - - template - inline void hash_table_manager::delete_group(node_ptr first_node) - { - delete_nodes(first_node, node::next_group(first_node)); - } - - template - inline void hash_table_manager::delete_nodes(node_ptr begin, node_ptr end) - { - while(begin != end) { - node_ptr node = begin; - begin = next_node(begin); - destruct_node(node); - } - } - - template - inline void hash_table_manager::delete_to_bucket_end(node_ptr begin) - { - while(BOOST_UNORDERED_BORLAND_BOOL(begin)) { - node_ptr node = begin; - begin = next_node(begin); - destruct_node(node); - } - } - - template - inline void hash_table_manager::clear_bucket(bucket_ptr b) - { - node_ptr node_it = b->next_; - b->next_ = node_ptr(); - - while(node_it) { - node_ptr node_to_destruct = node_it; - node_it = next_node(node_it); - destruct_node(node_to_destruct); - } - } - - template - void hash_table_manager::clear() - { - for(bucket_ptr begin = this->buckets_begin(), end = this->buckets_end(); begin != end; ++begin) { - clear_bucket(begin); - } - - this->cached_begin_bucket_ = this->buckets_end(); - this->size_ = 0; - } - - template - inline void hash_table_manager::delete_buckets() - { - clear(); - - // Destroy the buckets (including the sentinel bucket). - bucket_ptr end = this->buckets_end(); - ++end; - for(bucket_ptr begin = this->buckets_begin(); begin != end; ++begin) { - bucket_alloc().destroy(begin); - } - - bucket_alloc().deallocate(this->buckets_begin(), this->bucket_count() + 1); - - this->buckets_= bucket_ptr(); - } - - template - BOOST_DEDUCED_TYPENAME hash_table_manager::iterator_base - hash_table_manager::erase(iterator_base r) - { - BOOST_ASSERT(!r.is_end()); - iterator_base next = r; - next.increment(); - --this->size_; - node::unlink_node(*r.bucket_, r.node_); - destruct_node(r.node_); - // r has been invalidated but its bucket is still valid - this->recompute_begin_bucket(r.bucket_, next.bucket_); - return next; - } - - template - inline std::size_t hash_table_manager::erase_group(node_ptr* it, bucket_ptr bucket) - { - node_ptr pos = *it; - std::size_t count = node::group_count(*it); - this->size_ -= count; - node::unlink_group(it); - delete_group(pos); - this->recompute_begin_bucket(bucket); - return count; - } - - template - BOOST_DEDUCED_TYPENAME hash_table_manager::iterator_base - hash_table_manager::erase_range(iterator_base r1, iterator_base r2) - { - if(r1 != r2) - { - BOOST_ASSERT(!r1.is_end()); - - if (r1.bucket_ == r2.bucket_) { - this->size_ -= node_count(r1.node_, r2.node_); - node::unlink_nodes(*r1.bucket_, r1.node_, r2.node_); - delete_nodes(r1.node_, r2.node_); - - // No need to call recompute_begin_bucket because - // the nodes are only deleted from one bucket, which - // still contains r2 after the erase. - BOOST_ASSERT(r1.bucket_->next_); - } - else { - BOOST_ASSERT(r1.bucket_ < r2.bucket_); - - this->size_ -= node_count(r1.node_, node_ptr()); - node::unlink_nodes(*r1.bucket_, r1.node_, node_ptr()); - delete_to_bucket_end(r1.node_); - - bucket_ptr i = r1.bucket_; - for(++i; i != r2.bucket_; ++i) { - this->size_ -= node_count(i->next_, node_ptr()); - clear_bucket(i); - } - - if(!r2.is_end()) { - node_ptr first = r2.bucket_->next_; - this->size_ -= node_count(r2.bucket_->next_, r2.node_); - node::unlink_nodes(*r2.bucket_, r2.node_); - delete_nodes(first, r2.node_); - } - - // r1 has been invalidated but its bucket is still - // valid. - this->recompute_begin_bucket(r1.bucket_, r2.bucket_); - } - } - - return r2; - } - - //////////////////////////////////////////////////////////////////////////// - // hash_iterator_base implementation - - template - inline void hash_iterator_base::increment(node_ptr node) { - while(!node) { - ++bucket_; - node = bucket_->next_; - } - - node_ = node; - } - - template - inline void hash_iterator_base::increment() - { - increment(next_node(node_)); - } -}} - -#endif diff --git a/include/boost/unordered/detail/table.hpp b/include/boost/unordered/detail/table.hpp index f36e691c..c3afb6b7 100644 --- a/include/boost/unordered/detail/table.hpp +++ b/include/boost/unordered/detail/table.hpp @@ -13,7 +13,7 @@ #include #include -#include +#include #include namespace boost { namespace unordered_detail { @@ -135,24 +135,25 @@ namespace boost { namespace unordered_detail { template hash_table ::hash_table(std::size_t n, hasher const& hf, key_equal const& eq, value_allocator const& a) : - manager(a), func_(false), mlf_(1.0f), max_load_(0) + buckets(a, next_prime(n)), func_(false), cached_begin_bucket_(), size_(), mlf_(1.0f), max_load_(0) { std::uninitialized_fill((functions*)this->funcs_, (functions*)this->funcs_+2, functions(hf, eq)); - this->create_buckets(next_prime(n)); + this->cached_begin_bucket_ = this->buckets_end(); this->calculate_max_load(); } template hash_table ::hash_table(hash_table const& x) : - manager(x), func_(false), mlf_(x.mlf_), max_load_(0) + buckets(x.node_alloc(), next_prime(x.min_buckets_for_size(x.size_))), func_(false), cached_begin_bucket_(), size_(), mlf_(x.mlf_), max_load_(0) { std::uninitialized_fill((functions*)this->funcs_, (functions*)this->funcs_+2, x.current()); - this->create_buckets(next_prime(x.min_buckets_for_size(x.size_))); this->calculate_max_load(); x.copy_buckets_to(*this); + this->size_ = x.size_; + this->recompute_begin_bucket(); } // Copy Construct with allocator @@ -160,13 +161,15 @@ namespace boost { namespace unordered_detail { template hash_table ::hash_table(hash_table const& x, value_allocator const& a) : - manager(a), func_(false), mlf_(x.mlf_), max_load_(0) + buckets(a, next_prime(x.min_buckets_for_size(x.size_))), func_(false), cached_begin_bucket_(), size_(), mlf_(x.mlf_), max_load_(0) { std::uninitialized_fill((functions*)this->funcs_, (functions*)this->funcs_+2, x.current()); - this->create_buckets(next_prime(x.min_buckets_for_size(x.size_))); + this->cached_begin_bucket_ = this->buckets_end(); this->calculate_max_load(); x.copy_buckets_to(*this); + this->size_ = x.size_; + this->recompute_begin_bucket(); } // Move Construct @@ -174,8 +177,13 @@ namespace boost { namespace unordered_detail { template hash_table ::hash_table(hash_table& x, move_tag m) : - manager(x, m), func_(false), mlf_(x.mlf_), max_load_(0) + buckets(x, m), func_(false), cached_begin_bucket_(), size_(), mlf_(x.mlf_), max_load_(0) { + this->cached_begin_bucket_ = x.cached_begin_bucket_; + this->size_ = x.size_; + x.cached_begin_bucket_ = bucket_ptr(); + x.size_ = 0; + // TODO: Shouldn't I move the functions if poss. std::uninitialized_fill((functions*)this->funcs_, (functions*)this->funcs_+2, x.current()); @@ -184,7 +192,7 @@ namespace boost { namespace unordered_detail { template hash_table ::hash_table(hash_table& x, value_allocator const& a, move_tag m) : - manager(x, a, m), func_(false), mlf_(x.mlf_), max_load_(0) + buckets(x, a, m), func_(false), cached_begin_bucket_(), size_(), mlf_(x.mlf_), max_load_(0) { std::uninitialized_fill((functions*)this->funcs_, (functions*)this->funcs_+2, x.current()); @@ -192,10 +200,17 @@ namespace boost { namespace unordered_detail { this->calculate_max_load(); // no throw if(!this->buckets_) { - this->create_buckets(next_prime(x.min_buckets_for_size(x.size_))); - // This can throw, but hash_table_manager's destructor will clean - // up. - x.copy_buckets_to(*this); + buckets new_buckets(this->node_alloc(), x.min_buckets_for_size(x.size_)); + x.copy_buckets_to(new_buckets); + new_buckets.swap(*this); + this->size_ = x.size_; + this->recompute_begin_bucket(); + } + else { + this->cached_begin_bucket_ = x.cached_begin_bucket_; + this->size_ = x.size_; + x.cached_begin_bucket_ = bucket_ptr(); + x.size_ = 0; } } @@ -205,7 +220,8 @@ namespace boost { namespace unordered_detail { BOOST_UNORDERED_DESTRUCT(this->get_functions(false), functions); BOOST_UNORDERED_DESTRUCT(this->get_functions(true), functions); } - + + // TODO: Reuse current nodes amd buckets? template hash_table& hash_table::operator=(hash_table const& x) { @@ -214,11 +230,14 @@ namespace boost { namespace unordered_detail { this->set_functions( this->buffer_functions(x)); // throws, strong this->mlf_ = x.mlf_; // no throw + buckets new_buckets(this->node_alloc(), x.min_buckets_for_size(x.size_)); + x.copy_buckets_to(new_buckets); + new_buckets.swap(*this); this->calculate_max_load(); // no throw - this->reserve(x.size_); // throws - x.copy_buckets_to(*this); // throws + this->size_ = x.size_; + this->recompute_begin_bucket(); } - + return *this; } @@ -250,25 +269,28 @@ namespace boost { namespace unordered_detail { functions_ptr new_func_that = x.buffer_functions(*this); if(this->node_alloc() == x.node_alloc()) { - this->manager::swap(x); // No throw + this->buckets::swap(x); // No throw + std::swap(this->cached_begin_bucket_, x.cached_begin_bucket_); + std::swap(this->size_, x.size_); } else { - // Create new buckets in separate hash_table_manager objects + // Create new buckets in separate hash_buckets objects // which will clean up if anything throws an exception. // (all can throw, but with no effect as these are new objects). - manager new_this(*this); - new_this.create_buckets(x.min_buckets_for_size(x.size_)); + buckets new_this(this->node_alloc(), x.min_buckets_for_size(x.size_)); x.copy_buckets_to(new_this); - manager new_that(x); - new_that.create_buckets(this->min_buckets_for_size(this->size_)); + buckets new_that(x.node_alloc(), this->min_buckets_for_size(this->size_)); copy_buckets_to(new_that); // Modifying the data, so no throw from now on. - this->manager::swap(new_this); - x.manager::swap(new_that); + this->buckets::swap(new_this); + x.buckets::swap(new_that); + std::swap(this->size_, x.size_); + this->recompute_begin_bucket(); + x.recompute_begin_bucket(); } // The rest is no throw. @@ -300,19 +322,24 @@ namespace boost { namespace unordered_detail { functions_ptr new_func_this = this->buffer_functions(x); if(this->node_alloc() == x.node_alloc()) { - this->manager::move(x); // no throw + this->buckets::move(x); // no throw + this->size_ = x.size_; + this->cached_begin_bucket_ = x.cached_begin_bucket_; + x.size_ = 0; + x.cached_begin_bucket_ = bucket_ptr(); } else { // Create new buckets in separate HASH_TABLE_DATA objects // which will clean up if anything throws an exception. // (all can throw, but with no effect as these are new objects). - manager new_this(*this); - new_this.create_buckets(next_prime(x.min_buckets_for_size(x.size_))); + buckets new_this(this->node_alloc(), next_prime(x.min_buckets_for_size(x.size_))); x.copy_buckets_to(new_this); // Start updating the data here, no throw from now on. - this->manager::move(new_this); + this->buckets::move(new_this); + this->size_ = x.size_; + this->recompute_begin_bucket(); } // We've made it, the rest is no throw. @@ -383,28 +410,13 @@ namespace boost { namespace unordered_detail { if (n == this->bucket_count()) // no throw return; - manager new_buckets(*this); // throws, seperate - new_buckets.create_buckets(n); // throws, seperate - move_buckets_to(new_buckets); // basic/no throw - new_buckets.swap(*this); // no throw - this->calculate_max_load(); // no throw - } + // Save the size to restore it if successful + std::size_t size = this->size_; - //////////////////////////////////////////////////////////////////////////// - // move_buckets_to & copy_buckets_to - - // move_buckets_to - // - // if the hash function throws, basic excpetion safety - // no throw otherwise - - template - void hash_table - ::move_buckets_to(manager& dst) - { - BOOST_ASSERT(this->node_alloc() == dst.node_alloc()); - BOOST_ASSERT(this->buckets_ && dst.buckets_); + // Create another buckets to move the nodes into. + buckets dst(this->node_alloc(), n);// throws, separate + // Move the nodes to dst. hasher const& hf = this->hash_function(); bucket_ptr end = this->buckets_end(); @@ -418,17 +430,25 @@ namespace boost { namespace unordered_detail { bucket_ptr dst_bucket = dst.bucket_ptr_from_hash( hf(extractor::extract(node::get_value(src_bucket->next_)))); + node_ptr n = src_bucket->next_; - std::size_t count = node::group_count(src_bucket->next_); - this->size_ -= count; - dst.size_ += count; + this->size_ -= node::group_count(n); node::unlink_group(&src_bucket->next_); node::add_group_to_bucket(n, *dst_bucket); - if(dst_bucket < dst.cached_begin_bucket_) dst.cached_begin_bucket_ = dst_bucket; } } + + // Swap the new nodes back into the container and setup the local + // variables. + dst.swap(*this); // no throw + this->size_ = size; + this->recompute_begin_bucket(); // no throw + this->calculate_max_load(); // no throw } + //////////////////////////////////////////////////////////////////////////// + // copy_buckets_to + // copy_buckets_to // // basic excpetion safety. If an exception is thrown this will @@ -436,7 +456,7 @@ namespace boost { namespace unordered_detail { template void hash_table - ::copy_buckets_to(manager& dst) const + ::copy_buckets_to(buckets& dst) const { BOOST_ASSERT(this->buckets_ && dst.buckets_); @@ -459,13 +479,10 @@ namespace boost { namespace unordered_detail { a.construct(node::get_value(it)); node_ptr n = a.release(); node::add_to_bucket(n, *dst_bucket); - ++dst.size_; - if(dst_bucket < dst.cached_begin_bucket_) dst.cached_begin_bucket_ = dst_bucket; for(it = next_node(it); it != group_end; it = next_node(it)) { a.construct(node::get_value(it)); node::add_after_node(a.release(), n); - ++dst.size_; } } } @@ -476,18 +493,6 @@ namespace boost { namespace unordered_detail { // strong exception safety - template - std::size_t hash_table - ::erase_key(key_type const& k) - { - // No side effects in initial section - bucket_ptr bucket = this->get_bucket(this->bucket_index(k)); - node_ptr* it = find_for_erase(bucket, k); - - // No throw. - return *it ? this->erase_group(it, bucket) : 0; - } - // count // // strong exception safety, no side effects @@ -563,6 +568,167 @@ namespace boost { namespace unordered_detail { return std::pair(this->end(), this->end()); } } + + //////////////////////////////////////////////////////////////////////////// + // Erase methods + + template + void hash_table::clear() + { + for(bucket_ptr begin = this->buckets_begin(), end = this->buckets_end(); begin != end; ++begin) { + this->clear_bucket(begin); + } + + this->cached_begin_bucket_ = this->buckets_end(); + this->size_ = 0; + } + + template + std::size_t hash_table + ::erase_key(key_type const& k) + { + // No side effects in initial section + bucket_ptr bucket = this->get_bucket(this->bucket_index(k)); + node_ptr* it = this->find_for_erase(bucket, k); + + // No throw. + return *it ? this->erase_group(it, bucket) : 0; + } + + + template + BOOST_DEDUCED_TYPENAME hash_table::iterator_base + hash_table::erase(iterator_base r) + { + BOOST_ASSERT(!r.is_end()); + iterator_base next = r; + next.increment(); + --this->size_; + node::unlink_node(*r.bucket_, r.node_); + this->destruct_node(r.node_); + // r has been invalidated but its bucket is still valid + this->recompute_begin_bucket(r.bucket_, next.bucket_); + return next; + } + + template + inline std::size_t hash_table::erase_group(node_ptr* it, bucket_ptr bucket) + { + node_ptr pos = *it; + std::size_t count = node::group_count(*it); + this->size_ -= count; + node::unlink_group(it); + this->delete_group(pos); + this->recompute_begin_bucket(bucket); + return count; + } + + template + BOOST_DEDUCED_TYPENAME hash_table::iterator_base + hash_table::erase_range(iterator_base r1, iterator_base r2) + { + if(r1 != r2) + { + BOOST_ASSERT(!r1.is_end()); + + if (r1.bucket_ == r2.bucket_) { + this->size_ -= node_count(r1.node_, r2.node_); + node::unlink_nodes(*r1.bucket_, r1.node_, r2.node_); + this->delete_nodes(r1.node_, r2.node_); + + // No need to call recompute_begin_bucket because + // the nodes are only deleted from one bucket, which + // still contains r2 after the erase. + BOOST_ASSERT(r1.bucket_->next_); + } + else { + BOOST_ASSERT(r1.bucket_ < r2.bucket_); + + this->size_ -= node_count(r1.node_, node_ptr()); + node::unlink_nodes(*r1.bucket_, r1.node_, node_ptr()); + this->delete_to_bucket_end(r1.node_); + + bucket_ptr i = r1.bucket_; + for(++i; i != r2.bucket_; ++i) { + this->size_ -= node_count(i->next_, node_ptr()); + this->clear_bucket(i); + } + + if(!r2.is_end()) { + node_ptr first = r2.bucket_->next_; + this->size_ -= node_count(r2.bucket_->next_, r2.node_); + node::unlink_nodes(*r2.bucket_, r2.node_); + this->delete_nodes(first, r2.node_); + } + + // r1 has been invalidated but its bucket is still + // valid. + this->recompute_begin_bucket(r1.bucket_, r2.bucket_); + } + } + + return r2; + } + + + template + inline void hash_table::recompute_begin_bucket() + { + if (this->size_ != 0) { + this->cached_begin_bucket_ = this->buckets_begin(); + while (!this->cached_begin_bucket_->next_) + ++this->cached_begin_bucket_; + } else { + this->cached_begin_bucket_ = this->buckets_end(); + } + } + + // recompute_begin_bucket + // + // After an erase cached_begin_bucket_ might be left pointing to + // an empty bucket, so this is called to update it + // + // no throw + + template + inline void hash_table::recompute_begin_bucket(bucket_ptr b) + { + BOOST_ASSERT(!(b < this->cached_begin_bucket_)); + + if(b == this->cached_begin_bucket_) + { + if (this->size_ != 0) { + while (!this->cached_begin_bucket_->next_) + ++this->cached_begin_bucket_; + } else { + this->cached_begin_bucket_ = this->buckets_end(); + } + } + } + + // This is called when a range has been erased + // + // no throw + + template + inline void hash_table::recompute_begin_bucket(bucket_ptr b1, bucket_ptr b2) + { + BOOST_ASSERT(!(b1 < this->cached_begin_bucket_) && !(b2 < b1)); + BOOST_ASSERT(BOOST_UNORDERED_BORLAND_BOOL(b2->next_)); + + if(b1 == this->cached_begin_bucket_ && !b1->next_) + this->cached_begin_bucket_ = b2; + } + + // no throw + template + inline float hash_table::load_factor() const + { + BOOST_ASSERT(this->bucket_count_ != 0); + return static_cast(this->size_) + / static_cast(this->bucket_count_); + } + }} #endif diff --git a/include/boost/unordered/detail/util.hpp b/include/boost/unordered/detail/util.hpp index 5b0778ee..b6827b27 100644 --- a/include/boost/unordered/detail/util.hpp +++ b/include/boost/unordered/detail/util.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #if !defined(BOOST_UNORDERED_STD_FORWARD) @@ -210,20 +210,20 @@ namespace boost { namespace unordered_detail { template class hash_node_constructor { - typedef hash_table_manager manager; - typedef BOOST_DEDUCED_TYPENAME manager::node node; - typedef BOOST_DEDUCED_TYPENAME manager::real_node_ptr real_node_ptr; - typedef BOOST_DEDUCED_TYPENAME manager::value_type value_type; + typedef hash_buckets buckets; + typedef BOOST_DEDUCED_TYPENAME buckets::node node; + typedef BOOST_DEDUCED_TYPENAME buckets::real_node_ptr real_node_ptr; + typedef BOOST_DEDUCED_TYPENAME buckets::value_type value_type; - manager& manager_; + buckets& buckets_; real_node_ptr node_; bool node_constructed_; bool value_constructed_; public: - hash_node_constructor(manager& m) : - manager_(m), + hash_node_constructor(buckets& m) : + buckets_(m), node_(), node_constructed_(false), value_constructed_(false) @@ -281,12 +281,12 @@ namespace boost { namespace unordered_detail { } // no throw - BOOST_DEDUCED_TYPENAME manager::node_ptr release() + BOOST_DEDUCED_TYPENAME buckets::node_ptr release() { real_node_ptr p = node_; node_ = real_node_ptr(); // node_ptr cast - return manager_.bucket_alloc().address(*p); + return buckets_.bucket_alloc().address(*p); } private: @@ -305,9 +305,9 @@ namespace boost { namespace unordered_detail { } if (node_constructed_) - manager_.node_alloc().destroy(node_); + buckets_.node_alloc().destroy(node_); - manager_.node_alloc().deallocate(node_, 1); + buckets_.node_alloc().deallocate(node_, 1); } } @@ -318,8 +318,8 @@ namespace boost { namespace unordered_detail { node_constructed_ = false; value_constructed_ = false; - node_ = manager_.node_alloc().allocate(1); - manager_.node_alloc().construct(node_, node()); + node_ = buckets_.node_alloc().allocate(1); + buckets_.node_alloc().construct(node_, node()); node_constructed_ = true; } else {