forked from boostorg/unordered
Unordered: Overhaul the implementation.
Store nodes in a single linked list, with hash values so that their buckets can be found when needed. Iterators now only have to store a pointer to the node and don't have to iterate over empty buckets to reach the next node. This allows the container to meet the iterator requirements - fixing the speed issues with `equal_range` and `erase`. Also, define iterators in their own namespace, so that they don't accidentally pull in detail functions via ADL. I've simplified the code slightly by removing some of the special cases for empty containers. Renamed a few things as well and other minor changes that were made as I went along. [SVN r71327]
This commit is contained in:
1 parent
d9c49a6cde
commit
547e141166
14 files changed
+4330
-3985
No files matched your search
@@ -7,13 +7,12 @@
|
||||
#ifndef BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
|
||||
#define BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
|
||||
|
||||
#include <boost/unordered/detail/table.hpp>
|
||||
#include <boost/unordered/detail/extract_key.hpp>
|
||||
|
||||
namespace boost { namespace unordered_detail {
|
||||
namespace boost { namespace unordered { namespace detail {
|
||||
|
||||
template <class T>
|
||||
class hash_equivalent_table : public T::table
|
||||
class equivalent_table : public T::table
|
||||
{
|
||||
public:
|
||||
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
|
||||
@@ -27,48 +26,154 @@ namespace boost { namespace unordered_detail {
|
||||
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;
|
||||
|
||||
// Constructors
|
||||
|
||||
hash_equivalent_table(std::size_t n,
|
||||
equivalent_table(std::size_t n,
|
||||
hasher const& hf, key_equal const& eq, value_allocator const& a)
|
||||
: table(n, hf, eq, a) {}
|
||||
hash_equivalent_table(hash_equivalent_table const& x)
|
||||
equivalent_table(equivalent_table const& x)
|
||||
: table(x, x.node_alloc()) {}
|
||||
hash_equivalent_table(hash_equivalent_table const& x,
|
||||
equivalent_table(equivalent_table const& x,
|
||||
value_allocator const& a)
|
||||
: table(x, a) {}
|
||||
hash_equivalent_table(hash_equivalent_table& x, move_tag m)
|
||||
equivalent_table(equivalent_table& x, move_tag m)
|
||||
: table(x, m) {}
|
||||
hash_equivalent_table(hash_equivalent_table& x,
|
||||
equivalent_table(equivalent_table& x,
|
||||
value_allocator const& a, move_tag m)
|
||||
: table(x, a, m) {}
|
||||
~hash_equivalent_table() {}
|
||||
~equivalent_table() {}
|
||||
|
||||
// Equality
|
||||
|
||||
bool equals(equivalent_table const& other) const
|
||||
{
|
||||
if(this->size_ != other.size_) return false;
|
||||
if(!this->size_) return true;
|
||||
|
||||
for(node_ptr n1 = this->buckets_[this->bucket_count_].next_; n1;)
|
||||
{
|
||||
node_ptr n2 = other.find_matching_node(n1);
|
||||
if(!n2) return false;
|
||||
|
||||
node_ptr end1 = node::next_group(n1);
|
||||
node_ptr end2 = node::next_group(n2);
|
||||
|
||||
do {
|
||||
if(!extractor::compare_mapped(
|
||||
node::get_value(n1), node::get_value(n2)))
|
||||
return false;
|
||||
n1 = n1->next_;
|
||||
n2 = n2->next_;
|
||||
} while(n1 != end1 && n2 != end2);
|
||||
if(n1 != end1 || n2 != end2) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// A convenience method for adding nodes.
|
||||
|
||||
inline node_ptr add_node(
|
||||
node_constructor& a,
|
||||
std::size_t bucket_index,
|
||||
std::size_t hash,
|
||||
node_ptr pos)
|
||||
{
|
||||
node_ptr n = a.release();
|
||||
node::set_hash(n, hash);
|
||||
|
||||
if(BOOST_UNORDERED_BORLAND_BOOL(pos)) {
|
||||
node::add_after_node(n, pos);
|
||||
if (n->next_) {
|
||||
std::size_t next_bucket =
|
||||
node::get_hash(n->next_) % this->bucket_count_;
|
||||
if (next_bucket != bucket_index) {
|
||||
this->buckets_[next_bucket].next_ = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
bucket_ptr b = this->get_bucket(bucket_index);
|
||||
|
||||
if (!b->next_)
|
||||
{
|
||||
bucket_ptr start_node =
|
||||
this->get_bucket(this->bucket_count_);
|
||||
|
||||
if (BOOST_UNORDERED_BORLAND_BOOL(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
|
||||
|
||||
iterator_base emplace_impl(node_constructor& a);
|
||||
void emplace_impl_no_rehash(node_constructor& a);
|
||||
node_ptr emplace_impl(node_constructor& a)
|
||||
{
|
||||
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 position = this->find_node(bucket_index, hash, k);
|
||||
|
||||
// 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_;
|
||||
}
|
||||
|
||||
return add_node(a, bucket_index, hash, position);
|
||||
}
|
||||
|
||||
// equals
|
||||
|
||||
bool equals(hash_equivalent_table const&) const;
|
||||
|
||||
inline node_ptr add_node(node_constructor& a,
|
||||
bucket_ptr bucket, node_ptr pos);
|
||||
void emplace_impl_no_rehash(node_constructor& a)
|
||||
{
|
||||
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_;
|
||||
add_node(a, bucket_index, hash,
|
||||
this->find_node(bucket_index, hash, k));
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_STD_FORWARD)
|
||||
|
||||
template <class... Args>
|
||||
iterator_base emplace(Args&&... args);
|
||||
node_ptr emplace(Args&&... args)
|
||||
{
|
||||
// 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)...);
|
||||
|
||||
return emplace_impl(a);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \
|
||||
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, n)> \
|
||||
iterator_base emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, n));
|
||||
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
|
||||
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
|
||||
node_ptr emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
|
||||
{ \
|
||||
node_constructor a(*this); \
|
||||
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
|
||||
return emplace_impl(a); \
|
||||
}
|
||||
|
||||
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
|
||||
BOOST_UNORDERED_INSERT_IMPL, _)
|
||||
@@ -76,12 +181,51 @@ namespace boost { namespace unordered_detail {
|
||||
#undef BOOST_UNORDERED_INSERT_IMPL
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Insert range methods
|
||||
|
||||
// if hash function throws, or inserting > 1 element, basic exception
|
||||
// safety. Strong otherwise
|
||||
template <class I>
|
||||
void insert_for_range(I i, I j, forward_traversal_tag);
|
||||
void insert_for_range(I i, I j, forward_traversal_tag)
|
||||
{
|
||||
if(i == j) return;
|
||||
std::size_t distance = ::boost::unordered::detail::distance(i, j);
|
||||
if(distance == 1) {
|
||||
emplace(*i);
|
||||
}
|
||||
else {
|
||||
// Only require basic exception safety here
|
||||
this->reserve_for_insert(this->size_ + distance);
|
||||
|
||||
node_constructor a(*this);
|
||||
for (; i != j; ++i) {
|
||||
a.construct(*i);
|
||||
emplace_impl_no_rehash(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class I>
|
||||
void insert_for_range(I i, I j, boost::incrementable_traversal_tag);
|
||||
void insert_for_range(I i, I j, ::boost::incrementable_traversal_tag)
|
||||
{
|
||||
node_constructor a(*this);
|
||||
for (; i != j; ++i) {
|
||||
a.construct(*i);
|
||||
emplace_impl(a);
|
||||
}
|
||||
}
|
||||
|
||||
// If hash function throws, or inserting > 1 element, basic exception
|
||||
// safety. Strong otherwise
|
||||
template <class I>
|
||||
void insert_range(I i, I j);
|
||||
void insert_range(I i, I j)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME ::boost::iterator_traversal<I>::type
|
||||
iterator_traversal_tag;
|
||||
insert_for_range(i, j, iterator_traversal_tag);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <class H, class P, class A>
|
||||
@@ -90,10 +234,10 @@ namespace boost { namespace unordered_detail {
|
||||
BOOST_DEDUCED_TYPENAME A::value_type,
|
||||
H, P, A,
|
||||
set_extractor<BOOST_DEDUCED_TYPENAME A::value_type>,
|
||||
grouped>
|
||||
false>
|
||||
{
|
||||
typedef hash_equivalent_table<multiset<H, P, A> > impl;
|
||||
typedef hash_table<multiset<H, P, A> > table;
|
||||
typedef equivalent_table<multiset<H, P, A> > impl;
|
||||
typedef table<multiset<H, P, A> > table;
|
||||
};
|
||||
|
||||
template <class K, class H, class P, class A>
|
||||
@@ -101,204 +245,11 @@ namespace boost { namespace unordered_detail {
|
||||
K, BOOST_DEDUCED_TYPENAME A::value_type,
|
||||
H, P, A,
|
||||
map_extractor<K, BOOST_DEDUCED_TYPENAME A::value_type>,
|
||||
grouped>
|
||||
false>
|
||||
{
|
||||
typedef hash_equivalent_table<multimap<K, H, P, A> > impl;
|
||||
typedef hash_table<multimap<K, H, P, A> > table;
|
||||
typedef equivalent_table<multimap<K, H, P, A> > impl;
|
||||
typedef table<multimap<K, H, P, A> > table;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Equality
|
||||
|
||||
template <class T>
|
||||
bool hash_equivalent_table<T>
|
||||
::equals(hash_equivalent_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;
|
||||
|
||||
node_ptr end1 = node::next_group(it1);
|
||||
node_ptr end2 = node::next_group(it2);
|
||||
|
||||
do {
|
||||
if(!extractor::compare_mapped(
|
||||
node::get_value(it1), node::get_value(it2)))
|
||||
return false;
|
||||
it1 = it1->next_;
|
||||
it2 = it2->next_;
|
||||
} while(it1 != end1 && it2 != end2);
|
||||
if(it1 != end1 || it2 != end2) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// A convenience method for adding nodes.
|
||||
|
||||
template <class T>
|
||||
inline BOOST_DEDUCED_TYPENAME hash_equivalent_table<T>::node_ptr
|
||||
hash_equivalent_table<T>
|
||||
::add_node(node_constructor& a, bucket_ptr bucket, node_ptr pos)
|
||||
{
|
||||
node_ptr n = a.release();
|
||||
if(BOOST_UNORDERED_BORLAND_BOOL(pos)) {
|
||||
node::add_after_node(n, pos);
|
||||
}
|
||||
else {
|
||||
node::add_to_bucket(n, *bucket);
|
||||
if(bucket < this->cached_begin_bucket_)
|
||||
this->cached_begin_bucket_ = bucket;
|
||||
}
|
||||
++this->size_;
|
||||
return n;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Insert methods
|
||||
|
||||
template <class T>
|
||||
inline BOOST_DEDUCED_TYPENAME
|
||||
hash_equivalent_table<T>::iterator_base
|
||||
hash_equivalent_table<T>::emplace_impl(node_constructor& a)
|
||||
{
|
||||
key_type const& k = this->get_key(a.value());
|
||||
std::size_t hash_value = this->hash_function()(k);
|
||||
|
||||
if(!this->size_) {
|
||||
return this->emplace_empty_impl_with_node(a, 1);
|
||||
}
|
||||
else {
|
||||
bucket_ptr bucket = this->bucket_ptr_from_hash(hash_value);
|
||||
node_ptr position = this->find_iterator(bucket, k);
|
||||
|
||||
// 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);
|
||||
|
||||
return iterator_base(bucket, add_node(a, bucket, position));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void hash_equivalent_table<T>
|
||||
::emplace_impl_no_rehash(node_constructor& a)
|
||||
{
|
||||
key_type const& k = this->get_key(a.value());
|
||||
bucket_ptr bucket = this->get_bucket(this->bucket_index(k));
|
||||
add_node(a, bucket, this->find_iterator(bucket, k));
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_STD_FORWARD)
|
||||
|
||||
// Emplace (equivalent key containers)
|
||||
// (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_equivalent_table<T>::iterator_base
|
||||
hash_equivalent_table<T>
|
||||
::emplace(Args&&... args)
|
||||
{
|
||||
// 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)...);
|
||||
|
||||
return emplace_impl(a);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
|
||||
template <class T> \
|
||||
template <BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params)> \
|
||||
BOOST_DEDUCED_TYPENAME hash_equivalent_table<T>::iterator_base \
|
||||
hash_equivalent_table<T> \
|
||||
::emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
|
||||
{ \
|
||||
node_constructor a(*this); \
|
||||
a.construct(BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
|
||||
return emplace_impl(a); \
|
||||
}
|
||||
|
||||
BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
|
||||
BOOST_UNORDERED_INSERT_IMPL, _)
|
||||
|
||||
#undef BOOST_UNORDERED_INSERT_IMPL
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Insert range methods
|
||||
|
||||
// if hash function throws, or inserting > 1 element, basic exception safety
|
||||
// strong otherwise
|
||||
template <class T>
|
||||
template <class I>
|
||||
inline void hash_equivalent_table<T>
|
||||
::insert_for_range(I i, I j, forward_traversal_tag)
|
||||
{
|
||||
if(i == j) return;
|
||||
std::size_t distance = unordered_detail::distance(i, j);
|
||||
if(distance == 1) {
|
||||
emplace(*i);
|
||||
}
|
||||
else {
|
||||
node_constructor a(*this);
|
||||
|
||||
// Only require basic exception safety here
|
||||
if(this->size_) {
|
||||
this->reserve_for_insert(this->size_ + distance);
|
||||
}
|
||||
else {
|
||||
a.construct(*i++);
|
||||
this->emplace_empty_impl_with_node(a, distance);
|
||||
}
|
||||
|
||||
for (; i != j; ++i) {
|
||||
a.construct(*i);
|
||||
emplace_impl_no_rehash(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if hash function throws, or inserting > 1 element, basic exception safety
|
||||
// strong otherwise
|
||||
template <class T>
|
||||
template <class I>
|
||||
inline void hash_equivalent_table<T>
|
||||
::insert_for_range(I i, I j, boost::incrementable_traversal_tag)
|
||||
{
|
||||
node_constructor a(*this);
|
||||
for (; i != j; ++i) {
|
||||
a.construct(*i);
|
||||
emplace_impl(a);
|
||||
}
|
||||
}
|
||||
|
||||
// if hash function throws, or inserting > 1 element, basic exception safety
|
||||
// strong otherwise
|
||||
template <class T>
|
||||
template <class I>
|
||||
void hash_equivalent_table<T>::insert_range(I i, I j)
|
||||
{
|
||||
BOOST_DEDUCED_TYPENAME boost::iterator_traversal<I>::type
|
||||
iterator_traversal_tag;
|
||||
insert_for_range(i, j, iterator_traversal_tag);
|
||||
}
|
||||
}}
|
||||
}}}
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user