2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
// 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_ALL_HPP_INCLUDED
|
|
|
|
#define BOOST_UNORDERED_DETAIL_ALL_HPP_INCLUDED
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <boost/config/no_tr1/cmath.hpp>
|
|
|
|
#include <boost/iterator/iterator_categories.hpp>
|
|
|
|
|
2009-09-04 07:03:04 +00:00
|
|
|
#include <boost/unordered/detail/buckets.hpp>
|
2009-08-30 16:42:28 +00:00
|
|
|
#include <boost/unordered/detail/util.hpp>
|
|
|
|
|
|
|
|
namespace boost { namespace unordered_detail {
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Helper methods
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline bool hash_table<H, P, A, G, K>::equal(
|
|
|
|
key_type const& k, value_type const& v) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
return this->key_eq()(k, get_key(v));
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
inline BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::node_ptr
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::find_iterator(
|
|
|
|
bucket_ptr bucket, key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
node_ptr it = bucket->next_;
|
2009-09-20 21:55:15 +00:00
|
|
|
while (BOOST_UNORDERED_BORLAND_BOOL(it) &&
|
|
|
|
!equal(k, node::get_value(it)))
|
|
|
|
{
|
2009-08-30 16:42:28 +00:00
|
|
|
it = node::next_group(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
2009-09-20 21:55:15 +00:00
|
|
|
// pre: this->buckets_
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
inline BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::node_ptr
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::find_iterator(key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
return find_iterator(this->get_bucket(this->bucket_index(k)), k);
|
|
|
|
}
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::node_ptr*
|
|
|
|
hash_table<H, P, A, G, K>::find_for_erase(
|
|
|
|
bucket_ptr bucket, key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
node_ptr* it = &bucket->next_;
|
2009-09-20 21:55:15 +00:00
|
|
|
while(BOOST_UNORDERED_BORLAND_BOOL(*it) &&
|
|
|
|
!equal(k, node::get_value(*it)))
|
|
|
|
{
|
2009-08-30 16:42:28 +00:00
|
|
|
it = &node::next_group(*it);
|
2009-09-20 21:55:15 +00:00
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Load methods
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
std::size_t hash_table<H, P, A, G, K>::max_size() const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
// size < mlf_ * count
|
|
|
|
return double_to_size_t(ceil(
|
|
|
|
(double) this->mlf_ * this->max_bucket_count())) - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// strong safety
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline std::size_t hash_table<H, P, A, G, K>::bucket_index(
|
|
|
|
key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
// hash_function can throw:
|
2009-09-20 21:55:15 +00:00
|
|
|
return this->hash_function()(k) % this->bucket_count_;
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
// no throw
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline std::size_t hash_table<H, P, A, G, K>::calculate_max_load()
|
|
|
|
{
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
// From 6.3.1/13:
|
|
|
|
// Only resize when size >= mlf_ * count
|
|
|
|
return double_to_size_t(ceil((double) mlf_ * this->bucket_count_));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
void hash_table<H, P, A, G, K>::max_load_factor(float z)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
BOOST_ASSERT(z > 0);
|
|
|
|
mlf_ = (std::max)(z, minimum_max_load_factor);
|
2009-09-20 21:55:15 +00:00
|
|
|
this->max_load_ = this->calculate_max_load();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline std::size_t hash_table<H, P, A, G, K>::min_buckets_for_size(
|
2009-09-21 21:17:19 +00:00
|
|
|
std::size_t size) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
BOOST_ASSERT(this->mlf_ != 0);
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
// From 6.3.1/13:
|
|
|
|
// size < mlf_ * count
|
|
|
|
// => count > size / mlf_
|
|
|
|
//
|
|
|
|
// Or from rehash post-condition:
|
|
|
|
// count > size / mlf_
|
2009-09-21 21:17:19 +00:00
|
|
|
return next_prime(double_to_size_t(floor(size / (double) mlf_)) + 1);
|
2009-09-20 21:55:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// recompute_begin_bucket
|
|
|
|
|
|
|
|
// init_buckets
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
inline void hash_table<H, P, A, G, K>::init_buckets()
|
|
|
|
{
|
|
|
|
if (this->size_) {
|
|
|
|
this->cached_begin_bucket_ = this->buckets_;
|
|
|
|
while (!this->cached_begin_bucket_->next_)
|
|
|
|
++this->cached_begin_bucket_;
|
|
|
|
} else {
|
|
|
|
this->cached_begin_bucket_ = this->get_bucket(this->bucket_count_);
|
|
|
|
}
|
|
|
|
this->max_load_ = calculate_max_load();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
// After an erase cached_begin_bucket_ might be left pointing to
|
|
|
|
// an empty bucket, so this is called to update it
|
|
|
|
//
|
2009-08-30 16:42:28 +00:00
|
|
|
// no throw
|
2009-09-20 21:55:15 +00:00
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline void hash_table<H, P, A, G, K>::recompute_begin_bucket(bucket_ptr b)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_ASSERT(!(b < this->cached_begin_bucket_));
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
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->get_bucket(this->bucket_count_);
|
|
|
|
}
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
// This is called when a range has been erased
|
|
|
|
//
|
|
|
|
// no throw
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline void hash_table<H, P, A, G, K>::recompute_begin_bucket(
|
|
|
|
bucket_ptr b1, bucket_ptr b2)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
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;
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
// 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_);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Constructors
|
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-21 21:17:19 +00:00
|
|
|
hash_table<H, P, A, G, K>::hash_table(std::size_t num_buckets,
|
2009-09-20 21:55:15 +00:00
|
|
|
hasher const& hf, key_equal const& eq, node_allocator const& a)
|
2009-09-21 21:17:19 +00:00
|
|
|
: buckets(a, next_prime(num_buckets)),
|
2009-09-20 21:55:15 +00:00
|
|
|
base(hf, eq),
|
|
|
|
size_(),
|
|
|
|
mlf_(1.0f),
|
|
|
|
cached_begin_bucket_(),
|
|
|
|
max_load_(0)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Construct with allocator
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::hash_table(hash_table const& x,
|
|
|
|
node_allocator const& a)
|
|
|
|
: buckets(a, x.min_buckets_for_size(x.size_)),
|
|
|
|
base(x),
|
|
|
|
size_(x.size_),
|
|
|
|
mlf_(x.mlf_),
|
|
|
|
cached_begin_bucket_(),
|
|
|
|
max_load_(0)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
if(x.size_) {
|
|
|
|
x.copy_buckets_to(*this);
|
|
|
|
this->init_buckets();
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Move Construct
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::hash_table(hash_table& x, move_tag)
|
|
|
|
: buckets(x.node_alloc(), x.bucket_count_),
|
|
|
|
base(x),
|
|
|
|
size_(0),
|
|
|
|
mlf_(1.0f),
|
|
|
|
cached_begin_bucket_(),
|
|
|
|
max_load_(0)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
this->partial_swap(x);
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::hash_table(hash_table& x,
|
|
|
|
node_allocator const& a, move_tag)
|
|
|
|
: buckets(a, x.bucket_count_),
|
|
|
|
base(x),
|
|
|
|
size_(0),
|
|
|
|
mlf_(x.mlf_),
|
|
|
|
cached_begin_bucket_(),
|
|
|
|
max_load_(0)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
if(a == x.node_alloc()) {
|
|
|
|
this->partial_swap(x);
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
2009-09-20 21:55:15 +00:00
|
|
|
else if(x.size_) {
|
|
|
|
x.copy_buckets_to(*this);
|
2009-09-04 07:03:04 +00:00
|
|
|
this->size_ = x.size_;
|
2009-09-20 21:55:15 +00:00
|
|
|
this->init_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>& hash_table<H, P, A, G, K>::operator=(
|
|
|
|
hash_table const& x)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table tmp(x, this->node_alloc());
|
|
|
|
this->fast_swap(tmp);
|
2009-08-30 16:42:28 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Swap & Move
|
|
|
|
|
|
|
|
// Swap
|
|
|
|
//
|
|
|
|
// Strong exception safety
|
|
|
|
//
|
|
|
|
// Can throw if hash or predicate object's copy constructor throws
|
|
|
|
// or if allocators are unequal.
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
inline void hash_table<H, P, A, G, K>::partial_swap(hash_table& x)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
this->buckets::swap(x); // No throw
|
|
|
|
std::swap(this->size_, x.size_);
|
|
|
|
std::swap(this->mlf_, x.mlf_);
|
|
|
|
std::swap(this->cached_begin_bucket_, x.cached_begin_bucket_);
|
|
|
|
std::swap(this->max_load_, x.max_load_);
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
inline void hash_table<H, P, A, G, K>::fast_swap(hash_table& x)
|
|
|
|
{
|
2009-08-30 16:42:28 +00:00
|
|
|
// These can throw, but they only affect the function objects
|
|
|
|
// that aren't in use so it is strongly exception safe, via.
|
|
|
|
// double buffering.
|
2009-09-20 21:55:15 +00:00
|
|
|
{
|
|
|
|
set_hash_functions<H, P> op1(*this, x);
|
|
|
|
set_hash_functions<H, P> op2(x, *this);
|
|
|
|
op1.commit();
|
|
|
|
op2.commit();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
2009-09-20 21:55:15 +00:00
|
|
|
this->buckets::swap(x); // No throw
|
|
|
|
std::swap(this->size_, x.size_);
|
|
|
|
std::swap(this->mlf_, x.mlf_);
|
|
|
|
std::swap(this->cached_begin_bucket_, x.cached_begin_bucket_);
|
|
|
|
std::swap(this->max_load_, x.max_load_);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
inline void hash_table<H, P, A, G, K>::slow_swap(hash_table& x)
|
|
|
|
{
|
|
|
|
if(this == &x) return;
|
|
|
|
|
|
|
|
{
|
|
|
|
// These can throw, but they only affect the function objects
|
|
|
|
// that aren't in use so it is strongly exception safe, via.
|
|
|
|
// double buffering.
|
|
|
|
set_hash_functions<H, P> op1(*this, x);
|
|
|
|
set_hash_functions<H, P> op2(x, *this);
|
|
|
|
|
2009-09-04 07:03:04 +00:00
|
|
|
// Create new buckets in separate hash_buckets objects
|
2009-08-30 16:42:28 +00:00
|
|
|
// which will clean up if anything throws an exception.
|
|
|
|
// (all can throw, but with no effect as these are new objects).
|
2009-09-20 21:55:15 +00:00
|
|
|
|
|
|
|
buckets b1(this->node_alloc(), x.min_buckets_for_size(x.size_));
|
|
|
|
if(x.size_) x.copy_buckets_to(b1);
|
|
|
|
|
|
|
|
buckets b2(x.node_alloc(), this->min_buckets_for_size(this->size_));
|
|
|
|
if(this->size_) copy_buckets_to(b2);
|
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
// Modifying the data, so no throw from now on.
|
2009-09-20 21:55:15 +00:00
|
|
|
|
|
|
|
b1.swap(*this);
|
|
|
|
b2.swap(x);
|
|
|
|
op1.commit();
|
|
|
|
op2.commit();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
2009-09-20 21:55:15 +00:00
|
|
|
|
|
|
|
std::swap(this->size_, x.size_);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
if(this->buckets_) this->init_buckets();
|
|
|
|
if(x.buckets_) x.init_buckets();
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
void hash_table<H, P, A, G, K>::swap(hash_table& x)
|
|
|
|
{
|
|
|
|
if(this->node_alloc() == x.node_alloc()) {
|
|
|
|
if(this != &x) this->fast_swap(x);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->slow_swap(x);
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
2009-09-20 21:55:15 +00:00
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
// Move
|
|
|
|
//
|
|
|
|
// Strong exception safety (might change unused function objects)
|
|
|
|
//
|
|
|
|
// Can throw if hash or predicate object's copy constructor throws
|
|
|
|
// or if allocators are unequal.
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
void hash_table<H, P, A, G, K>::move(hash_table& x)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
// This can throw, but it only affects the function objects
|
|
|
|
// that aren't in use so it is strongly exception safe, via.
|
|
|
|
// double buffering.
|
2009-09-20 21:55:15 +00:00
|
|
|
set_hash_functions<H, P> new_func_this(*this, x);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
if(this->node_alloc() == x.node_alloc()) {
|
2009-09-04 07:03:04 +00:00
|
|
|
this->buckets::move(x); // no throw
|
|
|
|
this->size_ = x.size_;
|
|
|
|
this->cached_begin_bucket_ = x.cached_begin_bucket_;
|
2009-09-20 21:55:15 +00:00
|
|
|
this->max_load_ = x.max_load_;
|
2009-09-04 07:03:04 +00:00
|
|
|
x.size_ = 0;
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
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).
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
buckets b(this->node_alloc(), x.min_buckets_for_size(x.size_));
|
|
|
|
if(x.size_) x.copy_buckets_to(b);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
// Start updating the data here, no throw from now on.
|
2009-09-04 07:03:04 +00:00
|
|
|
this->size_ = x.size_;
|
2009-09-20 21:55:15 +00:00
|
|
|
b.swap(*this);
|
|
|
|
this->init_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We've made it, the rest is no throw.
|
|
|
|
this->mlf_ = x.mlf_;
|
2009-09-20 21:55:15 +00:00
|
|
|
new_func_this.commit();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Reserve & Rehash
|
|
|
|
|
|
|
|
// basic exception safety
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-21 21:17:19 +00:00
|
|
|
inline void hash_table<H, P, A, G, K>::create_for_insert(std::size_t size)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-21 21:17:40 +00:00
|
|
|
std::size_t min_buckets = this->min_buckets_for_size(size);
|
|
|
|
if(min_buckets > this->bucket_count_) this->bucket_count_ = min_buckets;
|
2009-09-20 21:55:15 +00:00
|
|
|
this->create_buckets();
|
|
|
|
this->init_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// basic exception safety
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-21 21:17:19 +00:00
|
|
|
inline bool hash_table<H, P, A, G, K>::reserve_for_insert(std::size_t size)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-21 21:17:19 +00:00
|
|
|
if(size >= max_load_) {
|
2009-08-30 16:42:28 +00:00
|
|
|
std::size_t s = this->size_;
|
|
|
|
s = s + (s >> 1);
|
2009-09-21 21:17:19 +00:00
|
|
|
std::size_t num_buckets
|
|
|
|
= this->min_buckets_for_size(s > size ? s : size);
|
|
|
|
if(num_buckets != this->bucket_count_) {
|
|
|
|
rehash_impl(num_buckets);
|
2009-09-20 21:55:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
2009-09-20 21:55:15 +00:00
|
|
|
|
|
|
|
return false;
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
// strong otherwise.
|
2009-09-20 21:55:15 +00:00
|
|
|
// TODO: Should this always create buckets?
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-21 21:17:19 +00:00
|
|
|
inline void hash_table<H, P, A, G, K>::rehash(std::size_t min_buckets)
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
using namespace std;
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
if(!this->buckets_) {
|
2009-09-21 21:17:19 +00:00
|
|
|
this->bucket_count_ = next_prime(min_buckets);
|
2009-09-20 21:55:15 +00:00
|
|
|
this->create_buckets();
|
|
|
|
this->init_buckets();
|
|
|
|
}
|
2009-09-21 21:17:19 +00:00
|
|
|
// TODO: Another bug:
|
|
|
|
else if(min_buckets != this->bucket_count_) {
|
2009-09-20 21:55:15 +00:00
|
|
|
// no throw:
|
|
|
|
// TODO: Needlessly calling next_prime twice.
|
|
|
|
std::size_t min_size = this->min_buckets_for_size(this->size_);
|
2009-09-21 21:17:19 +00:00
|
|
|
min_buckets = next_prime(min_buckets);
|
|
|
|
min_buckets = min_size > min_buckets ? min_size : min_buckets;
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-21 21:17:19 +00:00
|
|
|
rehash_impl(min_buckets);
|
2009-09-20 21:55:15 +00:00
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
// strong otherwise
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
// TODO: Rewrite so that it doesn't need to keep updating size_.
|
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
void hash_table<H, P, A, G, K>
|
2009-09-21 21:17:19 +00:00
|
|
|
::rehash_impl(std::size_t num_buckets)
|
2009-09-20 21:55:15 +00:00
|
|
|
{
|
|
|
|
hasher const& hf = this->hash_function();
|
2009-09-04 07:03:04 +00:00
|
|
|
std::size_t size = this->size_;
|
2009-09-20 21:55:15 +00:00
|
|
|
bucket_ptr end = this->get_bucket(this->bucket_count_);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-21 21:17:19 +00:00
|
|
|
buckets dst(this->node_alloc(), num_buckets);
|
2009-09-20 21:55:15 +00:00
|
|
|
dst.create_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
buckets src(this->node_alloc(), this->bucket_count_);
|
|
|
|
src.swap(*this);
|
|
|
|
this->size_ = 0;
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
for(bucket_ptr bucket = this->cached_begin_bucket_;
|
|
|
|
bucket != end; ++bucket)
|
|
|
|
{
|
|
|
|
node_ptr group = bucket->next_;
|
|
|
|
while(group) {
|
|
|
|
// Move the first group of equivalent nodes in bucket to dst.
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
// This next line throws iff the hash function throws.
|
|
|
|
bucket_ptr dst_bucket = dst.bucket_ptr_from_hash(
|
2009-09-20 21:55:15 +00:00
|
|
|
hf(get_key_from_ptr(group)));
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
node_ptr& next_group = node::next_group(group);
|
|
|
|
bucket->next_ = next_group;
|
|
|
|
next_group = dst_bucket->next_;
|
|
|
|
dst_bucket->next_ = group;
|
|
|
|
group = bucket->next_;
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-04 07:03:04 +00:00
|
|
|
|
|
|
|
// Swap the new nodes back into the container and setup the local
|
|
|
|
// variables.
|
|
|
|
this->size_ = size;
|
2009-09-20 21:55:15 +00:00
|
|
|
dst.swap(*this); // no throw
|
|
|
|
this->init_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
|
2009-09-04 07:03:04 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// copy_buckets_to
|
|
|
|
|
2009-08-30 16:42:28 +00:00
|
|
|
// copy_buckets_to
|
|
|
|
//
|
|
|
|
// basic excpetion safety. If an exception is thrown this will
|
|
|
|
// leave dst partially filled.
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
void hash_table<H, P, A, G, K>
|
2009-09-04 07:03:04 +00:00
|
|
|
::copy_buckets_to(buckets& dst) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_ASSERT(this->buckets_ && !dst.buckets_);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
hasher const& hf = this->hash_function();
|
2009-09-20 21:55:15 +00:00
|
|
|
bucket_ptr end = this->get_bucket(this->bucket_count_);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
hash_node_constructor<A, G> a(dst);
|
2009-09-20 21:55:15 +00:00
|
|
|
dst.create_buckets();
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
// no throw:
|
|
|
|
for(bucket_ptr i = this->cached_begin_bucket_; i != end; ++i) {
|
|
|
|
// no throw:
|
|
|
|
for(node_ptr it = i->next_; it;) {
|
|
|
|
// hash function can throw.
|
|
|
|
bucket_ptr dst_bucket = dst.bucket_ptr_from_hash(
|
2009-09-20 21:55:15 +00:00
|
|
|
hf(get_key_from_ptr(it)));
|
2009-08-30 16:42:28 +00:00
|
|
|
// throws, strong
|
|
|
|
|
|
|
|
node_ptr group_end = node::next_group(it);
|
|
|
|
|
|
|
|
a.construct(node::get_value(it));
|
|
|
|
node_ptr n = a.release();
|
2009-09-03 07:36:21 +00:00
|
|
|
node::add_to_bucket(n, *dst_bucket);
|
2009-08-30 16:42:28 +00:00
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
for(it = it->next_; it != group_end; it = it->next_) {
|
2009-08-30 16:42:28 +00:00
|
|
|
a.construct(node::get_value(it));
|
2009-09-03 07:36:21 +00:00
|
|
|
node::add_after_node(a.release(), n);
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Misc. key methods
|
|
|
|
|
|
|
|
// strong exception safety
|
|
|
|
|
|
|
|
// count
|
|
|
|
//
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
std::size_t hash_table<H, P, A, G, K>::count(key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
if(!this->size_) return 0;
|
|
|
|
node_ptr it = find_iterator(k); // throws, strong
|
|
|
|
return BOOST_UNORDERED_BORLAND_BOOL(it) ? node::group_count(it) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find
|
|
|
|
//
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::find(key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
if(!this->size_) return this->end();
|
|
|
|
|
|
|
|
bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
|
|
|
|
node_ptr it = find_iterator(bucket, k);
|
|
|
|
|
|
|
|
if (BOOST_UNORDERED_BORLAND_BOOL(it))
|
|
|
|
return iterator_base(bucket, it);
|
|
|
|
else
|
|
|
|
return this->end();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
BOOST_DEDUCED_TYPENAME A::value_type&
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::at(key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
if(!this->size_)
|
|
|
|
throw std::out_of_range("Unable to find key in unordered_map.");
|
|
|
|
|
|
|
|
bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
|
|
|
|
node_ptr it = find_iterator(bucket, k);
|
|
|
|
|
|
|
|
if (BOOST_UNORDERED_BORLAND_BOOL(it))
|
|
|
|
return node::get_value(it);
|
|
|
|
else
|
|
|
|
throw std::out_of_range("Unable to find key in unordered_map.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// equal_range
|
|
|
|
//
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_pair
|
|
|
|
hash_table<H, P, A, G, K>::equal_range(key_type const& k) const
|
2009-08-30 16:42:28 +00:00
|
|
|
{
|
|
|
|
if(!this->size_)
|
2009-09-20 21:55:15 +00:00
|
|
|
return iterator_pair(this->end(), this->end());
|
2009-08-30 16:42:28 +00:00
|
|
|
|
|
|
|
bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
|
|
|
|
node_ptr it = find_iterator(bucket, k);
|
|
|
|
if (BOOST_UNORDERED_BORLAND_BOOL(it)) {
|
|
|
|
iterator_base first(iterator_base(bucket, it));
|
|
|
|
iterator_base second(first);
|
2009-09-20 21:55:15 +00:00
|
|
|
second.increment_bucket(node::next_group(second.node_));
|
|
|
|
return iterator_pair(first, second);
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-09-20 21:55:15 +00:00
|
|
|
return iterator_pair(this->end(), this->end());
|
2009-08-30 16:42:28 +00:00
|
|
|
}
|
|
|
|
}
|
2009-09-04 07:03:04 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Erase methods
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
|
|
|
void hash_table<H, P, A, G, K>::clear()
|
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
// TODO: Is this check needed when called internally?
|
|
|
|
if(!this->size_) return;
|
|
|
|
|
|
|
|
bucket_ptr end = this->get_bucket(this->bucket_count_);
|
|
|
|
for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
|
2009-09-04 07:03:04 +00:00
|
|
|
this->clear_bucket(begin);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->size_ = 0;
|
2009-09-20 21:55:15 +00:00
|
|
|
this->cached_begin_bucket_ = end;
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
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;
|
|
|
|
node_ptr end = node::next_group(pos);
|
|
|
|
*it = end;
|
|
|
|
std::size_t count = this->delete_nodes(pos, end);
|
|
|
|
this->size_ -= count;
|
|
|
|
this->recompute_begin_bucket(bucket);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2009-09-04 07:03:04 +00:00
|
|
|
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)
|
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
if(!this->size_) return 0;
|
|
|
|
|
2009-09-04 07:03:04 +00:00
|
|
|
// 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)
|
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_ASSERT(r.node_);
|
2009-09-04 07:03:04 +00:00
|
|
|
iterator_base next = r;
|
|
|
|
next.increment();
|
|
|
|
--this->size_;
|
|
|
|
node::unlink_node(*r.bucket_, r.node_);
|
2009-09-20 21:55:15 +00:00
|
|
|
this->delete_node(r.node_);
|
2009-09-04 07:03:04 +00:00
|
|
|
// 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>
|
|
|
|
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
|
2009-09-20 21:55:15 +00:00
|
|
|
hash_table<H, P, A, G, K>::erase_range(
|
|
|
|
iterator_base r1, iterator_base r2)
|
2009-09-04 07:03:04 +00:00
|
|
|
{
|
|
|
|
if(r1 != r2)
|
|
|
|
{
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_ASSERT(r1.node_);
|
2009-09-04 07:03:04 +00:00
|
|
|
if (r1.bucket_ == r2.bucket_) {
|
|
|
|
node::unlink_nodes(*r1.bucket_, r1.node_, r2.node_);
|
2009-09-20 21:55:15 +00:00
|
|
|
this->size_ -= this->delete_nodes(r1.node_, r2.node_);
|
2009-09-04 07:03:04 +00:00
|
|
|
|
|
|
|
// No need to call recompute_begin_bucket because
|
|
|
|
// the nodes are only deleted from one bucket, which
|
|
|
|
// still contains r2 after the erase.
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_ASSERT(r1.bucket_->next_);
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-09-20 21:55:15 +00:00
|
|
|
bucket_ptr end_bucket = r2.node_ ?
|
|
|
|
r2.bucket_ : this->get_bucket(this->bucket_count_);
|
|
|
|
BOOST_ASSERT(r1.bucket_ < end_bucket);
|
2009-09-04 07:03:04 +00:00
|
|
|
node::unlink_nodes(*r1.bucket_, r1.node_, node_ptr());
|
2009-09-20 21:55:15 +00:00
|
|
|
this->size_ -= this->delete_nodes(r1.node_, node_ptr());
|
2009-09-04 07:03:04 +00:00
|
|
|
|
|
|
|
bucket_ptr i = r1.bucket_;
|
2009-09-20 21:55:15 +00:00
|
|
|
for(++i; i != end_bucket; ++i) {
|
|
|
|
this->size_ -= this->delete_nodes(i->next_, node_ptr());
|
|
|
|
i->next_ = node_ptr();
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
|
|
|
|
2009-09-20 21:55:15 +00:00
|
|
|
if(r2.node_) {
|
2009-09-04 07:03:04 +00:00
|
|
|
node_ptr first = r2.bucket_->next_;
|
|
|
|
node::unlink_nodes(*r2.bucket_, r2.node_);
|
2009-09-20 21:55:15 +00:00
|
|
|
this->size_ -= this->delete_nodes(first, r2.node_);
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// r1 has been invalidated but its bucket is still
|
|
|
|
// valid.
|
2009-09-20 21:55:15 +00:00
|
|
|
this->recompute_begin_bucket(r1.bucket_, end_bucket);
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return r2;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class H, class P, class A, class G, class K>
|
2009-09-20 21:55:15 +00:00
|
|
|
BOOST_DEDUCED_TYPENAME hash_table<H, P, A, G, K>::iterator_base
|
|
|
|
hash_table<H, P, A, G, K>::emplace_empty_impl_with_node(
|
2009-09-21 21:17:19 +00:00
|
|
|
node_constructor& a, std::size_t size)
|
2009-09-20 21:55:15 +00:00
|
|
|
{
|
|
|
|
key_type const& k = get_key(a.value());
|
|
|
|
std::size_t hash_value = this->hash_function()(k);
|
2009-09-21 21:17:19 +00:00
|
|
|
if(this->buckets_) this->reserve_for_insert(size);
|
|
|
|
else this->create_for_insert(size);
|
2009-09-20 21:55:15 +00:00
|
|
|
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
|
2009-09-21 21:17:19 +00:00
|
|
|
node_ptr n = a.release();
|
|
|
|
node::add_to_bucket(n, *bucket);
|
2009-09-20 21:55:15 +00:00
|
|
|
++this->size_;
|
|
|
|
this->cached_begin_bucket_ = bucket;
|
2009-09-21 21:17:19 +00:00
|
|
|
return iterator_base(bucket, n);
|
2009-09-04 07:03:04 +00:00
|
|
|
}
|
2009-08-30 16:42:28 +00:00
|
|
|
}}
|
|
|
|
|
|
|
|
#endif
|