forked from boostorg/unordered
Move size_ and cached_begin_bucket_ into table, rename hash_table_manager hash_buckets.
[SVN r56010]
This commit is contained in:
262
include/boost/unordered/detail/buckets.hpp
Normal file
262
include/boost/unordered/detail/buckets.hpp
Normal file
@ -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 <boost/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered/detail/node.hpp>
|
||||
|
||||
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 <class T>
|
||||
void destroy(T* x) {
|
||||
x->~T();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Constructors
|
||||
|
||||
template <class A, class G>
|
||||
hash_buckets<A, G>::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<bucket_allocator>
|
||||
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<ptrdiff_t>(bucket_count);
|
||||
sentinel->next_ = sentinel;
|
||||
|
||||
// Only release the buckets once everything is successfully
|
||||
// done.
|
||||
this->buckets_ = constructor.release();
|
||||
this->bucket_count_ = bucket_count;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
hash_buckets<A, G>::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 <class A, class G>
|
||||
hash_buckets<A, G>::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 <class A, class G>
|
||||
hash_buckets<A, G>::~hash_buckets()
|
||||
{
|
||||
if(this->buckets_) { delete_buckets(); }
|
||||
}
|
||||
|
||||
// no throw
|
||||
template <class A, class G>
|
||||
inline void hash_buckets<A, G>::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 <class A, class G>
|
||||
inline void hash_buckets<A, G>::swap(hash_buckets<A, G>& other)
|
||||
{
|
||||
BOOST_ASSERT(node_alloc() == other.node_alloc());
|
||||
std::swap(buckets_, other.buckets_);
|
||||
std::swap(bucket_count_, other.bucket_count_);
|
||||
}
|
||||
|
||||
// Buckets
|
||||
|
||||
template <class A, class G>
|
||||
inline std::size_t hash_buckets<A, G>::bucket_count() const
|
||||
{
|
||||
return bucket_count_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline std::size_t hash_buckets<A, G>::bucket_from_hash(std::size_t hashed) const
|
||||
{
|
||||
return hashed % bucket_count_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
|
||||
hash_buckets<A, G>::bucket_ptr_from_hash(std::size_t hashed) const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(
|
||||
bucket_from_hash(hashed));
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
|
||||
hash_buckets<A, G>::buckets_begin() const
|
||||
{
|
||||
return buckets_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
|
||||
hash_buckets<A, G>::buckets_end() const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(bucket_count_);
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline std::size_t hash_buckets<A, G>::bucket_size(std::size_t index) const
|
||||
{
|
||||
bucket_ptr ptr = (buckets_ + static_cast<std::ptrdiff_t>(index))->next_;
|
||||
std::size_t count = 0;
|
||||
while(ptr) {
|
||||
++count;
|
||||
ptr = next_node(ptr);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::bucket_ptr
|
||||
hash_buckets<A, G>::get_bucket(std::size_t n) const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(n);
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::node_ptr
|
||||
hash_buckets<A, G>::bucket_begin(std::size_t n) const
|
||||
{
|
||||
return (buckets_ + static_cast<std::ptrdiff_t>(n))->next_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_buckets<A, G>::node_ptr
|
||||
hash_buckets<A, G>::bucket_end(std::size_t) const
|
||||
{
|
||||
return node_ptr();
|
||||
}
|
||||
|
||||
// Construct/destruct
|
||||
|
||||
template <class A, class G>
|
||||
inline void hash_buckets<A, G>::destruct_node(node_ptr b)
|
||||
{
|
||||
node* raw_ptr = static_cast<node*>(&*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 <class A, class G>
|
||||
inline void hash_buckets<A, G>::delete_group(node_ptr first_node)
|
||||
{
|
||||
delete_nodes(first_node, node::next_group(first_node));
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline void hash_buckets<A, G>::delete_nodes(node_ptr begin, node_ptr end)
|
||||
{
|
||||
while(begin != end) {
|
||||
node_ptr node = begin;
|
||||
begin = next_node(begin);
|
||||
destruct_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline void hash_buckets<A, G>::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 <class A, class G>
|
||||
inline void hash_buckets<A, G>::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 <class A, class G>
|
||||
inline void hash_buckets<A, G>::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 <class BucketPtr>
|
||||
inline void hash_iterator_base<BucketPtr>::increment(node_ptr node) {
|
||||
while(!node) {
|
||||
++bucket_;
|
||||
node = bucket_->next_;
|
||||
}
|
||||
|
||||
node_ = node;
|
||||
}
|
||||
|
||||
template <class BucketPtr>
|
||||
inline void hash_iterator_base<BucketPtr>::increment()
|
||||
{
|
||||
increment(next_node(node_));
|
||||
}
|
||||
}}
|
||||
|
||||
#endif
|
@ -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 <class A, class G>
|
||||
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<bucket_allocator, node_allocator> 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 H, class P, class A, class G, class K>
|
||||
class hash_table :
|
||||
public hash_table_manager<A, G>
|
||||
public hash_buckets<A, G>
|
||||
|
||||
{
|
||||
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<A, G> manager;
|
||||
typedef hash_buckets<A, G> buckets;
|
||||
|
||||
typedef BOOST_DEDUCED_TYPENAME value_allocator::value_type value_type;
|
||||
typedef BOOST_DEDUCED_TYPENAME key_extractor::BOOST_NESTED_TEMPLATE apply<value_type>
|
||||
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<iterator_base, iterator_base> 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<A, G> manager;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node_ptr ptr;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node node;
|
||||
typedef hash_buckets<A, G> buckets;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr ptr;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
|
||||
typedef hash_const_local_iterator<A, G> const_local_iterator;
|
||||
|
||||
friend class hash_const_local_iterator<A, G>;
|
||||
@ -491,9 +496,9 @@ namespace boost { namespace unordered_detail {
|
||||
typedef BOOST_DEDUCED_TYPENAME A::value_type value_type;
|
||||
|
||||
private:
|
||||
typedef hash_table_manager<A, G> manager;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node_ptr ptr;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node node;
|
||||
typedef hash_buckets<A, G> buckets;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node_ptr ptr;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
|
||||
typedef hash_local_iterator<A, G> local_iterator;
|
||||
friend class hash_local_iterator<A, G>;
|
||||
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<A, G> manager;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node node;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::iterator_base base;
|
||||
typedef hash_buckets<A, G> buckets;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base;
|
||||
typedef hash_const_iterator<A, G> const_iterator;
|
||||
friend class hash_const_iterator<A, G>;
|
||||
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<A, G> manager;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::node node;
|
||||
typedef BOOST_DEDUCED_TYPENAME manager::iterator_base base;
|
||||
typedef hash_buckets<A, G> buckets;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::node node;
|
||||
typedef BOOST_DEDUCED_TYPENAME buckets::iterator_base base;
|
||||
typedef hash_iterator<A, G> iterator;
|
||||
friend class hash_iterator<A, G>;
|
||||
friend class iterator_access;
|
||||
|
@ -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 <boost/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/unordered/detail/node.hpp>
|
||||
|
||||
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 <class T>
|
||||
void destroy(T* x) {
|
||||
x->~T();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Constructors
|
||||
|
||||
template <class A, class G>
|
||||
hash_table_manager<A, G>::hash_table_manager()
|
||||
: buckets_(), cached_begin_bucket_(), size_(), allocators_() {}
|
||||
|
||||
template <class A, class G>
|
||||
hash_table_manager<A, G>::hash_table_manager(value_allocator const& a)
|
||||
: buckets_(), cached_begin_bucket_(), size_(), allocators_(a,a) {}
|
||||
|
||||
template <class A, class G>
|
||||
hash_table_manager<A, G>::hash_table_manager(hash_table_manager const& h)
|
||||
: buckets_(), cached_begin_bucket_(), size_(), allocators_(h.allocators_) {}
|
||||
|
||||
template <class A, class G>
|
||||
hash_table_manager<A, G>::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 <class A, class G>
|
||||
hash_table_manager<A, G>::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 <class A, class G>
|
||||
hash_table_manager<A, G>::~hash_table_manager()
|
||||
{
|
||||
if(this->buckets_) { delete_buckets(); }
|
||||
}
|
||||
|
||||
// no throw
|
||||
template <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::swap(hash_table_manager<A, G>& 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 <class A, class G>
|
||||
inline std::size_t hash_table_manager<A, G>::bucket_count() const
|
||||
{
|
||||
return bucket_count_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline std::size_t hash_table_manager<A, G>::bucket_from_hash(std::size_t hashed) const
|
||||
{
|
||||
return hashed % bucket_count_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::bucket_ptr
|
||||
hash_table_manager<A, G>::bucket_ptr_from_hash(std::size_t hashed) const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(
|
||||
bucket_from_hash(hashed));
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::bucket_ptr
|
||||
hash_table_manager<A, G>::buckets_begin() const
|
||||
{
|
||||
return buckets_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::bucket_ptr
|
||||
hash_table_manager<A, G>::buckets_end() const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(bucket_count_);
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline std::size_t hash_table_manager<A, G>::bucket_size(std::size_t index) const
|
||||
{
|
||||
bucket_ptr ptr = (buckets_ + static_cast<std::ptrdiff_t>(index))->next_;
|
||||
std::size_t count = 0;
|
||||
while(ptr) {
|
||||
++count;
|
||||
ptr = next_node(ptr);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::bucket_ptr
|
||||
hash_table_manager<A, G>::get_bucket(std::size_t n) const
|
||||
{
|
||||
return buckets_ + static_cast<std::ptrdiff_t>(n);
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::node_ptr
|
||||
hash_table_manager<A, G>::bucket_begin(std::size_t n) const
|
||||
{
|
||||
return (buckets_ + static_cast<std::ptrdiff_t>(n))->next_;
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::node_ptr
|
||||
hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline float hash_table_manager<A, G>::load_factor() const
|
||||
{
|
||||
BOOST_ASSERT(bucket_count_ != 0);
|
||||
return static_cast<float>(size_)
|
||||
/ static_cast<float>(bucket_count_);
|
||||
}
|
||||
|
||||
// Construct/destruct
|
||||
|
||||
template <class A, class G>
|
||||
void hash_table_manager<A, G>::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<bucket_allocator>
|
||||
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<ptrdiff_t>(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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::destruct_node(node_ptr b)
|
||||
{
|
||||
node* raw_ptr = static_cast<node*>(&*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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::delete_group(node_ptr first_node)
|
||||
{
|
||||
delete_nodes(first_node, node::next_group(first_node));
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline void hash_table_manager<A, G>::delete_nodes(node_ptr begin, node_ptr end)
|
||||
{
|
||||
while(begin != end) {
|
||||
node_ptr node = begin;
|
||||
begin = next_node(begin);
|
||||
destruct_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
template <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
void hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline void hash_table_manager<A, G>::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 <class A, class G>
|
||||
BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::iterator_base
|
||||
hash_table_manager<A, G>::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 <class A, class G>
|
||||
inline std::size_t hash_table_manager<A, G>::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 <class A, class G>
|
||||
BOOST_DEDUCED_TYPENAME hash_table_manager<A, G>::iterator_base
|
||||
hash_table_manager<A, G>::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 <class BucketPtr>
|
||||
inline void hash_iterator_base<BucketPtr>::increment(node_ptr node) {
|
||||
while(!node) {
|
||||
++bucket_;
|
||||
node = bucket_->next_;
|
||||
}
|
||||
|
||||
node_ = node;
|
||||
}
|
||||
|
||||
template <class BucketPtr>
|
||||
inline void hash_iterator_base<BucketPtr>::increment()
|
||||
{
|
||||
increment(next_node(node_));
|
||||
}
|
||||
}}
|
||||
|
||||
#endif
|
@ -13,7 +13,7 @@
|
||||
#include <boost/config/no_tr1/cmath.hpp>
|
||||
#include <boost/iterator/iterator_categories.hpp>
|
||||
|
||||
#include <boost/unordered/detail/manager.hpp>
|
||||
#include <boost/unordered/detail/buckets.hpp>
|
||||
#include <boost/unordered/detail/util.hpp>
|
||||
|
||||
namespace boost { namespace unordered_detail {
|
||||
@ -135,24 +135,25 @@ namespace boost { namespace unordered_detail {
|
||||
template <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
hash_table<H, P, A, G, K>& hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
void hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
void hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
std::size_t hash_table<H, P, A, G, K>
|
||||
::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<iterator_base, iterator_base>(this->end(), this->end());
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Erase methods
|
||||
|
||||
template <class H, class P, class A, class G, class K>
|
||||
void hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
std::size_t hash_table<H, P, A, G, K>
|
||||
::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 <class H, class P, class A, class G, class K>
|
||||
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
|
||||
hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
inline std::size_t hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
|
||||
hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
inline void hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
inline void hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
inline void hash_table<H, P, A, G, K>::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 <class H, class P, class A, class G, class K>
|
||||
inline float hash_table<H, P, A, G, K>::load_factor() const
|
||||
{
|
||||
BOOST_ASSERT(this->bucket_count_ != 0);
|
||||
return static_cast<float>(this->size_)
|
||||
/ static_cast<float>(this->bucket_count_);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <boost/iterator/iterator_categories.hpp>
|
||||
#include <boost/preprocessor/seq/size.hpp>
|
||||
#include <boost/preprocessor/seq/enum.hpp>
|
||||
#include <boost/unordered/detail/manager.hpp>
|
||||
#include <boost/unordered/detail/buckets.hpp>
|
||||
|
||||
#if !defined(BOOST_UNORDERED_STD_FORWARD)
|
||||
|
||||
@ -210,20 +210,20 @@ namespace boost { namespace unordered_detail {
|
||||
template <class Alloc, class Grouped>
|
||||
class hash_node_constructor
|
||||
{
|
||||
typedef hash_table_manager<Alloc, Grouped> 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<Alloc, Grouped> 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 {
|
||||
|
Reference in New Issue
Block a user