forked from boostorg/unordered
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]
367 lines
11 KiB
C++
367 lines
11 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)
|
|
|
|
// This contains the basic data structure, apart from the actual values. There's
|
|
// no construction or deconstruction here. So this only depends on the pointer
|
|
// type.
|
|
|
|
#ifndef BOOST_UNORDERED_DETAIL_NODE_HPP_INCLUDED
|
|
#define BOOST_UNORDERED_DETAIL_NODE_HPP_INCLUDED
|
|
|
|
#include <boost/unordered/detail/util.hpp>
|
|
|
|
#if BOOST_WORKAROUND(__BORLANDC__, <= 0X0582)
|
|
#define BOOST_UNORDERED_BORLAND_BOOL(x) (bool)(x)
|
|
#else
|
|
#define BOOST_UNORDERED_BORLAND_BOOL(x) x
|
|
#endif
|
|
|
|
namespace boost { namespace unordered { namespace detail {
|
|
|
|
// Some forward declarations for buckets and tables
|
|
|
|
template <typename T> class table;
|
|
template <class A, bool Unique> class buckets;
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This section implements buckets and nodes. Here's a rough
|
|
// inheritance diagram, to show how they pull together.
|
|
//
|
|
// For unordered_set/unordered_map:
|
|
//
|
|
// bucket<A> value_base<allocator_traits<A>::value_type>
|
|
// | |
|
|
// +--------------+-------------+
|
|
// |
|
|
// ungrouped_node<A>
|
|
//
|
|
// For unordered_multiset/unordered_multimap:
|
|
//
|
|
// bucket<A> value_base<allocator_traits<A>::value_type>
|
|
// | |
|
|
// +--------------+-------------+
|
|
// |
|
|
// grouped_node<A>
|
|
|
|
// bucket
|
|
//
|
|
// bucket is used for both the buckets and as a base class for
|
|
// nodes. By using 'bucket_ptr' for 'node_ptr', 'next_' can point
|
|
// to either a bucket or a node. This is used later to implement a
|
|
// sentinel at the end of the bucket array.
|
|
|
|
template <class A>
|
|
class bucket
|
|
{
|
|
bucket& operator=(bucket const&);
|
|
public:
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
::boost::unordered::detail::rebind_wrap<A, bucket>::type
|
|
bucket_allocator;
|
|
typedef BOOST_DEDUCED_TYPENAME
|
|
allocator_traits<bucket_allocator>::pointer bucket_ptr;
|
|
typedef bucket_ptr node_ptr;
|
|
|
|
node_ptr next_;
|
|
|
|
bucket() : next_() {}
|
|
};
|
|
|
|
// The space used to store values in a node.
|
|
|
|
template <class ValueType>
|
|
struct value_base
|
|
{
|
|
typedef ValueType value_type;
|
|
BOOST_DEDUCED_TYPENAME ::boost::aligned_storage<
|
|
sizeof(value_type),
|
|
::boost::alignment_of<value_type>::value>::type data_;
|
|
|
|
void* address() {
|
|
return this;
|
|
}
|
|
value_type& value() {
|
|
return *(ValueType*) this;
|
|
}
|
|
value_type* value_ptr() {
|
|
return (ValueType*) this;
|
|
}
|
|
private:
|
|
value_base& operator=(value_base const&);
|
|
};
|
|
|
|
// In containers with equivalent keys (unordered_multimap and
|
|
// unordered_multiset) equivalent nodes are grouped together, in
|
|
// containers with unique keys (unordered_map and unordered_set)
|
|
// individual nodes are treated as groups of one. The following two
|
|
// classes implement the data structure.
|
|
|
|
// This is used for containers with unique keys. There are no groups
|
|
// so it doesn't add any extra members, and just treats individual
|
|
// nodes as groups of one.
|
|
|
|
template <class A>
|
|
struct ungrouped_node
|
|
: ::boost::unordered::detail::bucket<A>,
|
|
value_base<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>
|
|
{
|
|
typedef ::boost::unordered::detail::bucket<A> bucket;
|
|
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
|
|
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type value_type;
|
|
|
|
std::size_t hash_;
|
|
|
|
ungrouped_node() : bucket() {}
|
|
|
|
void init(node_ptr) {}
|
|
|
|
static node_ptr next_group(node_ptr ptr)
|
|
{
|
|
return ptr->next_;
|
|
}
|
|
|
|
static node_ptr next_group2(node_ptr ptr)
|
|
{
|
|
return ptr->next_;
|
|
}
|
|
|
|
static std::size_t group_count(node_ptr n)
|
|
{
|
|
return !n ? 0 : 1;
|
|
}
|
|
|
|
static void add_after_node(node_ptr n, node_ptr position)
|
|
{
|
|
n->next_ = position->next_;
|
|
position->next_ = position;
|
|
}
|
|
|
|
static node_ptr unlink_node(bucket& b, node_ptr n)
|
|
{
|
|
return unlink_nodes(b, n, n->next_);
|
|
}
|
|
|
|
static node_ptr unlink_nodes(bucket& b, node_ptr begin, node_ptr end)
|
|
{
|
|
node_ptr prev = b.next_;
|
|
while(prev->next_ != begin) prev = prev->next_;
|
|
prev->next_ = end;
|
|
return prev;
|
|
}
|
|
|
|
static std::size_t get_hash(node_ptr p)
|
|
{
|
|
return static_cast<ungrouped_node&>(*p).hash_;
|
|
}
|
|
|
|
static void set_hash(node_ptr p, std::size_t hash)
|
|
{
|
|
static_cast<ungrouped_node&>(*p).hash_ = hash;
|
|
}
|
|
|
|
static value_type& get_value(node_ptr p)
|
|
{
|
|
return static_cast<ungrouped_node&>(*p).value();
|
|
}
|
|
|
|
static value_type* get_value_ptr(node_ptr p)
|
|
{
|
|
return static_cast<ungrouped_node&>(*p).value_ptr();
|
|
}
|
|
};
|
|
|
|
// This is used for containers with equivalent keys. It implements a
|
|
// circular list running in the opposite direction to the linked
|
|
// list through the nodes.
|
|
|
|
template <class A>
|
|
struct grouped_node
|
|
: ::boost::unordered::detail::bucket<A>,
|
|
value_base<BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type>
|
|
{
|
|
typedef ::boost::unordered::detail::bucket<A> bucket;
|
|
typedef BOOST_DEDUCED_TYPENAME bucket::bucket_ptr bucket_ptr;
|
|
typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
|
|
typedef BOOST_DEDUCED_TYPENAME allocator_traits<A>::value_type value_type;
|
|
|
|
std::size_t hash_;
|
|
node_ptr group_prev_;
|
|
|
|
grouped_node() : bucket(), group_prev_() {}
|
|
void init(node_ptr n)
|
|
{
|
|
group_prev_ = n;
|
|
}
|
|
|
|
static node_ptr next_group(node_ptr ptr)
|
|
{
|
|
return get(ptr).group_prev_->next_;
|
|
}
|
|
|
|
static node_ptr next_group2(node_ptr ptr)
|
|
{
|
|
return get(ptr->next_).group_prev_;
|
|
}
|
|
|
|
static std::size_t group_count(node_ptr ptr)
|
|
{
|
|
if (!ptr) return 0;
|
|
|
|
node_ptr start = ptr;
|
|
std::size_t size = 0;
|
|
do {
|
|
++size;
|
|
ptr = get(ptr).group_prev_;
|
|
} while(ptr != start);
|
|
return size;
|
|
}
|
|
|
|
static void add_after_node(node_ptr n, node_ptr pos)
|
|
{
|
|
n->next_ = get(pos).group_prev_->next_;
|
|
get(n).group_prev_ = get(pos).group_prev_;
|
|
get(pos).group_prev_->next_ = n;
|
|
get(pos).group_prev_ = n;
|
|
}
|
|
|
|
static node_ptr unlink_node(bucket& b, node_ptr n)
|
|
{
|
|
node_ptr next = n->next_;
|
|
node_ptr prev = get(n).group_prev_;
|
|
|
|
if(prev->next_ != n) {
|
|
// The node is at the beginning of a group.
|
|
|
|
// Find the previous node pointer:
|
|
prev = b.next_;
|
|
while(prev->next_ != n) {
|
|
prev = next_group2(prev);
|
|
}
|
|
|
|
// Remove from group
|
|
if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
|
|
get(next).group_prev_ == n)
|
|
{
|
|
get(next).group_prev_ = get(n).group_prev_;
|
|
}
|
|
}
|
|
else if(BOOST_UNORDERED_BORLAND_BOOL(next) &&
|
|
get(next).group_prev_ == n)
|
|
{
|
|
// The deleted node is not at the end of the group, so
|
|
// change the link from the next node.
|
|
get(next).group_prev_ = get(n).group_prev_;
|
|
}
|
|
else {
|
|
// The deleted node is at the end of the group, so the
|
|
// first node in the group is pointing to it.
|
|
// Find that to change its pointer.
|
|
node_ptr x = get(n).group_prev_;
|
|
while(get(x).group_prev_ != n) {
|
|
x = get(x).group_prev_;
|
|
}
|
|
get(x).group_prev_ = get(n).group_prev_;
|
|
}
|
|
prev->next_ = next;
|
|
|
|
return prev;
|
|
}
|
|
|
|
static node_ptr unlink_nodes(bucket& b, node_ptr begin, node_ptr end)
|
|
{
|
|
node_ptr prev = get(begin).group_prev_;
|
|
|
|
if(prev->next_ != begin) {
|
|
// The node is at the beginning of a group.
|
|
|
|
// Find the previous node pointer:
|
|
prev = b.next_;
|
|
while(prev->next_ != begin) prev = next_group2(prev);
|
|
|
|
if(BOOST_UNORDERED_BORLAND_BOOL(end)) split_group(end);
|
|
}
|
|
else {
|
|
node_ptr group1 = split_group(begin);
|
|
if(BOOST_UNORDERED_BORLAND_BOOL(end)) {
|
|
node_ptr group2 = split_group(end);
|
|
|
|
if(begin == group2) {
|
|
node_ptr end1 = get(group1).group_prev_;
|
|
node_ptr end2 = get(group2).group_prev_;
|
|
get(group1).group_prev_ = end2;
|
|
get(group2).group_prev_ = end1;
|
|
}
|
|
}
|
|
}
|
|
|
|
prev->next_ = end;
|
|
|
|
return prev;
|
|
}
|
|
|
|
// Break a ciruclar list into two, with split as the beginning
|
|
// of the second group (if split is at the beginning then don't
|
|
// split).
|
|
static node_ptr split_group(node_ptr split)
|
|
{
|
|
// Find first node in group.
|
|
node_ptr first = split;
|
|
while(next_group(first) == first)
|
|
first = get(first).group_prev_;
|
|
|
|
if(first == split) return split;
|
|
|
|
node_ptr last = get(first).group_prev_;
|
|
get(first).group_prev_ = get(split).group_prev_;
|
|
get(split).group_prev_ = last;
|
|
|
|
return first;
|
|
}
|
|
|
|
static std::size_t get_hash(node_ptr p) {
|
|
return static_cast<grouped_node&>(*p).hash_;
|
|
}
|
|
static void set_hash(node_ptr p, std::size_t hash) {
|
|
static_cast<grouped_node&>(*p).hash_ = hash;
|
|
}
|
|
static value_type& get_value(node_ptr p) {
|
|
return static_cast<grouped_node&>(*p).value();
|
|
}
|
|
static value_type* get_value_ptr(node_ptr p) {
|
|
return static_cast<grouped_node&>(*p).value_ptr();
|
|
}
|
|
|
|
static grouped_node& get(node_ptr ptr) {
|
|
return static_cast<grouped_node&>(*ptr);
|
|
}
|
|
};
|
|
|
|
// These two classes implement an easy way to pass around the node
|
|
// group policy classes without the messy template parameters.
|
|
// Whenever you see the template parameter 'G' it's one of these.
|
|
|
|
struct ungrouped
|
|
{
|
|
template <class A>
|
|
struct node {
|
|
typedef ungrouped_node<A> type;
|
|
};
|
|
};
|
|
|
|
struct grouped
|
|
{
|
|
template <class A>
|
|
struct node {
|
|
typedef grouped_node<A> type;
|
|
};
|
|
};
|
|
|
|
}}}
|
|
|
|
#endif
|