blob: 2283d8c25289053fec01422a5f3c8c6a3cbed2ba [file] [log] [blame]
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2010 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/table.hpp>
#include <boost/unordered/detail/extract_key.hpp>
namespace boost { namespace unordered_detail {
template <class T>
class hash_unique_table : public T::table
{
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 table;
typedef BOOST_DEDUCED_TYPENAME T::node_constructor node_constructor;
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::iterator_base iterator_base;
typedef BOOST_DEDUCED_TYPENAME T::extractor extractor;
typedef std::pair<iterator_base, bool> emplace_return;
// Constructors
hash_unique_table(std::size_t n, hasher const& hf, key_equal const& eq,
value_allocator const& a)
: table(n, hf, eq, a) {}
hash_unique_table(hash_unique_table const& x)
: table(x, x.node_alloc()) {}
hash_unique_table(hash_unique_table const& x, value_allocator const& a)
: table(x, a) {}
hash_unique_table(hash_unique_table& x, move_tag m)
: table(x, m) {}
hash_unique_table(hash_unique_table& x, value_allocator const& a,
move_tag m)
: table(x, a, m) {}
~hash_unique_table() {}
// Insert methods
emplace_return emplace_impl_with_node(node_constructor& a);
value_type& operator[](key_type const& k);
// equals
bool equals(hash_unique_table const&) const;
node_ptr add_node(node_constructor& a, bucket_ptr bucket);
#if defined(BOOST_UNORDERED_STD_FORWARD)
template<class... Args>
emplace_return emplace(Args&&... args);
template<class... Args>
emplace_return emplace_impl(key_type const& k, Args&&... args);
template<class... Args>
emplace_return emplace_impl(no_key, Args&&... args);
template<class... Args>
emplace_return emplace_empty_impl(Args&&... args);
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n)); \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
emplace_return emplace_empty_impl( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, n));
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
// if hash function throws, or inserting > 1 element, basic exception
// safety strong otherwise
template <class InputIt>
void insert_range(InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl(key_type const&, InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl2(node_constructor&, key_type const&, InputIt i, InputIt j);
template <class InputIt>
void insert_range_impl(no_key, InputIt i, InputIt j);
};
template <class H, class P, class A>
struct set : public types<
BOOST_DEDUCED_TYPENAME A::value_type,
BOOST_DEDUCED_TYPENAME A::value_type,
H, P, A,
set_extractor<BOOST_DEDUCED_TYPENAME A::value_type>,
ungrouped>
{
typedef hash_unique_table<set<H, P, A> > impl;
typedef hash_table<set<H, P, A> > table;
};
template <class K, class H, class P, class A>
struct map : public types<
K, BOOST_DEDUCED_TYPENAME A::value_type,
H, P, A,
map_extractor<K, BOOST_DEDUCED_TYPENAME A::value_type>,
ungrouped>
{
typedef hash_unique_table<map<K, H, P, A> > impl;
typedef hash_table<map<K, H, P, A> > table;
};
////////////////////////////////////////////////////////////////////////////
// Equality
template <class T>
bool hash_unique_table<T>
::equals(hash_unique_table<T> const& other) const
{
if(this->size_ != other.size_) return false;
if(!this->size_) return true;
bucket_ptr end = this->get_bucket(this->bucket_count_);
for(bucket_ptr i = this->cached_begin_bucket_; i != end; ++i)
{
node_ptr it1 = i->next_;
while(BOOST_UNORDERED_BORLAND_BOOL(it1))
{
node_ptr it2 = other.find_iterator(this->get_key_from_ptr(it1));
if(!BOOST_UNORDERED_BORLAND_BOOL(it2)) return false;
if(!extractor::compare_mapped(
node::get_value(it1), node::get_value(it2)))
return false;
it1 = it1->next_;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////
// A convenience method for adding nodes.
template <class T>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::node_ptr
hash_unique_table<T>::add_node(node_constructor& a,
bucket_ptr bucket)
{
node_ptr n = a.release();
node::add_to_bucket(n, *bucket);
++this->size_;
if(bucket < this->cached_begin_bucket_)
this->cached_begin_bucket_ = bucket;
return n;
}
////////////////////////////////////////////////////////////////////////////
// Insert methods
// if hash function throws, basic exception safety
// strong otherwise
template <class T>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::value_type&
hash_unique_table<T>::operator[](key_type const& k)
{
typedef BOOST_DEDUCED_TYPENAME value_type::second_type mapped_type;
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
if(!this->buckets_) {
node_constructor a(*this);
a.construct_pair(k, (mapped_type*) 0);
return *this->emplace_empty_impl_with_node(a, 1);
}
node_ptr pos = this->find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
return node::get_value(pos);
}
else {
// 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_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 = this->bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
return node::get_value(add_node(a, bucket));
}
}
template <class T>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::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_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(iterator_base(bucket, pos), false);
} else {
// reserve has basic exception safety if the hash function
// throws, strong otherwise.
if(this->reserve_for_insert(this->size_ + 1))
bucket = this->bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
return emplace_return(
iterator_base(bucket, add_node(a, bucket)),
true);
}
}
#if defined(BOOST_UNORDERED_STD_FORWARD)
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_impl(key_type const& k,
Args&&... args)
{
// No side effects in this initial code
std::size_t hash_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, k);
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) {
// Found an existing key, return it (no throw).
return emplace_return(iterator_base(bucket, pos), false);
} else {
// 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 = this->bucket_ptr_from_hash(hash_value);
// Nothing after this point can throw.
return emplace_return(
iterator_base(bucket, add_node(a, bucket)),
true);
}
}
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::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);
}
template <class T>
template<class... Args>
inline BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace_empty_impl(Args&&... args)
{
node_constructor a(*this);
a.construct(std::forward<Args>(args)...);
return emplace_return(this->emplace_empty_impl_with_node(a, 1), true);
}
#else
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>::emplace_impl( \
key_type const& k, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
std::size_t hash_value = this->hash_function()(k); \
bucket_ptr bucket \
= this->bucket_ptr_from_hash(hash_value); \
node_ptr pos = this->find_iterator(bucket, k); \
\
if (BOOST_UNORDERED_BORLAND_BOOL(pos)) { \
return emplace_return(iterator_base(bucket, pos), false); \
} else { \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
\
if(this->reserve_for_insert(this->size_ + 1)) \
bucket = this->bucket_ptr_from_hash(hash_value); \
\
return emplace_return(iterator_base(bucket, \
add_node(a, bucket)), true); \
} \
} \
\
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>:: \
emplace_impl(no_key, \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_impl_with_node(a); \
} \
\
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
inline BOOST_DEDUCED_TYPENAME \
hash_unique_table<T>::emplace_return \
hash_unique_table<T>:: \
emplace_empty_impl( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
node_constructor a(*this); \
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
return emplace_return(this->emplace_empty_impl_with_node(a, 1), true); \
}
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
#if defined(BOOST_UNORDERED_STD_FORWARD)
// Emplace (unique keys)
// (I'm using an overloaded emplace for both 'insert' and 'emplace')
// if hash function throws, basic exception safety
// strong otherwise
template <class T>
template<class... Args>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace(Args&&... args)
{
return this->size_ ?
emplace_impl(
extractor::extract(std::forward<Args>(args)...),
std::forward<Args>(args)...) :
emplace_empty_impl(std::forward<Args>(args)...);
}
#else
template <class T>
template <class Arg0>
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return
hash_unique_table<T>::emplace(Arg0 const& arg0)
{
return this->size_ ?
emplace_impl(extractor::extract(arg0), arg0) :
emplace_empty_impl(arg0);
}
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
template <class T> \
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
BOOST_DEDUCED_TYPENAME hash_unique_table<T>::emplace_return \
hash_unique_table<T>::emplace( \
BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
{ \
return this->size_ ? \
emplace_impl(extractor::extract(arg0, arg1), \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)) : \
emplace_empty_impl( \
BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
}
BOOST_PP_REPEAT_FROM_TO(2, BOOST_UNORDERED_EMPLACE_LIMIT,
BOOST_UNORDERED_INSERT_IMPL, _)
#undef BOOST_UNORDERED_INSERT_IMPL
#endif
////////////////////////////////////////////////////////////////////////////
// Insert range methods
template <class T>
template <class InputIt>
inline void hash_unique_table<T>::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_value = this->hash_function()(k);
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
node_ptr pos = this->find_iterator(bucket, 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 = this->bucket_ptr_from_hash(hash_value);
}
// Nothing after this point can throw.
add_node(a, bucket);
}
}
template <class T>
template <class InputIt>
inline void hash_unique_table<T>::insert_range_impl(
key_type const&, InputIt i, InputIt j)
{
node_constructor a(*this);
if(!this->size_) {
a.construct(*i);
this->emplace_empty_impl_with_node(a, 1);
++i;
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 T>
template <class InputIt>
inline void hash_unique_table<T>::insert_range_impl(
no_key, InputIt i, InputIt j)
{
node_constructor a(*this);
if(!this->size_) {
a.construct(*i);
this->emplace_empty_impl_with_node(a, 1);
++i;
if(i == j) return;
}
do {
// No side effects in this initial code
a.construct(*i);
emplace_impl_with_node(a);
} while(++i != j);
}
// if hash function throws, or inserting > 1 element, basic exception safety
// strong otherwise
template <class T>
template <class InputIt>
void hash_unique_table<T>::insert_range(InputIt i, InputIt j)
{
if(i != j)
return insert_range_impl(extractor::extract(*i), i, j);
}
}}
#endif