Files
boost_unordered/include/boost/unordered/detail/unique.hpp
Daniel James 5f622027cd Unordered: Implement allocator propagation on assignment.
It's pretty messy because I'm trying to avoid swapping allocators in
these cases. I'm also not sure of the exception requirements of
allocator swap and assignment.

[SVN r73756]
2011-08-14 18:53:29 +00:00

416 lines
16 KiB
C++

// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 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_UNIQUE_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_UNIQUE_HPP_INCLUDED
#include <boost/unordered/detail/extract_key.hpp>
namespace boost { namespace unordered { namespace detail {
template <class T>
class unique_table : public T::table_base
{
public:
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
typedef BOOST_DEDUCED_TYPENAME T::key_equal key_equal;
typedef BOOST_DEDUCED_TYPENAME T::value_allocator value_allocator;
typedef BOOST_DEDUCED_TYPENAME T::key_type key_type;
typedef BOOST_DEDUCED_TYPENAME T::value_type value_type;
typedef BOOST_DEDUCED_TYPENAME T::table_base table_base;
typedef BOOST_DEDUCED_TYPENAME T::node_constructor node_constructor;
typedef BOOST_DEDUCED_TYPENAME T::node_allocator node_allocator;
typedef BOOST_DEDUCED_TYPENAME T::node node;
typedef BOOST_DEDUCED_TYPENAME T::node_ptr node_ptr;
typedef BOOST_DEDUCED_TYPENAME T::bucket_ptr bucket_ptr;
typedef BOOST_DEDUCED_TYPENAME T::extractor extractor;
typedef std::pair<node_ptr, bool> emplace_return;
// Constructors
unique_table(std::size_t n, hasher const& hf, key_equal const& eq,
value_allocator const& a)
: table_base(n, hf, eq, a) {}
unique_table(unique_table const& x)
: table_base(x,
allocator_traits<node_allocator>::
select_on_container_copy_construction(x.node_alloc())) {}
unique_table(unique_table const& x, value_allocator const& a)
: table_base(x, a) {}
unique_table(unique_table& x, move_tag m)
: table_base(x, m) {}
unique_table(unique_table& x, value_allocator const& a,
move_tag m)
: table_base(x, a, m) {}
~unique_table() {}
// equals
bool equals(unique_table const& other) const
{
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
for(node_ptr n1 = this->get_bucket(this->bucket_count_)->next_;
n1; n1 = n1->next_)
{
node_ptr n2 = other.find_matching_node(n1);
if(!n2 || node::get_value(n1) != node::get_value(n2))
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
node_ptr add_node(
node_constructor& a,
std::size_t bucket_index,
std::size_t hash)
{
bucket_ptr b = this->get_bucket(bucket_index);
node_ptr n = a.release();
node::set_hash(n, hash);
if (!b->next_)
{
bucket_ptr start_node = this->get_bucket(this->bucket_count_);
if (start_node->next_) {
this->buckets_[
node::get_hash(start_node->next_) % this->bucket_count_
].next_ = n;
}
b->next_ = start_node;
n->next_ = start_node->next_;
start_node->next_ = n;
}
else
{
n->next_ = b->next_->next_;
b->next_->next_ = n;
}
++this->size_;
return n;
}
////////////////////////////////////////////////////////////////////////////
// Insert methods
// if hash function throws, basic exception safety
// strong otherwise
value_type& operator[](key_type const& k)
{
typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
return node::get_value(pos);
}
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct_pair(k, (mapped_type*) 0);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return node::get_value(add_node(a, bucket_index, hash));
}
emplace_return emplace_impl_with_node(node_constructor& a)
{
// No side effects in this initial code
key_type const& k = this->get_key(a.value());
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return emplace_return(add_node(a, bucket_index, hash), true);
}
emplace_return insert(value_type const& v)
{
key_type const& k = extractor::extract(v);
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// Isn't in table, add to bucket.
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct(v);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return emplace_return(add_node(a, bucket_index, hash), true);
}
#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template<class... Args>
emplace_return emplace(Args&&... args)
{
return emplace_impl(
extractor::extract(std::forward<Args>(args)...),
std::forward<Args>(args)...);
}
template<class... Args>
emplace_return emplace_impl(key_type const& k, Args&&... args)
{
// No side effects in this initial code
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(pos, false);
}
// Doesn't already exist, add to bucket.
// Side effects only in this block.
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket_index = hash % this->bucket_count_;
// Nothing after this point can throw.
return emplace_return(add_node(a, bucket_index, hash), true);
}
template<class... Args>
emplace_return emplace_impl(no_key, Args&&... args)
{
// Construct the node regardless - in order to get the key.
// It will be discarded if it isn't used
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
return emplace_impl_with_node(a);
}
#else
template <class Arg0>
emplace_return emplace(Arg0 const& arg0)
{
return emplace_impl(extractor::extract(arg0), arg0);
}
#define BOOST_UNORDERED_INSERT1_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
return emplace_impl(extractor::extract(arg0, arg1), \
BOOST_UNORDERED_CALL_PARAMS(z, n)); \
}
#define BOOST_UNORDERED_INSERT2_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
std::size_t hash = this->hash_function()(k); \
std::size_t bucket_index = hash % this->bucket_count_; \
node_ptr pos = this->find_node(bucket_index, hash, k); \
\
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { \
return emplace_return(pos, false); \
} else { \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, n)); \
\
if(this->reserve_for_insert(this->size_ + 1)) \
bucket_index = hash % this->bucket_count_; \
\
return emplace_return( \
add_node(a, bucket_index, hash), \
true); \
} \
} \
\
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, n)); \
return emplace_impl_with_node(a); \
}
BOOST_PP_REPEAT_FROM_TO(2, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT1_IMPL, _)
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT2_IMPL, _)
#undef BOOST_UNORDERED_INSERT1_IMPL
#undef BOOST_UNORDERED_INSERT2_IMPL
#endif
////////////////////////////////////////////////////////////////////////
// Insert range methods
//
// if hash function throws, or inserting > 1 element, basic exception
// safety strong otherwise
template <class InputIt>
void insert_range(InputIt i, InputIt j)
{
if(i != j)
return insert_range_impl(extractor::extract(*i), i, j);
}
template <class InputIt>
void insert_range_impl(key_type const&, InputIt i, InputIt j)
{
node_constructor a(*this);
// Special case for empty buckets so that we can use
// max_load_ (which isn't valid when buckets_ is null).
if (!this->buckets_) {
insert_range_empty(a, extractor::extract(*i), i, j);
if (++i == j) return;
}
do {
// Note: can't use get_key as '*i' might not be value_type - it
// could be a pair with first_types as key_type without const or a
// different second_type.
//
// TODO: Might be worth storing the value_type instead of the key
// here. Could be more efficient if '*i' is expensive. Could be
// less efficient if copying the full value_type is expensive.
insert_range_impl2(a, extractor::extract(*i), i, j);
} while(++i != j);
}
template <class InputIt>
void insert_range_empty(node_constructor& a, key_type const& k,
InputIt i, InputIt j)
{
std::size_t hash = this->hash_function()(k);
a.construct(*i);
this->reserve_for_insert(this->size_ + insert_size(i, j));
add_node(a, hash % this->bucket_count_, hash);
}
template <class InputIt>
void insert_range_impl2(node_constructor& a, key_type const& k,
InputIt i, InputIt j)
{
// No side effects in this initial code
std::size_t hash = this->hash_function()(k);
std::size_t bucket_index = hash % this->bucket_count_;
node_ptr pos = this->find_node(bucket_index, hash, k);
if (!BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Doesn't already exist, add to bucket.
// Side effects only in this block.
// Create the node before rehashing in case it throws an
// exception (need strong safety in such a case).
a.construct(*i);
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->size_ + 1 >= this->max_load_) {
this->reserve_for_insert(this->size_ + insert_size(i, j));
bucket_index = hash % this->bucket_count_;
}
// Nothing after this point can throw.
add_node(a, bucket_index, hash);
}
}
template <class InputIt>
void insert_range_impl(no_key, InputIt i, InputIt j)
{
node_constructor a(*this);
do {
// No side effects in this initial code
a.construct(*i);
emplace_impl_with_node(a);
} while(++i != j);
}
};
template <class H, class P, class A>
struct set : public types<
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
set_extractor<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
true>
{
typedef ::boost::unordered::detail::unique_table<set<H, P, A> > impl;
typedef ::boost::unordered::detail::table<set<H, P, A> > table_base;
};
template <class K, class H, class P, class A>
struct map : public types<
K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type,
H, P, A,
map_extractor<K, BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>,
true>
{
typedef ::boost::unordered::detail::unique_table<map<K, H, P, A> > impl;
typedef ::boost::unordered::detail::table<map<K, H, P, A> > table_base;
};
}}}
#endif