2005-04-24 14:23:24 +00:00
|
|
|
|
// JTC1/SC22/WG21 N1456 Hash table implementation
|
|
|
|
|
// http://std.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1456.html
|
|
|
|
|
|
|
|
|
|
// boost/detail/hash_table.hpp
|
|
|
|
|
|
|
|
|
|
// Copyright <20> 2003-2004 Jeremy B. Maitin-Shepard.
|
|
|
|
|
// Copyright <20> 2005 Daniel James
|
|
|
|
|
|
|
|
|
|
// Use, modification, and distribution is subject to 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_HASH_TABLE_HPP_INCLUDED
|
|
|
|
|
#define BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
|
|
|
|
# pragma once
|
|
|
|
|
#endif
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#include <boost/config.hpp>
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <boost/iterator.hpp>
|
|
|
|
|
#include <boost/iterator/iterator_categories.hpp>
|
2005-11-05 16:57:31 +00:00
|
|
|
|
#include <boost/limits.hpp>
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#include <boost/assert.hpp>
|
|
|
|
|
#include <boost/static_assert.hpp>
|
|
|
|
|
#include <boost/unordered/detail/allocator.hpp>
|
|
|
|
|
#include <boost/type_traits/is_same.hpp>
|
|
|
|
|
#include <boost/mpl/if.hpp>
|
|
|
|
|
#include <boost/mpl/and.hpp>
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#include <boost/detail/workaround.hpp>
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
#include <boost/mpl/aux_/config/eti.hpp>
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#include <boost/compressed_pair.hpp>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// See hash_table::swap() for details about this.
|
|
|
|
|
#if !defined(BOOST_UNORDERED_SWAP_METHOD)
|
|
|
|
|
#define BOOST_UNORDERED_SWAP_METHOD 3
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if BOOST_UNORDERED_SWAP_METHOD == 1
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if BOOST_WORKAROUND(__BORLANDC__, <= 0x0551)
|
|
|
|
|
#define BOOST_HASH_BORLAND_BOOL(x) (bool)(x)
|
|
|
|
|
#else
|
|
|
|
|
#define BOOST_HASH_BORLAND_BOOL(x) x
|
|
|
|
|
#endif
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
namespace boost {
|
|
|
|
|
namespace unordered_detail {
|
|
|
|
|
template <class T> struct type_wrapper {};
|
|
|
|
|
|
|
|
|
|
const static std::size_t default_initial_bucket_count = 50;
|
|
|
|
|
inline std::size_t next_prime(std::size_t n);
|
|
|
|
|
|
|
|
|
|
template <class T>
|
2006-02-26 18:33:49 +00:00
|
|
|
|
inline void hash_swap(T& x, T& y)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
swap(x, y);
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
inline std::size_t float_to_size_t(float f)
|
2005-11-05 16:57:31 +00:00
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
return f > static_cast<float>((std::numeric_limits<std::size_t>::max)()) ?
|
2005-11-05 16:57:31 +00:00
|
|
|
|
(std::numeric_limits<std::size_t>::max)() :
|
|
|
|
|
static_cast<std::size_t>(f);
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// prime number list, accessor
|
|
|
|
|
|
|
|
|
|
static const std::size_t prime_list[] = {
|
|
|
|
|
53ul, 97ul, 193ul, 389ul, 769ul,
|
|
|
|
|
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
|
|
|
|
|
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
|
|
|
|
|
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
|
|
|
|
|
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
|
|
|
|
|
1610612741ul, 3221225473ul, 4294967291ul };
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
inline std::size_t next_prime(std::size_t n) {
|
|
|
|
|
std::size_t const* bound =
|
|
|
|
|
std::lower_bound(prime_list,prime_list + 28, n);
|
|
|
|
|
if(bound == prime_list + 28)
|
|
|
|
|
bound--;
|
|
|
|
|
return *bound;
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-05 16:57:31 +00:00
|
|
|
|
// no throw
|
|
|
|
|
inline std::size_t prev_prime(std::size_t n) {
|
|
|
|
|
std::size_t const* bound =
|
|
|
|
|
std::upper_bound(prime_list,prime_list + 28, n);
|
|
|
|
|
if(bound != prime_list)
|
|
|
|
|
bound--;
|
|
|
|
|
return *bound;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// pair_cast - used to convert between pair types.
|
|
|
|
|
|
|
|
|
|
template <class Dst1, class Dst2, class Src1, class Src2>
|
|
|
|
|
inline std::pair<Dst1, Dst2> pair_cast(std::pair<Src1, Src2> const& x)
|
|
|
|
|
{
|
|
|
|
|
return std::pair<Dst1, Dst2>(Dst1(x.first), Dst2(x.second));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hash Table Data
|
|
|
|
|
//
|
|
|
|
|
// Responsible for managing the hash buckets. Has no knowledge of hash
|
|
|
|
|
// functions or uniqueness.
|
|
|
|
|
|
|
|
|
|
template <class Alloc>
|
|
|
|
|
class hash_table_data
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
class node;
|
|
|
|
|
class bucket;
|
|
|
|
|
|
|
|
|
|
typedef std::size_t size_type;
|
|
|
|
|
|
|
|
|
|
typedef Alloc value_allocator;
|
2005-06-20 21:55:28 +00:00
|
|
|
|
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
|
|
|
boost::unordered_detail::rebind_wrap<Alloc, node>::type
|
|
|
|
|
node_allocator;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
|
|
|
boost::unordered_detail::rebind_wrap<Alloc, bucket>::type
|
|
|
|
|
bucket_allocator;
|
|
|
|
|
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type value_type;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_pointer<node_allocator>::type node_ptr;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_pointer<bucket_allocator>::type bucket_ptr;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_reference<value_allocator>::type reference;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
#if defined(BOOST_UNORDERED_PARANOID)
|
|
|
|
|
// If the allocator has the expected pointer types I take some liberties.
|
|
|
|
|
BOOST_STATIC_CONSTANT(bool, is_pointer_allocator =
|
|
|
|
|
(boost::mpl::and_<
|
|
|
|
|
boost::is_same<node_ptr, node*>,
|
|
|
|
|
boost::is_same<bucket_ptr, bucket*>
|
|
|
|
|
>::value));
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME boost::mpl::if_c<
|
2005-04-24 14:23:24 +00:00
|
|
|
|
is_pointer_allocator, bucket_ptr, node_ptr>::type link_ptr;
|
|
|
|
|
#else
|
|
|
|
|
typedef bucket_ptr link_ptr;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Hash Bucket
|
|
|
|
|
//
|
|
|
|
|
// all no throw (memory management is performed by hash_table_data).
|
|
|
|
|
|
|
|
|
|
class bucket
|
|
|
|
|
{
|
|
|
|
|
bucket& operator=(bucket const&);
|
|
|
|
|
public:
|
|
|
|
|
link_ptr next_;
|
|
|
|
|
|
|
|
|
|
bucket() : next_()
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
|
|
|
|
unordered_detail::reset(next_);
|
|
|
|
|
#endif
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
bucket(bucket const& x) : next_(x.next_)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
// Only copy construct when allocating.
|
|
|
|
|
BOOST_ASSERT(!x.next_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool empty() const
|
|
|
|
|
{
|
|
|
|
|
return !this->next_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Hash Node
|
|
|
|
|
//
|
|
|
|
|
// all no throw
|
|
|
|
|
|
|
|
|
|
class node : public bucket
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
node(value_type const& v) : bucket(), value_(v) {}
|
|
|
|
|
|
|
|
|
|
value_type value_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if !defined(BOOST_UNORDERED_PARANOID)
|
|
|
|
|
class node_constructor
|
|
|
|
|
{
|
|
|
|
|
node_allocator& node_alloc_;
|
|
|
|
|
bucket_allocator& bucket_alloc_;
|
|
|
|
|
value_allocator value_alloc_;
|
|
|
|
|
|
|
|
|
|
node_ptr ptr_;
|
|
|
|
|
bool value_allocated_;
|
|
|
|
|
bool bucket_allocated_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
node_constructor(node_allocator& n, bucket_allocator& b)
|
|
|
|
|
: node_alloc_(n), bucket_alloc_(b), value_alloc_(n),
|
|
|
|
|
ptr_(), value_allocated_(false), bucket_allocated_(false)
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
|
|
|
|
unordered_detail::reset(ptr_);
|
|
|
|
|
#endif
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~node_constructor()
|
|
|
|
|
{
|
|
|
|
|
if (ptr_) {
|
|
|
|
|
if (value_allocated_)
|
|
|
|
|
value_alloc_.destroy(
|
|
|
|
|
value_alloc_.address(ptr_->value_));
|
|
|
|
|
if (bucket_allocated_)
|
|
|
|
|
bucket_alloc_.destroy(
|
|
|
|
|
bucket_alloc_.address(*ptr_));
|
|
|
|
|
|
|
|
|
|
node_alloc_.deallocate(ptr_, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class V>
|
2005-06-20 21:55:28 +00:00
|
|
|
|
void construct(V const& v)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
BOOST_ASSERT(!ptr_);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
value_allocated_ = bucket_allocated_ = false;
|
|
|
|
|
|
|
|
|
|
ptr_ = node_alloc_.allocate(1);
|
|
|
|
|
|
|
|
|
|
bucket_alloc_.construct(bucket_alloc_.address(
|
|
|
|
|
*ptr_), bucket());
|
|
|
|
|
bucket_allocated_ = true;
|
|
|
|
|
|
|
|
|
|
value_alloc_.construct(value_alloc_.address(
|
|
|
|
|
ptr_->value_), v);
|
|
|
|
|
value_allocated_ = true;
|
2005-06-20 21:55:28 +00:00
|
|
|
|
}
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// no throw
|
|
|
|
|
link_ptr release()
|
|
|
|
|
{
|
2005-04-24 14:23:24 +00:00
|
|
|
|
node_ptr p = ptr_;
|
2006-02-26 18:33:49 +00:00
|
|
|
|
unordered_detail::reset(ptr_);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
return bucket_alloc_.address(*p);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
2006-02-26 18:33:49 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
node_constructor(node_constructor const&);
|
|
|
|
|
node_constructor& operator=(node_constructor const&);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
};
|
2005-06-20 21:55:28 +00:00
|
|
|
|
#else
|
|
|
|
|
class node_constructor
|
|
|
|
|
: public allocator_constructor<node_allocator>
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
node_constructor(node_allocator& n, bucket_allocator&)
|
|
|
|
|
: allocator_constructor<node_allocator>(n);
|
|
|
|
|
};
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
class local_iterator_base
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
link_ptr node_pointer_;
|
|
|
|
|
|
|
|
|
|
local_iterator_base()
|
2006-02-26 18:33:49 +00:00
|
|
|
|
: node_pointer_()
|
|
|
|
|
{
|
|
|
|
|
#if BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
|
|
|
|
unordered_detail::reset(node_pointer_);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
explicit local_iterator_base(link_ptr n)
|
|
|
|
|
: node_pointer_(n) {}
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
bool not_finished() const
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
return node_pointer_ ? true : false;
|
2005-06-20 21:55:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
bool operator==(local_iterator_base const& x) const
|
|
|
|
|
{
|
|
|
|
|
return node_pointer_ == x.node_pointer_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator!=(local_iterator_base const& x) const
|
|
|
|
|
{
|
|
|
|
|
return node_pointer_ != x.node_pointer_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reference operator*() const
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(node_pointer_);
|
|
|
|
|
return static_cast<node&>(*node_pointer_).value_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void increment()
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(node_pointer_);
|
|
|
|
|
node_pointer_ = node_pointer_->next_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Erase Iterator
|
|
|
|
|
//
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// This is an internal 'iterator' (not an STL iterator) which is
|
|
|
|
|
// used to erase or move a node.
|
2005-04-24 14:23:24 +00:00
|
|
|
|
//
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// All no throw.
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
class erase_iterator
|
|
|
|
|
{
|
|
|
|
|
link_ptr* prev_ptr;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
explicit erase_iterator(bucket_ptr b)
|
|
|
|
|
: prev_ptr(&b->next_) {}
|
|
|
|
|
|
|
|
|
|
void next()
|
|
|
|
|
{
|
|
|
|
|
prev_ptr = &(*prev_ptr)->next_;
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
bool not_finished() const
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
return *prev_ptr ? true : false;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value_type& operator*() const
|
|
|
|
|
{
|
|
|
|
|
return static_cast<node&>(**prev_ptr).value_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
link_ptr& get_node() const
|
|
|
|
|
{
|
|
|
|
|
return *prev_ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator!=(local_iterator_base const& y)
|
|
|
|
|
{
|
|
|
|
|
return *prev_ptr != y.node_pointer_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class iterator_base
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
bucket_ptr bucket_;
|
|
|
|
|
local_iterator_base local_;
|
|
|
|
|
|
|
|
|
|
iterator_base()
|
|
|
|
|
: bucket_(), local_() {}
|
|
|
|
|
|
|
|
|
|
iterator_base(bucket_ptr b)
|
|
|
|
|
: bucket_(b), local_(b->next_) {}
|
|
|
|
|
|
|
|
|
|
iterator_base(bucket_ptr b, link_ptr n)
|
|
|
|
|
: bucket_(b), local_(n) {}
|
|
|
|
|
|
|
|
|
|
iterator_base(bucket_ptr b, local_iterator_base const& it)
|
|
|
|
|
: bucket_(b), local_(it) {}
|
|
|
|
|
|
|
|
|
|
local_iterator_base local() const
|
|
|
|
|
{
|
|
|
|
|
return local_iterator_base(local_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator==(iterator_base const& x) const
|
|
|
|
|
{
|
|
|
|
|
return local_ == x.local_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool operator!=(iterator_base const& x) const
|
|
|
|
|
{
|
|
|
|
|
return local_ != x.local_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reference operator*() const
|
|
|
|
|
{
|
|
|
|
|
return *local_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void increment()
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(bucket_);
|
|
|
|
|
local_.increment();
|
|
|
|
|
|
|
|
|
|
while (!local_.node_pointer_) {
|
|
|
|
|
++bucket_;
|
|
|
|
|
local_ = local_iterator_base(bucket_->next_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Member Variables
|
|
|
|
|
|
|
|
|
|
node_allocator node_alloc_;
|
|
|
|
|
bucket_allocator bucket_alloc_;
|
|
|
|
|
|
|
|
|
|
bucket_ptr buckets_;
|
|
|
|
|
size_type bucket_count_;
|
|
|
|
|
bucket_ptr cached_begin_bucket_;
|
|
|
|
|
size_type size_;
|
|
|
|
|
|
|
|
|
|
// Constructor
|
|
|
|
|
|
|
|
|
|
hash_table_data(size_type n, node_allocator const& a)
|
|
|
|
|
: node_alloc_(a), bucket_alloc_(a),
|
|
|
|
|
buckets_(), bucket_count_(next_prime(n)),
|
|
|
|
|
cached_begin_bucket_(), size_(0)
|
|
|
|
|
{
|
|
|
|
|
// The array constructor will clean up in the event of an
|
|
|
|
|
// exception.
|
|
|
|
|
allocator_array_constructor<bucket_allocator>
|
|
|
|
|
constructor(bucket_alloc_);
|
|
|
|
|
constructor.construct(bucket(), bucket_count_ + 1);
|
|
|
|
|
|
|
|
|
|
cached_begin_bucket_ = constructor.get() + bucket_count_;
|
|
|
|
|
|
|
|
|
|
// Only release the buckets once everything is successfully
|
|
|
|
|
// done.
|
|
|
|
|
buckets_ = constructor.release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
~hash_table_data()
|
|
|
|
|
{
|
|
|
|
|
if(buckets_) {
|
|
|
|
|
if(buckets_[bucket_count_].next_) remove_end_marker();
|
|
|
|
|
|
|
|
|
|
for(size_type i = 0; i < bucket_count_; ++i)
|
2005-06-20 21:55:28 +00:00
|
|
|
|
delete_nodes(erase_iterator(buckets_ + i));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
for(size_type i2 = 0; i2 < bucket_count_ + 1; ++i2)
|
|
|
|
|
bucket_alloc_.destroy(buckets_ + i2);
|
|
|
|
|
|
|
|
|
|
bucket_alloc_.deallocate(buckets_, bucket_count_ + 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void add_end_marker()
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) &&
|
|
|
|
|
!buckets_[bucket_count_].next_);
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#if !defined(BOOST_UNORDERED_PARANOID)
|
|
|
|
|
buckets_[bucket_count_].next_ = buckets_ + bucket_count_;
|
|
|
|
|
#else
|
|
|
|
|
if(is_pointer_allocator) {
|
|
|
|
|
buckets_[bucket_count_].next_ = buckets_ + bucket_count_;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// This seems very wasteful, but I can't think of a better
|
|
|
|
|
// way to create an end node and work with all allocators.
|
|
|
|
|
// Although, I might change it to do something different
|
|
|
|
|
// when typename node_allocator::pointer == node*.
|
2005-04-24 14:23:24 +00:00
|
|
|
|
buckets_[bucket_count_].next_ = node_alloc_.allocate(1);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void move_end_marker(hash_table_data& src)
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) &&
|
|
|
|
|
!buckets_[bucket_count_].next_);
|
|
|
|
|
BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(src.buckets_) &&
|
|
|
|
|
BOOST_HASH_BORLAND_BOOL(src.buckets_[src.bucket_count_].next_));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
#if !defined(BOOST_UNORDERED_PARANOID)
|
|
|
|
|
buckets_[bucket_count_].next_ = buckets_ + bucket_count_;
|
|
|
|
|
#else
|
|
|
|
|
if(is_pointer_allocator) {
|
|
|
|
|
buckets_[bucket_count_].next_ = buckets_ + bucket_count_;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
buckets_[bucket_count_].next_
|
|
|
|
|
= src.buckets_[src.bucket_count_].next_;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
unordered_detail::reset(src.buckets_[src.bucket_count_].next_);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void remove_end_marker()
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
BOOST_ASSERT(BOOST_HASH_BORLAND_BOOL(buckets_) &&
|
|
|
|
|
BOOST_HASH_BORLAND_BOOL(buckets_[bucket_count_].next_));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
#if defined(BOOST_UNORDERED_PARANOID)
|
|
|
|
|
if(!is_pointer_allocator)
|
|
|
|
|
node_alloc_.deallocate(static_cast<node_ptr>(
|
|
|
|
|
buckets_[bucket_count_].next_), 1);
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
unordered_detail::reset(buckets_[bucket_count_].next_);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
hash_table_data(hash_table_data const&);
|
|
|
|
|
hash_table_data& operator=(hash_table_data const&);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
void swap(hash_table_data& other)
|
|
|
|
|
{
|
|
|
|
|
std::swap(buckets_, other.buckets_);
|
|
|
|
|
std::swap(bucket_count_, other.bucket_count_);
|
|
|
|
|
std::swap(cached_begin_bucket_, other.cached_begin_bucket_);
|
|
|
|
|
std::swap(size_, other.size_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the bucket index for a hashed value.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
size_type index_from_hash(size_type hashed) const
|
|
|
|
|
{
|
|
|
|
|
return hashed % bucket_count_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Begin & End
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
iterator_base begin() const
|
|
|
|
|
{
|
|
|
|
|
return size_
|
|
|
|
|
? iterator_base(cached_begin_bucket_)
|
|
|
|
|
: end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator_base end() const
|
|
|
|
|
{
|
|
|
|
|
return iterator_base(buckets_ + bucket_count_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_iterator_base begin(size_type n) const
|
|
|
|
|
{
|
|
|
|
|
return local_iterator_base(buckets_[n].next_);
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
local_iterator_base end(size_type) const
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
return local_iterator_base();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local_iterator_base begin(bucket_ptr b) const
|
|
|
|
|
{
|
|
|
|
|
return local_iterator_base(b->next_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bucket Size
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type bucket_size(size_type n) const
|
|
|
|
|
{
|
|
|
|
|
std::size_t count = 0;
|
|
|
|
|
local_iterator_base it1 = begin(n);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while(it1.not_finished()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
++count;
|
|
|
|
|
it1.increment();
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Erase iterator
|
|
|
|
|
//
|
|
|
|
|
// Find the pointer to a node, for use when erasing.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
erase_iterator get_for_erase(iterator_base r) const
|
|
|
|
|
{
|
|
|
|
|
erase_iterator it(r.bucket_);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
local_iterator_base pos(r.local());
|
|
|
|
|
while(it != pos) it.next();
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Link/Unlink/Move Node
|
|
|
|
|
//
|
|
|
|
|
// For adding nodes to buckets, removing them and moving them to a
|
|
|
|
|
// new bucket.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
void link_node(link_ptr node, local_iterator_base pos)
|
|
|
|
|
{
|
|
|
|
|
node->next_ = pos.node_pointer_->next_;
|
|
|
|
|
pos.node_pointer_->next_ = node;
|
|
|
|
|
++size_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void link_node(link_ptr node, bucket_ptr base)
|
|
|
|
|
{
|
|
|
|
|
node->next_ = base->next_;
|
|
|
|
|
base->next_ = node;
|
|
|
|
|
++size_;
|
|
|
|
|
if(base < cached_begin_bucket_) cached_begin_bucket_ = base;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void unlink_node(erase_iterator it)
|
|
|
|
|
{
|
|
|
|
|
it.get_node() = it.get_node()->next_;
|
|
|
|
|
--size_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void move_node(hash_table_data& src, erase_iterator it,
|
|
|
|
|
bucket_ptr dst)
|
|
|
|
|
{
|
|
|
|
|
link_ptr n = it.get_node();
|
|
|
|
|
src.unlink_node(it);
|
|
|
|
|
link_node(n, dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// throws, strong exception-safety:
|
|
|
|
|
link_ptr construct_node(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
node_constructor a(node_alloc_, bucket_alloc_);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
a.construct(v);
|
|
|
|
|
return a.release();
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create Node
|
|
|
|
|
//
|
|
|
|
|
// Create a node and add it to the buckets in the given position.
|
|
|
|
|
//
|
|
|
|
|
// strong exception safety.
|
|
|
|
|
|
|
|
|
|
iterator_base create_node(value_type const& v, bucket_ptr base)
|
|
|
|
|
{
|
|
|
|
|
// throws, strong exception-safety:
|
|
|
|
|
link_ptr node = construct_node(v);
|
|
|
|
|
|
|
|
|
|
// Rest is no throw
|
|
|
|
|
link_node(node, base);
|
|
|
|
|
return iterator_base(base, node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator_base create_node(value_type const& v, iterator_base position)
|
|
|
|
|
{
|
|
|
|
|
// throws, strong exception-safety:
|
|
|
|
|
link_ptr node = construct_node(v);
|
|
|
|
|
|
|
|
|
|
// Rest is no throw
|
|
|
|
|
link_node(node, position.local());
|
|
|
|
|
return iterator_base(position.bucket_, node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator_base create_node(value_type const& v,
|
|
|
|
|
bucket_ptr base, local_iterator_base position)
|
|
|
|
|
{
|
|
|
|
|
// throws, strong exception-safety:
|
|
|
|
|
link_ptr node = construct_node(v);
|
|
|
|
|
|
|
|
|
|
// Rest is no throw
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if(position.not_finished())
|
2005-04-24 14:23:24 +00:00
|
|
|
|
link_node(node, position);
|
|
|
|
|
else
|
|
|
|
|
link_node(node, base);
|
|
|
|
|
|
|
|
|
|
return iterator_base(base, node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete Node
|
|
|
|
|
//
|
|
|
|
|
// Remove a node, or a range of nodes, from a bucket, and destory
|
|
|
|
|
// them.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
void delete_node(erase_iterator it)
|
|
|
|
|
{
|
|
|
|
|
node_ptr n = node_alloc_.address(
|
|
|
|
|
static_cast<node&>(*it.get_node()));
|
|
|
|
|
unlink_node(it);
|
|
|
|
|
|
|
|
|
|
node_alloc_.destroy(n);
|
|
|
|
|
node_alloc_.deallocate(n, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void delete_nodes(erase_iterator begin, local_iterator_base end)
|
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while(begin != end) {
|
|
|
|
|
BOOST_ASSERT(begin.not_finished());
|
|
|
|
|
delete_node(begin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void delete_nodes(erase_iterator begin)
|
|
|
|
|
{
|
|
|
|
|
while(begin.not_finished()) delete_node(begin);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear
|
|
|
|
|
//
|
|
|
|
|
// Remove all the nodes.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
//
|
|
|
|
|
// TODO: If delete_nodes did throw (it shouldn't but just for
|
|
|
|
|
// argument's sake), could this leave cached_begin_bucket_ pointing
|
|
|
|
|
// at an empty bucket?
|
|
|
|
|
|
|
|
|
|
void clear()
|
|
|
|
|
{
|
|
|
|
|
bucket_ptr end = buckets_ + bucket_count_;
|
|
|
|
|
while(cached_begin_bucket_ != end) {
|
2005-06-20 21:55:28 +00:00
|
|
|
|
delete_nodes(erase_iterator(cached_begin_bucket_));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
++cached_begin_bucket_;
|
|
|
|
|
}
|
|
|
|
|
BOOST_ASSERT(!size_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Erase
|
|
|
|
|
//
|
|
|
|
|
// Return type of erase(const_iterator):
|
|
|
|
|
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1753.html#130
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
iterator_base erase(iterator_base r)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(r != end());
|
|
|
|
|
iterator_base next = r;
|
|
|
|
|
next.increment();
|
|
|
|
|
delete_node(get_for_erase(r));
|
|
|
|
|
// r has been invalidated but its bucket is still valid
|
|
|
|
|
recompute_begin_bucket(r.bucket_, next.bucket_);
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator_base erase(iterator_base r1, iterator_base r2)
|
|
|
|
|
{
|
|
|
|
|
if(r1 != r2)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(r1 != end());
|
|
|
|
|
|
|
|
|
|
if (r1.bucket_ == r2.bucket_) {
|
|
|
|
|
delete_nodes(get_for_erase(r1), r2.local());
|
|
|
|
|
|
|
|
|
|
// 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_->empty());
|
|
|
|
|
}
|
|
|
|
|
else {
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_ASSERT(r1.bucket_ < r2.bucket_);
|
|
|
|
|
|
|
|
|
|
delete_nodes(get_for_erase(r1));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
for(bucket_ptr i = r1.bucket_ + 1; i != r2.bucket_; ++i)
|
2005-06-20 21:55:28 +00:00
|
|
|
|
delete_nodes(erase_iterator(i));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
delete_nodes(erase_iterator(r2.bucket_), r2.local());
|
|
|
|
|
|
|
|
|
|
// r1 has been invalidated but its bucket is still
|
|
|
|
|
// valid.
|
|
|
|
|
recompute_begin_bucket(r1.bucket_, r2.bucket_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 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 ptr)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(!(ptr < cached_begin_bucket_));
|
|
|
|
|
|
|
|
|
|
if(ptr == cached_begin_bucket_)
|
|
|
|
|
{
|
|
|
|
|
if (size_ != 0) {
|
|
|
|
|
while (cached_begin_bucket_->empty())
|
|
|
|
|
++cached_begin_bucket_;
|
|
|
|
|
} else {
|
|
|
|
|
cached_begin_bucket_ = buckets_ + bucket_count_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is called when a range has been erased
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
void recompute_begin_bucket(bucket_ptr i, bucket_ptr j)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(!(i < cached_begin_bucket_) && !(j < i));
|
|
|
|
|
BOOST_ASSERT(j == buckets_ + bucket_count_ || !j->empty());
|
|
|
|
|
|
|
|
|
|
if(i == cached_begin_bucket_ && i->empty())
|
|
|
|
|
cached_begin_bucket_ = j;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
#if defined(BOOST_MPL_CFG_MSVC_ETI_BUG)
|
|
|
|
|
template <>
|
|
|
|
|
class hash_table_data<int>
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
public:
|
|
|
|
|
typedef int size_type;
|
|
|
|
|
typedef int iterator_base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
};
|
2005-06-20 21:55:28 +00:00
|
|
|
|
#endif
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
template <class ValueType, class KeyType,
|
|
|
|
|
class Hash, class Pred,
|
|
|
|
|
class Alloc, bool EquivalentKeys>
|
|
|
|
|
class hash_table
|
2005-06-20 21:55:28 +00:00
|
|
|
|
: public hash_table_data<Alloc>
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef hash_table_data<Alloc> data;
|
|
|
|
|
|
|
|
|
|
typedef typename data::node_constructor node_constructor;
|
|
|
|
|
typedef typename data::link_ptr link_ptr;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::value_allocator value_allocator;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::node_allocator node_allocator;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::bucket_ptr bucket_ptr;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::erase_iterator erase_iterator;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
// Type definitions
|
|
|
|
|
|
|
|
|
|
typedef KeyType key_type;
|
|
|
|
|
typedef Hash hasher;
|
|
|
|
|
typedef Pred key_equal;
|
|
|
|
|
typedef ValueType value_type;
|
|
|
|
|
typedef std::size_t size_type;
|
|
|
|
|
|
|
|
|
|
// iterators
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::local_iterator_base local_iterator_base;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// From the compressed functions docs:
|
|
|
|
|
//
|
|
|
|
|
// "Finally, a word of caution for Visual C++ 6 users: if either
|
|
|
|
|
// argument is an empty type, then assigning to that member will
|
|
|
|
|
// produce memory corruption, unless the empty type has a "do
|
|
|
|
|
// nothing" assignment operator defined. This is due to a bug in
|
|
|
|
|
// the way VC6 generates implicit assignment operators."
|
|
|
|
|
//
|
|
|
|
|
// Nice.
|
|
|
|
|
//
|
|
|
|
|
// So use std::pair for Visual C++.
|
|
|
|
|
|
|
|
|
|
class functions
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
boost::compressed_pair<hasher, key_equal> functions_;
|
|
|
|
|
#else
|
2005-06-20 21:55:28 +00:00
|
|
|
|
std::pair<hasher, key_equal> functions_;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
functions(hasher const& h, key_equal const& k)
|
|
|
|
|
: functions_(h, k) {}
|
|
|
|
|
|
|
|
|
|
hasher const& hash_function() const
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return functions_.first();
|
|
|
|
|
#else
|
|
|
|
|
return functions_.first;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key_equal const& key_eq() const
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#if !BOOST_WORKAROUND(BOOST_MSVC, < 1300)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return functions_.second();
|
|
|
|
|
#else
|
|
|
|
|
return functions_.second;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Both hasher and key_equal's copy/assign can throw so double
|
|
|
|
|
// buffering is used to copy them. func_ points to the currently
|
|
|
|
|
// active function objects.
|
|
|
|
|
|
|
|
|
|
typedef functions hash_table::*functions_ptr;
|
|
|
|
|
|
|
|
|
|
functions func1_;
|
|
|
|
|
functions func2_;
|
|
|
|
|
functions_ptr func_;
|
|
|
|
|
|
|
|
|
|
float mlf_;
|
|
|
|
|
size_type max_load_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// Constructors
|
|
|
|
|
|
|
|
|
|
hash_table(size_type n,
|
|
|
|
|
hasher const& hf, key_equal const& eq,
|
|
|
|
|
value_allocator const& a)
|
|
|
|
|
: data(n, a), // throws, cleans itself up
|
|
|
|
|
func1_(hf, eq), // throws " "
|
|
|
|
|
func2_(hf, eq), // throws " "
|
|
|
|
|
func_(&hash_table::func1_), // no throw
|
|
|
|
|
mlf_(1.0f) // no throw
|
|
|
|
|
{
|
|
|
|
|
this->add_end_marker();
|
|
|
|
|
calculate_max_load(); // no throw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Construct from iterators
|
|
|
|
|
|
|
|
|
|
// initial_size
|
|
|
|
|
//
|
|
|
|
|
// A helper function for the copy constructor to calculate how many
|
|
|
|
|
// nodes will be created if the iterator's support it. Might get it
|
|
|
|
|
// totally wrong for containers with unique keys.
|
|
|
|
|
//
|
|
|
|
|
// no throw
|
|
|
|
|
|
|
|
|
|
template <class I>
|
|
|
|
|
std::size_t initial_size(I i, I j, size_type x,
|
|
|
|
|
boost::random_access_traversal_tag)
|
|
|
|
|
{
|
|
|
|
|
// max load factor isn't set yet, but when it is, it'll be 1.0.
|
|
|
|
|
size_type n = j - i + 1;
|
|
|
|
|
return n > x ? n : x;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class I>
|
2006-02-26 18:33:49 +00:00
|
|
|
|
std::size_t initial_size(I, I, size_type n,
|
2005-04-24 14:23:24 +00:00
|
|
|
|
boost::incrementable_traversal_tag)
|
|
|
|
|
{
|
|
|
|
|
return n;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class I>
|
|
|
|
|
std::size_t initial_size(I i, I j, size_type x)
|
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME boost::iterator_traversal<I>::type
|
2005-04-24 14:23:24 +00:00
|
|
|
|
iterator_traversal_tag;
|
2005-06-20 21:55:28 +00:00
|
|
|
|
return initial_size(i, j, x, iterator_traversal_tag);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class I>
|
|
|
|
|
hash_table(I i, I j, size_type n,
|
|
|
|
|
hasher const& hf, key_equal const& eq,
|
2005-11-09 21:15:19 +00:00
|
|
|
|
value_allocator const& a)
|
2005-04-24 14:23:24 +00:00
|
|
|
|
: data(initial_size(i, j, n), a), // throws, cleans itself up
|
|
|
|
|
func1_(hf, eq), // throws " "
|
|
|
|
|
func2_(hf, eq), // throws " "
|
|
|
|
|
func_(&hash_table::func1_), // no throw
|
|
|
|
|
mlf_(1.0f) // no throw
|
|
|
|
|
{
|
|
|
|
|
this->add_end_marker();
|
|
|
|
|
calculate_max_load(); // no throw
|
|
|
|
|
|
|
|
|
|
// This can throw, but hash_table_data's destructor will clean
|
|
|
|
|
// up.
|
|
|
|
|
insert(i, j);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy Construct
|
|
|
|
|
|
|
|
|
|
hash_table(hash_table const& x)
|
|
|
|
|
: data(x.min_buckets_for_size(x.size()), x.node_alloc_), // throws
|
|
|
|
|
func1_(x.current_functions()), // throws
|
|
|
|
|
func2_(x.current_functions()), // throws
|
|
|
|
|
func_(&hash_table::func1_), // no throw
|
|
|
|
|
mlf_(x.mlf_) // no throw
|
|
|
|
|
{
|
|
|
|
|
this->add_end_marker();
|
|
|
|
|
calculate_max_load(); // no throw
|
|
|
|
|
|
|
|
|
|
// This can throw, but hash_table_data's destructor will clean
|
|
|
|
|
// up.
|
|
|
|
|
copy_buckets(x, *this, current_functions());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assign
|
|
|
|
|
//
|
|
|
|
|
// basic exception safety, if copy_functions of reserver throws
|
|
|
|
|
// the container is left in a sane, empty state. If copy_buckets
|
|
|
|
|
// throws the container is left with whatever was successfully
|
|
|
|
|
// copied.
|
|
|
|
|
|
|
|
|
|
hash_table& operator=(hash_table const& x)
|
|
|
|
|
{
|
|
|
|
|
if(this != &x)
|
|
|
|
|
{
|
|
|
|
|
// TODO: I could rewrite this to use the existing nodes.
|
|
|
|
|
this->clear(); // no throw
|
|
|
|
|
func_ = copy_functions(x); // throws, strong
|
|
|
|
|
mlf_ = x.mlf_; // no throw
|
|
|
|
|
calculate_max_load(); // no throw
|
|
|
|
|
reserve(x.size()); // throws
|
|
|
|
|
copy_buckets(x, *this, current_functions()); // throws
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Swap
|
|
|
|
|
//
|
|
|
|
|
// Swap's behaviour when allocators aren't equal is in dispute, see
|
|
|
|
|
// this paper for full details:
|
|
|
|
|
//
|
|
|
|
|
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1599.html
|
|
|
|
|
//
|
|
|
|
|
// It lists 3 possible behaviours:
|
|
|
|
|
//
|
|
|
|
|
// 1 - If the allocators aren't equal then throw an exception.
|
|
|
|
|
// 2 - Reallocate the elements in the containers with the
|
|
|
|
|
// appropriate allocators - messing up exception safety in
|
|
|
|
|
// the process.
|
|
|
|
|
// 3 - Swap the allocators, hoping that the allocators have a
|
|
|
|
|
// no-throw swap.
|
|
|
|
|
//
|
|
|
|
|
// The paper recommends #3.
|
|
|
|
|
//
|
|
|
|
|
// I've implemented all three, but actived #3 by default, to change
|
|
|
|
|
// it '#define BOOST_UNORDERED_SWAP_METHOD n' where n is the option.
|
|
|
|
|
//
|
|
|
|
|
// ----------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
|
// Strong exception safety (might change unused function objects)
|
|
|
|
|
//
|
|
|
|
|
// Can throw if hash or predicate object's copy constructor throws.
|
|
|
|
|
// If allocators are unequal:
|
|
|
|
|
// Method 1: always throws.
|
|
|
|
|
// Method 2: can throw if copying throws
|
|
|
|
|
// (memory allocation/hash function object)
|
|
|
|
|
// Method 3: Can throw if allocator's swap throws
|
|
|
|
|
// (TODO: This one is broken right now, double buffering?)
|
|
|
|
|
|
|
|
|
|
void swap(hash_table& x)
|
|
|
|
|
{
|
|
|
|
|
// This only effects the function objects that aren't in use
|
|
|
|
|
// so it is strongly exception safe, via. double buffering.
|
|
|
|
|
functions_ptr new_func_this = copy_functions(x); // throws
|
|
|
|
|
functions_ptr new_func_that = x.copy_functions(*this); // throws
|
|
|
|
|
|
|
|
|
|
if(this->node_alloc_ == x.node_alloc_) {
|
|
|
|
|
this->data::swap(x); // no throw
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
#if BOOST_UNORDERED_SWAP_METHOD == 1
|
|
|
|
|
throw std::runtime_error(
|
|
|
|
|
"Swapping containers with different allocators.");;
|
|
|
|
|
#elif BOOST_UNORDERED_SWAP_METHOD == 2
|
|
|
|
|
// Create new buckets in separate hash_table_data objects
|
|
|
|
|
// which will clean up if any of this throws.
|
|
|
|
|
data new_this(x.min_buckets_for_size(x.size_),
|
|
|
|
|
this->node_alloc_); // throws
|
|
|
|
|
copy_buckets(x, new_this, this->*new_func_this); // throws
|
|
|
|
|
|
|
|
|
|
data new_that(min_buckets_for_size(this->size_),
|
|
|
|
|
x.node_alloc_); // throws
|
|
|
|
|
x.copy_buckets(*this, new_that, x.*new_func_that); // throws
|
|
|
|
|
|
|
|
|
|
// Start updating the data here, no throw from now on.
|
|
|
|
|
new_this.move_end_marker(*this); // no throw
|
|
|
|
|
new_that.move_end_marker(x); // no throw
|
|
|
|
|
this->data::swap(new_this); // no throw
|
|
|
|
|
x.data::swap(new_that); // no throw
|
|
|
|
|
#elif BOOST_UNORDERED_SWAP_METHOD == 3
|
|
|
|
|
hash_swap(this->node_alloc_,
|
|
|
|
|
x.node_alloc_); // no throw, or is it?
|
|
|
|
|
hash_swap(this->bucket_alloc_,
|
|
|
|
|
x.bucket_alloc_); // no throw, or is it?
|
|
|
|
|
this->data::swap(x); // no throw
|
|
|
|
|
#else
|
|
|
|
|
#error "Invalid swap method"
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We've made it, the rest is no throw.
|
|
|
|
|
std::swap(mlf_, x.mlf_);
|
|
|
|
|
|
|
|
|
|
func_ = new_func_this;
|
|
|
|
|
x.func_ = new_func_that;
|
|
|
|
|
|
|
|
|
|
calculate_max_load();
|
|
|
|
|
x.calculate_max_load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
functions const& current_functions() const
|
|
|
|
|
{
|
|
|
|
|
return this->*func_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This copies the given function objects into the currently unused
|
|
|
|
|
// function objects and returns a pointer, that func_ can later be
|
|
|
|
|
// set to, to commit the change.
|
|
|
|
|
//
|
|
|
|
|
// Strong exception safety (since only usued function objects are
|
|
|
|
|
// changed).
|
|
|
|
|
functions_ptr copy_functions(hash_table const& x)
|
|
|
|
|
{
|
|
|
|
|
// no throw:
|
|
|
|
|
functions_ptr ptr = func_ == &hash_table::func1_
|
|
|
|
|
? &hash_table::func2_ : &hash_table::func1_;
|
|
|
|
|
// throws, functions not in use, so strong
|
|
|
|
|
this->*ptr = x.current_functions();
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// accessors
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
// TODO: This creates an unnecessary copy.
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// no throw
|
2006-02-26 18:33:49 +00:00
|
|
|
|
value_allocator get_allocator() const
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
return this->node_alloc_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
hasher const& hash_function() const
|
|
|
|
|
{
|
|
|
|
|
return current_functions().hash_function();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
key_equal const& key_eq() const
|
|
|
|
|
{
|
|
|
|
|
return current_functions().key_eq();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type size() const
|
|
|
|
|
{
|
|
|
|
|
return this->size_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
bool empty() const
|
|
|
|
|
{
|
|
|
|
|
return this->size_ == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type max_size() const
|
|
|
|
|
{
|
2005-11-05 16:57:31 +00:00
|
|
|
|
// size < mlf_ * count
|
|
|
|
|
return float_to_size_t(ceil(
|
|
|
|
|
max_bucket_count() * mlf_)) - 1;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong safety
|
|
|
|
|
size_type bucket(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
// hash_function can throw:
|
|
|
|
|
return this->index_from_hash(hash_function()(k));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong safety
|
|
|
|
|
bucket_ptr get_bucket(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
return this->buckets_ + bucket(k);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type bucket_count() const
|
|
|
|
|
{
|
|
|
|
|
return this->bucket_count_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type max_bucket_count() const
|
|
|
|
|
{
|
2005-11-05 16:57:31 +00:00
|
|
|
|
// -1 to account for the end marker.
|
|
|
|
|
return prev_prime(this->bucket_alloc_.max_size() - 1);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
size_type min_buckets_for_size(size_type n) const
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(mlf_ != 0);
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
// From 6.3.1/13:
|
|
|
|
|
// size < mlf_ * count
|
|
|
|
|
// => count > size / mlf_
|
|
|
|
|
//
|
|
|
|
|
// Or from rehash post-condition:
|
|
|
|
|
// count > size / mlf_
|
|
|
|
|
return static_cast<size_type>(floor(n / mlf_)) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
void calculate_max_load()
|
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
// From 6.3.1/13:
|
|
|
|
|
// Only resize when size >= mlf_ * count
|
2005-11-05 16:57:31 +00:00
|
|
|
|
max_load_ = float_to_size_t(ceil(mlf_ * this->bucket_count_));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// basic exception safety
|
|
|
|
|
bool reserve(size_type n)
|
|
|
|
|
{
|
|
|
|
|
bool need_to_reserve = n >= max_load_;
|
|
|
|
|
// throws - basic:
|
|
|
|
|
if (need_to_reserve) rehash_impl(min_buckets_for_size(n));
|
2005-11-05 16:57:31 +00:00
|
|
|
|
BOOST_ASSERT(n < max_load_);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return need_to_reserve;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
float max_load_factor() const
|
|
|
|
|
{
|
|
|
|
|
return mlf_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
2005-11-05 16:57:31 +00:00
|
|
|
|
//
|
|
|
|
|
// TODO: the argument is a hint. So don't use it if it's
|
|
|
|
|
// unreasonably small.
|
2005-04-24 14:23:24 +00:00
|
|
|
|
void max_load_factor(float z)
|
|
|
|
|
{
|
2005-11-05 16:57:31 +00:00
|
|
|
|
BOOST_ASSERT(z > 0);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
mlf_ = z;
|
|
|
|
|
calculate_max_load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
float load_factor() const
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(this->bucket_count_ != 0);
|
|
|
|
|
return static_cast<float>(this->size_)
|
|
|
|
|
/ static_cast<float>(this->bucket_count_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// key extractors
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
static key_type const& extract_key(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
return extract(v, (type_wrapper<value_type>*)0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static key_type const& extract(value_type const& v,
|
|
|
|
|
type_wrapper<key_type>*)
|
|
|
|
|
{
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static key_type const& extract(value_type const& v,
|
|
|
|
|
void*)
|
|
|
|
|
{
|
|
|
|
|
return v.first;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise.
|
|
|
|
|
void rehash(size_type n)
|
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
// no throw:
|
|
|
|
|
size_type min_size = min_buckets_for_size(size());
|
|
|
|
|
// basic/strong:
|
|
|
|
|
rehash_impl(min_size > n ? min_size : n);
|
|
|
|
|
|
|
|
|
|
BOOST_ASSERT(bucket_count() > size() / max_load_factor()
|
|
|
|
|
&& bucket_count() >= n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
void rehash_impl(size_type n)
|
|
|
|
|
{
|
|
|
|
|
n = next_prime(n); // no throw
|
|
|
|
|
|
|
|
|
|
if (n == bucket_count()) // no throw
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
data new_buckets(n, this->node_alloc_); // throws, seperate
|
|
|
|
|
move_buckets(*this, new_buckets); // basic/no throw
|
|
|
|
|
new_buckets.swap(*this); // no throw
|
|
|
|
|
calculate_max_load(); // no throw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// move_buckets & copy_buckets
|
|
|
|
|
//
|
|
|
|
|
// Note: Because equivalent elements are already
|
|
|
|
|
// adjacent to each other in the existing buckets, this
|
|
|
|
|
// simple rehashing technique is sufficient to ensure
|
|
|
|
|
// that they remain adjacent to each other in the new
|
|
|
|
|
// buckets (but in reverse order).
|
|
|
|
|
//
|
|
|
|
|
// if the hash function throws, basic excpetion safety
|
|
|
|
|
// no throw otherwise
|
|
|
|
|
|
|
|
|
|
void move_buckets(data& src, data& dst)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(dst.size_ == 0);
|
|
|
|
|
BOOST_ASSERT(src.node_alloc_ == dst.node_alloc_);
|
|
|
|
|
|
|
|
|
|
bucket_ptr end = src.buckets_ + src.bucket_count_; // no throw
|
2005-06-20 21:55:28 +00:00
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
for(; src.cached_begin_bucket_ != end; // no throw
|
|
|
|
|
++src.cached_begin_bucket_) {
|
|
|
|
|
erase_iterator it(src.cached_begin_bucket_); // no throw
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while(it.not_finished()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// This next line throws iff the hash function throws.
|
|
|
|
|
bucket_ptr dst_bucket = dst.buckets_ +
|
|
|
|
|
dst.index_from_hash(
|
|
|
|
|
hash_function()(extract_key(*it)));
|
|
|
|
|
|
|
|
|
|
dst.move_node(src, it, dst_bucket); // no throw
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Move the end marker from the source to destination.
|
|
|
|
|
// Now destination is valid, source is not.
|
|
|
|
|
dst.move_end_marker(src);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// basic excpetion safety, will leave dst partially filled.
|
|
|
|
|
|
|
|
|
|
static void copy_buckets(data const& src, data& dst, functions const& f)
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(dst.size_ == 0);
|
|
|
|
|
// no throw:
|
|
|
|
|
bucket_ptr end = src.buckets_ + src.bucket_count_;
|
|
|
|
|
hasher const& hf = f.hash_function();
|
|
|
|
|
|
|
|
|
|
// no throw:
|
|
|
|
|
for(bucket_ptr i = src.cached_begin_bucket_; i != end; ++i) {
|
|
|
|
|
// no throw:
|
|
|
|
|
for(local_iterator_base it = src.begin(i);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
it.not_finished(); it.increment()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// hash function can throw.
|
|
|
|
|
bucket_ptr dst_bucket = dst.buckets_ +
|
|
|
|
|
dst.index_from_hash(hf(extract_key(*it)));
|
|
|
|
|
// throws, strong
|
|
|
|
|
dst.create_node(*it, dst_bucket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// Insert functions
|
|
|
|
|
//
|
|
|
|
|
// basic exception safety, if hash function throws
|
|
|
|
|
// strong otherwise.
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
value_type& operator[](key_type const& k)
|
|
|
|
|
{
|
|
|
|
|
BOOST_STATIC_ASSERT(!EquivalentKeys);
|
|
|
|
|
BOOST_STATIC_ASSERT((
|
|
|
|
|
!boost::is_same<value_type, key_type>::value));
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
bucket_ptr bucket = get_bucket(k);
|
|
|
|
|
local_iterator_base node = find_iterator(bucket, k);
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if (node.not_finished())
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return *node;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Effects only in this block:
|
|
|
|
|
|
2005-11-05 16:57:31 +00:00
|
|
|
|
// Create the node before rehashing in case it throws.
|
|
|
|
|
// throws, no side effects:
|
|
|
|
|
node_constructor a(this->node_alloc_, this->bucket_alloc_);
|
|
|
|
|
a.construct(value_type(k, mapped_type()));
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
if (reserve(size() + 1)) // basic/strong
|
|
|
|
|
bucket = get_bucket(k); // throws, strong
|
2005-11-05 16:57:31 +00:00
|
|
|
|
|
|
|
|
|
link_ptr node = a.release();
|
|
|
|
|
this->link_node(node, bucket);
|
|
|
|
|
|
|
|
|
|
return *local_iterator_base(node);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert for containers with equivalent keys
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// Insert node without checking if a resize is necessary.
|
|
|
|
|
|
|
|
|
|
// strong exception safety.
|
|
|
|
|
iterator_base unchecked_insert_equivalent(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
key_type const& k = extract_key(v);
|
|
|
|
|
bucket_ptr bucket = get_bucket(k);
|
|
|
|
|
local_iterator_base position = find_iterator(bucket, k);
|
|
|
|
|
|
|
|
|
|
// No effects until here.
|
|
|
|
|
return this->create_node(v, bucket, position); // throws, strong
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong exception safety
|
|
|
|
|
iterator_base unchecked_insert_equivalent(iterator_base const& it,
|
|
|
|
|
value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
// Condition throws, no side effects.
|
|
|
|
|
if(it != this->end() && equal(extract_key(v), *it)) {
|
2005-06-20 21:55:28 +00:00
|
|
|
|
return this->create_node(v, it); // throws, strong
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
2005-06-20 21:55:28 +00:00
|
|
|
|
return unchecked_insert_equivalent(v); // throws, strong
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
iterator_base insert_equivalent(value_type const& v)
|
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
key_type const& k = extract_key(v);
|
|
|
|
|
size_type hash_value = hash_function()(k);
|
|
|
|
|
bucket_ptr bucket = this->buckets_
|
|
|
|
|
+ this->index_from_hash(hash_value);
|
|
|
|
|
local_iterator_base position = find_iterator(bucket, k);
|
|
|
|
|
|
|
|
|
|
// Create the node before rehashing in case it throws.
|
|
|
|
|
// throws, no side effects:
|
|
|
|
|
node_constructor a(this->node_alloc_, this->bucket_alloc_);
|
|
|
|
|
a.construct(v);
|
|
|
|
|
|
|
|
|
|
// strong/no throw:
|
|
|
|
|
if(reserve(size() + 1)) // basic/strong
|
|
|
|
|
bucket = this->buckets_ + this->index_from_hash(hash_value);
|
|
|
|
|
|
|
|
|
|
// No throw from here.
|
|
|
|
|
|
|
|
|
|
link_ptr node = a.release();
|
|
|
|
|
|
|
|
|
|
// I'm relying on local_iterator_base not being invalidated by
|
|
|
|
|
// the rehash here.
|
|
|
|
|
if(position.not_finished())
|
2005-11-05 16:57:31 +00:00
|
|
|
|
this->link_node(node, position);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
else
|
2005-11-05 16:57:31 +00:00
|
|
|
|
this->link_node(node, bucket);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
|
|
|
|
|
return iterator_base(bucket, node);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
iterator_base insert_equivalent(iterator_base const& it, value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
if (it != this->end() && equal(extract_key(v), *it)) { // throws, no side effects
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// Create the node before rehashing in case it throws.
|
|
|
|
|
// throws, no side effects:
|
|
|
|
|
node_constructor a(this->node_alloc_, this->bucket_alloc_);
|
|
|
|
|
a.construct(v);
|
|
|
|
|
|
|
|
|
|
// The hash function can throw in get_bucket, but that's okay
|
|
|
|
|
// because then only basic exception safety is required.
|
|
|
|
|
bucket_ptr base = reserve(size() + 1) ?
|
|
|
|
|
get_bucket(extract_key(v)) : it.bucket_;
|
|
|
|
|
|
|
|
|
|
link_ptr node = a.release();
|
2005-11-05 16:57:31 +00:00
|
|
|
|
this->link_node(node, it.local());
|
2005-06-20 21:55:28 +00:00
|
|
|
|
|
|
|
|
|
return iterator_base(base, node);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return insert_equivalent(v); // basic/strong
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// Insert for containers with unique keys
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
std::pair<iterator_base, bool> insert_unique(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
// Throws, but no side effects in this initial code
|
|
|
|
|
key_type const& k = extract_key(v);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
size_type hash_value = hash_function()(k);
|
|
|
|
|
bucket_ptr bucket = this->buckets_
|
|
|
|
|
+ this->index_from_hash(hash_value);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
local_iterator_base pos = find_iterator(bucket, k);
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if (pos.not_finished()) { // no throw
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// Found existing key, return it.
|
|
|
|
|
return std::pair<iterator_base, bool>(
|
|
|
|
|
iterator_base(bucket, pos), false); // no throw
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// Doesn't already exist, add to bucket.
|
|
|
|
|
// Data is only changed in this block.
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
// Create the node before rehashing in case it throws.
|
|
|
|
|
// throws, no side effects:
|
|
|
|
|
node_constructor a(this->node_alloc_, this->bucket_alloc_);
|
|
|
|
|
a.construct(v);
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
// If we resize, then need to recalculate bucket.
|
|
|
|
|
if(reserve(size() + 1)) // throws, basic/strong
|
2005-06-20 21:55:28 +00:00
|
|
|
|
bucket = this->buckets_ + this->index_from_hash(hash_value);
|
|
|
|
|
|
|
|
|
|
link_ptr node = a.release();
|
2005-11-05 16:57:31 +00:00
|
|
|
|
this->link_node(node, bucket);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
return std::pair<iterator_base, bool>(
|
2005-06-20 21:55:28 +00:00
|
|
|
|
iterator_base(bucket, node), true); // throws, strong
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
iterator_base insert_unique(iterator_base const& it, value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
// If we are given an iterator pointer to the given key,
|
|
|
|
|
// then just return it.
|
|
|
|
|
if(it != this->end() && equal(extract_key(v), *it)) // throws, strong
|
|
|
|
|
return it;
|
|
|
|
|
else
|
|
|
|
|
return insert_unique(v).first; // basic, if hash
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
void insert(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
if(EquivalentKeys)
|
|
|
|
|
insert_equivalent(v);
|
|
|
|
|
else
|
|
|
|
|
insert_unique(v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if hash function throws, basic exception safety
|
|
|
|
|
// strong otherwise
|
|
|
|
|
void unchecked_insert(value_type const& v)
|
|
|
|
|
{
|
|
|
|
|
if(EquivalentKeys)
|
|
|
|
|
unchecked_insert_equivalent(v);
|
|
|
|
|
else
|
|
|
|
|
insert_unique(v);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert from iterators
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
// if hash function throws, or inserting > 1 element, basic exception safety
|
|
|
|
|
// strong otherwise
|
2005-04-24 14:23:24 +00:00
|
|
|
|
template <class I>
|
|
|
|
|
void insert_for_range(I i, I j,
|
|
|
|
|
boost::random_access_traversal_tag)
|
|
|
|
|
{
|
2006-02-26 18:33:49 +00:00
|
|
|
|
if(EquivalentKeys) {
|
|
|
|
|
std::size_t distance = j - i;
|
|
|
|
|
if(distance == 1) {
|
|
|
|
|
insert(*i);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
reserve(size() + distance); // basic/strong
|
|
|
|
|
for (; i != j; ++i) unchecked_insert(*i); // strong
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (; i != j; ++i) insert_unique(*i); // basic/strong
|
|
|
|
|
}
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
// if hash function throws, or inserting > 1 element, basic exception safety
|
|
|
|
|
// strong otherwise
|
2005-04-24 14:23:24 +00:00
|
|
|
|
template <class I>
|
|
|
|
|
void insert_for_range(I i, I j,
|
|
|
|
|
boost::incrementable_traversal_tag)
|
|
|
|
|
{
|
|
|
|
|
for (; i != j; ++i) insert(*i); // basic/strong
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
// if hash function throws, or inserting > 1 element, basic exception safety
|
|
|
|
|
// strong otherwise
|
2005-04-24 14:23:24 +00:00
|
|
|
|
template <class InputIterator>
|
|
|
|
|
void insert(InputIterator i, InputIterator j)
|
|
|
|
|
{
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME boost::iterator_traversal<InputIterator>::type
|
2005-04-24 14:23:24 +00:00
|
|
|
|
iterator_traversal_tag;
|
2005-06-20 21:55:28 +00:00
|
|
|
|
insert_for_range(i, j, iterator_traversal_tag);
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
// erase
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
iterator_base erase(iterator_base const& r)
|
|
|
|
|
{
|
|
|
|
|
return this->data::erase(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong exception safety
|
|
|
|
|
size_type erase(key_type const& k)
|
|
|
|
|
{
|
|
|
|
|
// No side effects in initial section
|
|
|
|
|
bucket_ptr bucket = get_bucket(k);
|
|
|
|
|
size_type count = 0;
|
|
|
|
|
erase_iterator it(find_for_erase(bucket, k));
|
|
|
|
|
|
|
|
|
|
// Rest is no throw, side effects only after this point.
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if (it.not_finished()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
if (EquivalentKeys) {
|
|
|
|
|
do {
|
|
|
|
|
++count;
|
|
|
|
|
this->delete_node(it);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
} while(it.not_finished() && equal(k, *it));
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
count = 1;
|
|
|
|
|
this->delete_node(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->recompute_begin_bucket(bucket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no throw
|
|
|
|
|
iterator_base erase(iterator_base const& r1, iterator_base const& r2)
|
|
|
|
|
{
|
|
|
|
|
return this->data::erase(r1, r2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// count
|
|
|
|
|
//
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
size_type count(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
local_iterator_base it = find_iterator(k); // throws, strong
|
|
|
|
|
size_type count = 0;
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if(it.not_finished()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
if(EquivalentKeys) {
|
|
|
|
|
do {
|
|
|
|
|
++count;
|
|
|
|
|
it.increment();
|
2005-06-20 21:55:28 +00:00
|
|
|
|
} while (it.not_finished() && equal(k, *it)); // throws, strong
|
2005-04-24 14:23:24 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
count = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// find
|
|
|
|
|
//
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
iterator_base find(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
bucket_ptr bucket = get_bucket(k);
|
|
|
|
|
local_iterator_base it = find_iterator(bucket, k);
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if (it.not_finished())
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return iterator_base(bucket, it);
|
|
|
|
|
else
|
|
|
|
|
return this->end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// equal_range
|
|
|
|
|
//
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
std::pair<iterator_base, iterator_base> equal_range(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
bucket_ptr bucket = get_bucket(k);
|
|
|
|
|
local_iterator_base it = find_iterator(bucket, k);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
if (it.not_finished()) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
local_iterator_base last = it;
|
|
|
|
|
|
|
|
|
|
if(EquivalentKeys) {
|
|
|
|
|
local_iterator_base next = last;
|
|
|
|
|
next.increment();
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while(next.not_finished() && equal(k, *next)) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
last = next;
|
|
|
|
|
next.increment();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iterator_base first(iterator_base(bucket, it));
|
|
|
|
|
iterator_base second(iterator_base(bucket, last));
|
|
|
|
|
second.increment();
|
|
|
|
|
return std::pair<iterator_base, iterator_base>(first, second);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return std::pair<iterator_base, iterator_base>(
|
|
|
|
|
this->end(), this->end());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
bool equal(key_type const& k, value_type const& v) const
|
|
|
|
|
{
|
|
|
|
|
return key_eq()(k, extract_key(v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
local_iterator_base find_iterator(key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
return find_iterator(get_bucket(k), k);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
local_iterator_base find_iterator(bucket_ptr bucket,
|
|
|
|
|
key_type const& k) const
|
|
|
|
|
{
|
|
|
|
|
local_iterator_base it = this->begin(bucket);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while (it.not_finished() && !equal(k, *it))
|
2005-04-24 14:23:24 +00:00
|
|
|
|
it.increment();
|
|
|
|
|
|
|
|
|
|
return it;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// strong exception safety, no side effects
|
|
|
|
|
erase_iterator find_for_erase(bucket_ptr bucket, key_type const& k)
|
|
|
|
|
const
|
|
|
|
|
{
|
|
|
|
|
erase_iterator it(bucket);
|
2005-06-20 21:55:28 +00:00
|
|
|
|
while(it.not_finished() && !equal(k, *it))
|
2005-04-24 14:23:24 +00:00
|
|
|
|
it.next();
|
|
|
|
|
|
|
|
|
|
return it;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Iterators
|
|
|
|
|
|
|
|
|
|
template <class Alloc> class hash_iterator;
|
|
|
|
|
template <class Alloc> class hash_const_iterator;
|
|
|
|
|
template <class Alloc> class hash_local_iterator;
|
|
|
|
|
template <class Alloc> class hash_const_local_iterator;
|
|
|
|
|
class iterator_access;
|
|
|
|
|
|
|
|
|
|
// Local Iterators
|
|
|
|
|
//
|
|
|
|
|
// all no throw
|
|
|
|
|
|
|
|
|
|
template <class Alloc>
|
|
|
|
|
class hash_local_iterator
|
|
|
|
|
: public boost::iterator <
|
|
|
|
|
std::forward_iterator_tag,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type,
|
2005-04-24 14:23:24 +00:00
|
|
|
|
std::ptrdiff_t,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_pointer<Alloc>::type,
|
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_reference<Alloc>::type >
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type value_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME hash_table_data<Alloc>::local_iterator_base base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
typedef hash_const_local_iterator<Alloc> const_local_iterator;
|
|
|
|
|
|
|
|
|
|
friend class hash_const_local_iterator<Alloc>;
|
|
|
|
|
base base_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
hash_local_iterator() : base_() {}
|
|
|
|
|
explicit hash_local_iterator(base x) : base_(x) {}
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_reference<Alloc>::type operator*() const
|
|
|
|
|
{ return *base_; }
|
2005-04-24 14:23:24 +00:00
|
|
|
|
value_type* operator->() const { return &*base_; }
|
|
|
|
|
hash_local_iterator& operator++() { base_.increment(); return *this; }
|
|
|
|
|
hash_local_iterator operator++(int) { hash_local_iterator tmp(base_); base_.increment(); return tmp; }
|
|
|
|
|
bool operator==(hash_local_iterator x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator==(const_local_iterator x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator!=(hash_local_iterator x) const { return base_ != x.base_; }
|
|
|
|
|
bool operator!=(const_local_iterator x) const { return base_ != x.base_; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class Alloc>
|
|
|
|
|
class hash_const_local_iterator
|
|
|
|
|
: public boost::iterator <
|
|
|
|
|
std::forward_iterator_tag,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type,
|
2005-04-24 14:23:24 +00:00
|
|
|
|
std::ptrdiff_t,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_pointer<Alloc>::type,
|
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_reference<Alloc>::type >
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type value_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME hash_table_data<Alloc>::local_iterator_base base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
typedef hash_local_iterator<Alloc> local_iterator;
|
|
|
|
|
friend class hash_local_iterator<Alloc>;
|
|
|
|
|
base base_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
hash_const_local_iterator() : base_() {}
|
|
|
|
|
explicit hash_const_local_iterator(base x) : base_(x) {}
|
|
|
|
|
hash_const_local_iterator(local_iterator x) : base_(x.base_) {}
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_reference<Alloc>::type
|
|
|
|
|
operator*() const { return *base_; }
|
2005-04-24 14:23:24 +00:00
|
|
|
|
value_type const* operator->() const { return &*base_; }
|
|
|
|
|
hash_const_local_iterator& operator++() { base_.increment(); return *this; }
|
|
|
|
|
hash_const_local_iterator operator++(int) { hash_const_local_iterator tmp(base_); base_.increment(); return tmp; }
|
|
|
|
|
bool operator==(local_iterator x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator==(hash_const_local_iterator x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator!=(local_iterator x) const { return base_ != x.base_; }
|
|
|
|
|
bool operator!=(hash_const_local_iterator x) const { return base_ != x.base_; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// iterators
|
|
|
|
|
//
|
|
|
|
|
// all no throw
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class Alloc>
|
|
|
|
|
class hash_iterator
|
|
|
|
|
: public boost::iterator <
|
|
|
|
|
std::forward_iterator_tag,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type,
|
2005-04-24 14:23:24 +00:00
|
|
|
|
std::ptrdiff_t,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_pointer<Alloc>::type,
|
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_reference<Alloc>::type >
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type value_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME hash_table_data<Alloc>::iterator_base base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
typedef hash_const_iterator<Alloc> const_iterator;
|
|
|
|
|
friend class hash_const_iterator<Alloc>;
|
|
|
|
|
base base_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
hash_iterator() : base_() {}
|
|
|
|
|
explicit hash_iterator(base const& x) : base_(x) {}
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_reference<Alloc>::type
|
|
|
|
|
operator*() const { return *base_; }
|
2005-04-24 14:23:24 +00:00
|
|
|
|
value_type* operator->() const { return &*base_; }
|
|
|
|
|
hash_iterator& operator++() { base_.increment(); return *this; }
|
|
|
|
|
hash_iterator operator++(int) { hash_iterator tmp(base_); base_.increment(); return tmp; }
|
|
|
|
|
bool operator==(hash_iterator const& x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator==(const_iterator const& x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator!=(hash_iterator const& x) const { return base_ != x.base_; }
|
|
|
|
|
bool operator!=(const_iterator const& x) const { return base_ != x.base_; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class Alloc>
|
|
|
|
|
class hash_const_iterator
|
|
|
|
|
: public boost::iterator <
|
|
|
|
|
std::forward_iterator_tag,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type,
|
2005-04-24 14:23:24 +00:00
|
|
|
|
std::ptrdiff_t,
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_pointer<Alloc>::type,
|
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_reference<Alloc>::type >
|
2005-04-24 14:23:24 +00:00
|
|
|
|
{
|
|
|
|
|
public:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_value_type<Alloc>::type value_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
private:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME hash_table_data<Alloc>::iterator_base base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
typedef hash_iterator<Alloc> iterator;
|
|
|
|
|
friend class hash_iterator<Alloc>;
|
|
|
|
|
friend class iterator_access;
|
|
|
|
|
base base_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
hash_const_iterator() : base_() {}
|
|
|
|
|
explicit hash_const_iterator(base const& x) : base_(x) {}
|
|
|
|
|
hash_const_iterator(iterator const& x) : base_(x.base_) {}
|
2005-06-20 21:55:28 +00:00
|
|
|
|
BOOST_DEDUCED_TYPENAME allocator_const_reference<Alloc>::type
|
|
|
|
|
operator*() const { return *base_; }
|
2005-04-24 14:23:24 +00:00
|
|
|
|
value_type const* operator->() const { return &*base_; }
|
|
|
|
|
hash_const_iterator& operator++() { base_.increment(); return *this; }
|
|
|
|
|
hash_const_iterator operator++(int) { hash_const_iterator tmp(base_); base_.increment(); return tmp; }
|
|
|
|
|
bool operator==(iterator const& x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator==(hash_const_iterator const& x) const { return base_ == x.base_; }
|
|
|
|
|
bool operator!=(iterator const& x) const { return base_ != x.base_; }
|
|
|
|
|
bool operator!=(hash_const_iterator const& x) const { return base_ != x.base_; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class iterator_access
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
template <class Iterator>
|
2005-06-20 21:55:28 +00:00
|
|
|
|
static BOOST_DEDUCED_TYPENAME Iterator::base const& get(Iterator const& it) {
|
2005-04-24 14:23:24 +00:00
|
|
|
|
return it.base_;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class ValueType, class KeyType,
|
|
|
|
|
class Hash, class Pred,
|
|
|
|
|
class Alloc, bool EquivalentKeys>
|
|
|
|
|
class hash_types
|
|
|
|
|
{
|
|
|
|
|
public:
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
|
|
|
boost::unordered_detail::rebind_wrap<Alloc, ValueType>::type
|
|
|
|
|
value_allocator;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef hash_table<ValueType, KeyType, Hash, Pred, value_allocator,
|
|
|
|
|
EquivalentKeys> hash_table;
|
|
|
|
|
typedef hash_table_data<value_allocator> data;
|
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::iterator_base iterator_base;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
|
|
|
|
|
typedef hash_const_local_iterator<value_allocator> const_local_iterator;
|
|
|
|
|
typedef hash_local_iterator<value_allocator> local_iterator;
|
|
|
|
|
typedef hash_const_iterator<value_allocator> const_iterator;
|
|
|
|
|
typedef hash_iterator<value_allocator> iterator;
|
|
|
|
|
|
2005-06-20 21:55:28 +00:00
|
|
|
|
typedef BOOST_DEDUCED_TYPENAME data::size_type size_type;
|
2005-04-24 14:23:24 +00:00
|
|
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
|
};
|
|
|
|
|
} // namespace boost::unordered_detail
|
|
|
|
|
} // namespace boost
|
|
|
|
|
|
2006-02-26 18:33:49 +00:00
|
|
|
|
#undef BOOST_HASH_BORLAND_BOOL
|
|
|
|
|
|
2005-04-24 14:23:24 +00:00
|
|
|
|
#endif // BOOST_UNORDERED_DETAIL_HASH_TABLE_HPP_INCLUDED
|
|
|
|
|
|