diff --git a/doc/changes.qbk b/doc/changes.qbk
index 7fb950a6..58a16854 100644
--- a/doc/changes.qbk
+++ b/doc/changes.qbk
@@ -134,4 +134,21 @@ First official release.
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
iterators which returns `value_type` by copy.
+[h2 Boost 1.48.0]
+
+This is major change which has been converted to use Boost.Move's move
+emulation, and be more compliant with the C++11 standard. This has resulted
+in some breaking changes:
+
+* Equality comparison has been changed to the C++11 specification.
+ In a container with equivalent keys, elements in a group with equal
+ keys used to have to be in the same order to be considered equal,
+ now they can be a permutation of each other.
+
+* The behaviour of swap is different when the two containers to be
+ swapped has unequal allocators. It used to allocate new nodes using
+ the appropriate allocators, it now swaps the allocators if
+ the allocator has a member structure `propagate_on_container_swap`,
+ such that `propagate_on_container_swap::value` is true.
+
[endsect]
diff --git a/doc/compliance.qbk b/doc/compliance.qbk
new file mode 100644
index 00000000..759d9bde
--- /dev/null
+++ b/doc/compliance.qbk
@@ -0,0 +1,54 @@
+[/ Copyright 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) ]
+
+[section:compliance C++11 Compliance]
+
+/TODO/: Look into C++11's `std::pair`.
+
+[section:allocator_compliance Use of allocators]
+
+* Objects are not constructed using the allocator. The node containing them
+ is constructed using the allocator's `construct` function, but then the
+ object is constructed in a buffer in that node by calling the constructor
+ directly.
+* Similarly the object is destructed by calling its destructor directly, and
+ then the allocator's `destroy` method is used to destruct the node.
+* For most compilers `select_on_container_copy` is only detected for an
+ exact signature match in the allocator itself - not in a base. There is full
+ detection for g++ 4.4 or laster, Visual C++ 2008 or later, Clang and maybe
+ other compilers which support SFINAE for expressions.
+* `pointer_traits` aren't used. Instead, pointer types are obtained from
+ rebound allocators.
+* /TODO/: Any other defficiences of `allocator_traits` emulation.
+* Pointers of base types are used to store the location of a derived type.
+ (/TODO/: I'm not sure if that isn't compliant).
+
+[endsect]
+
+[section:move Move emulation]
+
+Move emulation is implemented using Boost.Move. If rvalue references are
+available it will use them, but if not it uses a close, but imperfect emulation
+and to get the advantage of using movable container elements, you'll need to
+use Boost.Move.
+
+* Non-copyable objects can be stored in the containers, but without support
+ for rvalue references the container will not be movable.
+* The number of arguments used in emplace is limited to /TODO/.
+* Argument forwarding is not perfect.
+* /TODO/: Constructor call for pairs.
+
+[endsect]
+
+[section:other Other]
+
+* When swapping, `Pred` and `Hash` are not currently swapped by calling
+ `swap`, their copy constructors are used.
+* As a consequence when swapping an exception may be throw from their
+ copy constructor.
+
+
+[endsect]
+
+[endsect]
diff --git a/doc/rationale.qbk b/doc/rationale.qbk
index ff371386..6662f13e 100644
--- a/doc/rationale.qbk
+++ b/doc/rationale.qbk
@@ -126,7 +126,7 @@ a little to accomodate non-C++0x compilers.
It isn't clear how to swap containers when their allocators aren't equal.
This is
-[@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#431
+[@http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#431
Issue 431: Swapping containers with unequal allocators]. This has been resolved
with the new allocator specification, so this should be fixed when
support is added.
diff --git a/doc/ref.php b/doc/ref.php
new file mode 100644
index 00000000..66f4d930
--- /dev/null
+++ b/doc/ref.php
@@ -0,0 +1,1040 @@
+
+
+
+
+
+EOL;
+
+ $key_type = 'Key';
+ $key_name = 'key';
+ $value_type = 'std::pair<Key const, Mapped>';
+ $full_type = $name.'<Key, Mapped, Hash, Pred, Alloc>';
+ }
+ else
+ {
+ $template_value = <<
+
+
+EOL;
+
+ $key_type = 'Value';
+ $key_name = 'value';
+ $value_type = 'Value';
+ $full_type = $name.'<Value, Hash, Pred, Alloc>';
+ }
+?>
+
+
+
+
+ boost::hash<>
+
+
+ std::equal_to<>
+
+
+ std::allocator<>
+
+
+
+ An unordered associative container that
+
+
+
+ Based on chapter 23 of
+ the working draft of the C++ standard [n2960].
+ But without the updated rules for allocators.
+
+ Template Parameters
+
+
+
+
+
+ Key
+ Key must be Assignable and CopyConstructible.
+
+ Mapped
+ Mapped must be CopyConstructible
+
+
+ Value
+ Value must be Assignable and CopyConstructible
+
+
+ Hash
+ A unary function object type that acts a hash function for a
. It takes a single argument of type
and returns a value of type std::size_t.
+
+ Pred
+ A binary function object that implements an equivalence relation on values of type
.
+ A binary function object that induces an equivalence relation on values of type
.
+ It takes two arguments of type
and returns a value of type bool.
+
+ Alloc
+ An allocator whose value type is the same as the container's value type.
+ The elements are organized into buckets.
+ The number of buckets can be automatically increased by a call to insert, or as the result of calling rehash.
+
+
+
+
+
+
+
+
+
+ Mapped
+
+
+
+ Hash
+
+
+ Pred
+
+
+ Alloc
+
+
+ typename allocator_type::pointer
+
+
+ typename allocator_type::const_pointer
+
+
+ typename allocator_type::reference
+ lvalue of value_type.
+
+
+ typename allocator_type::const_reference
+ const lvalue of value_type.
+
+
+ implementation-defined
+
+ An unsigned integral type.
+ size_type can represent any non-negative value of difference_type.
+
+
+
+ implementation-defined
+
+ A signed integral type.
+ Is identical to the difference type of iterator and const_iterator.
+
+
+
+ implementation-defined
+
+ iterator whose value type is value_type.
+ The iterator category is at least a forward iterator.
+ Convertible to const_iterator.
+
+
+
+ implementation-defined
+
+ A constant iterator whose value type is value_type.
+ The iterator category is at least a forward iterator.
+
+
+
+ implementation-defined
+
+ An iterator with the same value type, difference type and pointer and reference type as iterator.
+ A local_iterator object can be used to iterate through a single bucket.
+
+
+
+ implementation-defined
+
+ A constant iterator with the same value type, difference type and pointer and reference type as const_iterator.
+ A const_local_iterator object can be used to iterate through a single bucket.
+
+
+
+
+ size_type
+ implementation-defined
+
+
+ hasher const&
+ hasher()
+
+
+ key_equal const&
+ key_equal()
+
+
+ allocator_type const&
+ allocator_type()
+
+
+ size() == 0
+
+
+ Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0.
+
+
+
+
+
+
+
+
+ InputIterator
+
+
+ InputIterator
+
+
+ size_type
+ implementation-defined
+
+
+ hasher const&
+ hasher()
+
+
+ key_equal const&
+ key_equal()
+
+
+ allocator_type const&
+ allocator_type()
+
+
+ Constructs an empty container with at least n buckets, using hf as the hash function, eq as the key equality predicate, a as the allocator and a maximum load factor of 1.0 and inserts the elements from [f, l) into it.
+
+
+
+
+ const&
+
+
+ The copy constructor. Copies the contained elements, hash function, predicate, maximum load factor and allocator.
+
+
+ value_type
is copy constructible
+
+
+
+
+ &&
+
+
+ The move constructor.
+
+
+ This is emulated on compilers without rvalue references.
+
+
+
+ value_type
is move constructible.
+ (TODO: This is not actually required in this implementation).
+
+
+
+
+
+ Allocator const&
+
+
+ Constructs an empty container, using allocator a
.
+
+
+
+
+ const&
+
+
+ Allocator const&
+
+
+ Constructs an container, copying x
's contained elements, hash function, predicate, maximum load factor, but using allocator a
.
+
+
+
+
+ The destructor is applied to every element, and all memory is deallocated
+
+
+
+
+ const&
+
+ &
+
+ The assignment operator. Copies the contained elements, hash function, predicate and maximum load factor but not the allocator.
+
+
+
+ On compilers without rvalue references, there is a single assignment
+ operator with the signature operator=()
+ in order to emulate move semantics.
+
+
+
+ value_type
is copy constructible
+
+
+
+
+ &&
+
+ &
+
+ The move assignment operator.
+
+
+
+ On compilers without rvalue references, there is a single assignment
+ operator with the signature operator=()
+ in order to emulate move semantics.
+
+
+
+
+ value_type
is move constructible.
+ (TODO: This is not actually required in this implementation).
+
+
+
+
+ allocator_type
+
+
+
+ bool
+
+ size() == 0
+
+
+
+ size_type
+
+ std::distance(begin(), end())
+
+
+
+ size_type
+ size()
of the largest possible container.
+
+
+
+
+
+ iterator
+ const_iterator
+ An iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container.
+
+
+
+
+ iterator
+
+
+ const_iterator
+
+ An iterator which refers to the past-the-end value for the container.
+
+
+
+ const_iterator
+ A constant iterator referring to the first element of the container, or if the container is empty the past-the-end value for the container.
+
+
+
+ const_iterator
+ A constant iterator which refers to the past-the-end value for the container.
+
+
+
+
+
+
+
+
+
+
+ Args&&
+
+
+
+ Inserts an object, constructed with the arguments args
, in the container
+
+
+
+ An iterator pointing to the inserted element.
+
+ The bool component of the return type is true if an insert took place.
+ If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent .
+
+
+
+ If an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+ If the compiler doesn't support variadic template arguments or rvalue
+ references, this is emulated for up to 10 arguments, with no support
+ for rvalue references or move semantics.
+
+
+
+
+
+
+
+
+ const_iterator
+
+
+ Args&&
+
+ iterator
+
+ Inserts an object, constructed with the arguments args
, in the container
+ hint
is a suggestion to where the element should be inserted.
+
+
+
+ An iterator pointing to the inserted element.
+
+ If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent .
+
+
+
+ If an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same .
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+ If the compiler doesn't support variadic template arguments or rvalue
+ references, this is emulated for up to 10 arguments, with no support
+ for rvalue references or move semantics.
+
+
+
+
+ value_type const&
+
+
+
+ Inserts obj
in the container
+
+
+
+ An iterator pointing to the inserted element.
+
+ The bool component of the return type is true if an insert took place.
+ If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent .
+
+
+
+ If an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+
+
+
+
+ const_iterator
+
+
+ value_type const&
+
+ iterator
+
+
+ Inserts obj
in the container.
+
+ Inserts obj
in the container if and only if there is no element in the container with an equivalent .
+
+ hint is a suggestion to where the element should be inserted.
+
+
+
+ An iterator pointing to the inserted element.
+
+ If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent .
+
+
+
+ If an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ The standard is fairly vague on the meaning of the hint. But the only practical way to use it, and the only way that Boost.Unordered supports is to point to an existing element with the same .
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+
+
+
+
+
+
+
+
+ InputIterator
+
+
+ InputIterator
+
+ void
+
+ Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent .
+
+
+ When inserting a single element, if an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+
+
+
+
+ const_iterator
+
+ iterator
+
+ Erase the element pointed to by position
.
+
+
+ The iterator following position
before the erasure.
+
+
+ Only throws an exception if it is thrown by hasher
or key_equal
.
+ In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations.
+
+
+
+ When the number of elements is a lot smaller than the number of buckets
+ this function can be very inefficient as it has to search through empty
+ buckets for the next element, in order to return the iterator.
+ The method quick_erase is faster, but has yet
+ to be standardized.
+
+
+
+
+
+ key_type const&
+
+ size_type
+
+ Erase all elements with key equivalent to k
.
+
+
+ The number of elements erased.
+
+
+ Only throws an exception if it is thrown by hasher
or key_equal
.
+
+
+
+
+ const_iterator
+
+
+ const_iterator
+
+ iterator
+
+ Erases the elements in the range from first
to last
.
+
+
+ The iterator following the erased elements - i.e. last
.
+
+
+ Only throws an exception if it is thrown by hasher
or key_equal
.
+ In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations.
+
+
+
+
+ const_iterator
+
+ void
+
+ Erase the element pointed to by position
.
+
+
+ Only throws an exception if it is thrown by hasher
or key_equal
.
+ In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations.
+
+
+
+ This method is faster than erase as
+ it doesn't have to find the next element in the container -
+ a potentially costly operation.
+
+
+ As it hasn't been standardized, it's likely that this may
+ change in the future.
+
+
+
+
+
+ const_iterator
+
+ void
+
+ Erase the element pointed to by position
.
+
+
+ Only throws an exception if it is thrown by hasher
or key_equal
.
+ In this implementation, this overload doesn't call either function object's methods so it is no throw, but this might not be true in other implementations.
+
+
+
+ This method is now deprecated, use
+ quick_return instead. Although be
+ warned that as that isn't standardized yet, it could also
+ change.
+
+
+
+
+ void
+
+ Erases all elements in the container.
+
+
+ size() == 0
+
+
+ Never throws an exception.
+
+
+
+
+ &
+
+ void
+
+ If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of key_equal
or hasher
.
+
+
+ For a discussion of the behavior when allocators aren't equal see
+ the implementation details.
+
+
+
+
+
+ hasher
+ The container's hash function.
+
+
+
+ key_equal
+ The container's key equality predicate.
+
+
+
+
+
+
+
+ key_type const&
+
+ iterator
+
+
+
+ key_type const&
+
+ const_iterator
+
+
+
+
+
+
+
+
+ CompatibleKey const&
+
+
+ CompatibleHash const&
+
+
+ CompatiblePredicate const&
+
+ iterator
+
+
+
+
+
+
+
+
+ CompatibleKey const&
+
+
+ CompatibleHash const&
+
+
+ CompatiblePredicate const&
+
+ const_iterator
+
+
+ An iterator pointing to an element with key equivalent to k
, or b.end()
if no such element exists.
+
+
+ The templated overloads are a non-standard extensions which
+ allows you to use a compatible hash function and equality
+ predicate for a key of a different type in order to avoid
+ an expensive type cast. In general, its use is not encouraged.
+
+
+
+
+ key_type const&
+
+ size_type
+
+ The number of elements with key equivalent to k
.
+
+
+
+
+
+ key_type const&
+
+ std::pair<iterator, iterator>
+
+
+
+ key_type const&
+
+ std::pair<const_iterator, const_iterator>
+
+
+ A range containing all elements with key equivalent to k
.
+ If the container doesn't container any such elements, returns
+ std::make_pair(b.end(),b.end())
.
+
+
+
+
+
+
+ key_type const&
+
+ mapped_type&
+
+ If the container does not already contain an elements with a key equivalent to k
, inserts the value std::pair<key_type const, mapped_type>(k, mapped_type())
+
+
+ A reference to x.second
where x is the element already in the container, or the newly inserted element with a key equivalent to k
+
+
+ If an exception is thrown by an operation other than a call to hasher
the function has no effect.
+
+
+ Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
+ Pointers and references to elements are never invalidated.
+
+
+
+ Mapped&
+ key_type const&
+ Mapped const&
+ key_type const&
+
+ A reference to x.second
where x
is the (unique) element whose key is equivalent to k
.
+
+
+ An exception object of type std::out_of_range
if no such element is present.
+
+
+ This is not specified in the draft standard, but that is probably an oversight. The issue has been raised in
+ comp.std.c++.
+
+
+
+
+
+
+ size_type
+
+ The number of buckets.
+
+
+
+ size_type
+
+ An upper bound on the number of buckets.
+
+
+
+
+ size_type
+
+ size_type
+
+ n < bucket_count()
+
+
+ The number of elements in bucket n
.
+
+
+
+
+ key_type const&
+
+ size_type
+
+ The index of the bucket which would contain an element with key k
.
+
+
+ The return value is less than bucket_count()
+
+
+
+
+
+ size_type
+
+ local_iterator
+
+
+
+ size_type
+
+ const_local_iterator
+
+
+ n
shall be in the range [0, bucket_count())
.
+
+
+ A local iterator pointing the first element in the bucket with index n
.
+
+
+
+
+
+ size_type
+
+ local_iterator
+
+
+
+ size_type
+
+ const_local_iterator
+
+
+ n
shall be in the range [0, bucket_count())
.
+
+
+ A local iterator pointing the 'one past the end' element in the bucket with index n
.
+
+
+
+
+ size_type
+
+ const_local_iterator
+
+ n
shall be in the range [0, bucket_count())
.
+
+
+ A constant local iterator pointing the first element in the bucket with index n
.
+
+
+
+
+ size_type
+
+ const_local_iterator
+
+ n
shall be in the range [0, bucket_count())
.
+
+
+ A constant local iterator pointing the 'one past the end' element in the bucket with index n
.
+
+
+
+
+
+ float
+
+ The average number of elements per bucket.
+
+
+
+ float
+
+ Returns the current maximum load factor.
+
+
+
+
+ float
+
+ void
+
+ Changes the container's maximum load factor, using z
as a hint.
+
+
+
+
+ size_type
+
+ void
+
+ Changes the number of buckets so that there at least n
buckets, and so that the load factor is less than the maximum load factor.
+ Invalidates iterators, and changes the order of elements. Pointers and references to elements are not invalidated.
+
+
+ The function has no effect if an exception is thrown, unless it is thrown by the container's hash function or comparison function.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ const&
+
+
+ const&
+
+ bool
+
+ This is a boost extension.
+ Behavior is undefined if the two containers don't have
+ equivalent equality predicates.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ const&
+
+
+ const&
+
+ bool
+
+ This is a boost extension.
+ Behavior is undefined if the two containers don't have
+ equivalent equality predicates.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &
+
+
+ &
+
+ void
+
+ x.swap(y)
+
+
+ If the allocators are equal, doesn't throw an exception unless it is thrown by the copy constructor or copy assignment operator of Hash
or Pred
.
+
+
+ For a discussion of the behavior when allocators aren't equal see
+ the implementation details.
+
+
+
+
+
+
+
+
+
diff --git a/doc/ref.xml b/doc/ref.xml
index 92aaffa7..7825de2f 100644
--- a/doc/ref.xml
+++ b/doc/ref.xml
@@ -40,8 +40,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Pred
A binary function object that implements an equivalence relation on values of type Value
.
- A binary function object that induces an equivalence relation on values of type Key.
- It takes two arguments of type Key and returns a value of type bool.
+ A binary function object that induces an equivalence relation on values of type Value
.
+ It takes two arguments of type Value
and returns a value of type bool.
Alloc
An allocator whose value type is the same as the container's value type.
@@ -360,7 +360,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
Inserts an object, constructed with the arguments args
, in the container if and only if there is no element in the container with an equivalent value.
- hint is a suggestion to where the element should be inserted.
+ hint
is a suggestion to where the element should be inserted.
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent value.
@@ -383,7 +383,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
std::pair<iterator, bool>
- Inserts obj in the container if and only if there is no element in the container with an equivalent value.
+ Inserts obj
in the container if and only if there is no element in the container with an equivalent value.
The bool component of the return type is true if an insert took place.
@@ -406,7 +406,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container if and only if there is no element in the container with an equivalent value.
+ Inserts obj
in the container if and only if there is no element in the container with an equivalent value.
hint is a suggestion to where the element should be inserted.
@@ -815,8 +815,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -839,8 +839,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -865,8 +865,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -909,7 +909,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- An unordered associative container that stores values. The same key can be stored multiple times.
+ An unordered associative container that stores values. The same key can be stored multiple times.
Based on chapter 23 of
@@ -929,8 +929,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Pred
A binary function object that implements an equivalence relation on values of type Value
.
- A binary function object that induces an equivalence relation on values of type Key.
- It takes two arguments of type Key and returns a value of type bool.
+ A binary function object that induces an equivalence relation on values of type Value
.
+ It takes two arguments of type Value
and returns a value of type bool.
Alloc
An allocator whose value type is the same as the container's value type.
@@ -1248,7 +1248,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
Inserts an object, constructed with the arguments args
, in the container.
- hint is a suggestion to where the element should be inserted.
+ hint
is a suggestion to where the element should be inserted.
An iterator pointing to the inserted element.
@@ -1271,7 +1271,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container.
+ Inserts obj
in the container.
An iterator pointing to the inserted element.
@@ -1293,7 +1293,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container.
+ Inserts obj
in the container.
hint is a suggestion to where the element should be inserted.
@@ -1321,7 +1321,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
void
- Inserts a range of elements into the container.
+ Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent value.
When inserting a single element, if an exception is thrown by an operation other than a call to hasher
the function has no effect.
@@ -1702,8 +1702,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -1726,8 +1726,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -1752,8 +1752,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
+
+
@@ -1798,7 +1798,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
std::equal_to<Key>
- std::allocator<std::pair<Key const, Mapped> >
+ std::allocator<std::pair<Key const, Mapped>>
@@ -1825,8 +1825,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Pred
A binary function object that implements an equivalence relation on values of type Key
.
- A binary function object that induces an equivalence relation on values of type Key.
- It takes two arguments of type Key and returns a value of type bool.
+ A binary function object that induces an equivalence relation on values of type Key
.
+ It takes two arguments of type Key
and returns a value of type bool.
Alloc
An allocator whose value type is the same as the container's value type.
@@ -1882,7 +1882,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
implementation-defined
- A iterator whose value type is value_type.
+ An iterator whose value type is value_type.
The iterator category is at least a forward iterator.
Convertible to const_iterator.
@@ -2148,7 +2148,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
Inserts an object, constructed with the arguments args
, in the container if and only if there is no element in the container with an equivalent key.
- hint is a suggestion to where the element should be inserted.
+ hint
is a suggestion to where the element should be inserted.
If an insert took place, then the iterator points to the newly inserted element. Otherwise, it points to the element with equivalent key.
@@ -2171,7 +2171,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
std::pair<iterator, bool>
- Inserts obj in the container if and only if there is no element in the container with an equivalent key.
+ Inserts obj
in the container if and only if there is no element in the container with an equivalent key.
The bool component of the return type is true if an insert took place.
@@ -2194,7 +2194,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container if and only if there is no element in the container with an equivalent key.
+ Inserts obj
in the container if and only if there is no element in the container with an equivalent key.
hint is a suggestion to where the element should be inserted.
@@ -2638,10 +2638,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
@@ -2664,10 +2664,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
@@ -2692,10 +2692,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
@@ -2736,11 +2736,11 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
std::equal_to<Key>
- std::allocator<std::pair<Key const, Mapped> >
+ std::allocator<std::pair<Key const, Mapped>>
- An unordered associative container that associates keys with another value. The same key can be stored multiple times.
+ An unordered associative container that associates keys with another value. The same key can be stored multiple times.
Based on chapter 23 of
@@ -2763,8 +2763,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Pred
A binary function object that implements an equivalence relation on values of type Key
.
- A binary function object that induces an equivalence relation on values of type Key.
- It takes two arguments of type Key and returns a value of type bool.
+ A binary function object that induces an equivalence relation on values of type Key
.
+ It takes two arguments of type Key
and returns a value of type bool.
Alloc
An allocator whose value type is the same as the container's value type.
@@ -2820,7 +2820,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
implementation-defined
- A iterator whose value type is value_type.
+ An iterator whose value type is value_type.
The iterator category is at least a forward iterator.
Convertible to const_iterator.
@@ -3085,7 +3085,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
Inserts an object, constructed with the arguments args
, in the container.
- hint is a suggestion to where the element should be inserted.
+ hint
is a suggestion to where the element should be inserted.
An iterator pointing to the inserted element.
@@ -3108,7 +3108,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container.
+ Inserts obj
in the container.
An iterator pointing to the inserted element.
@@ -3130,7 +3130,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
iterator
- Inserts obj in the container.
+ Inserts obj
in the container.
hint is a suggestion to where the element should be inserted.
@@ -3158,7 +3158,7 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
void
- Inserts a range of elements into the container.
+ Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key.
When inserting a single element, if an exception is thrown by an operation other than a call to hasher
the function has no effect.
@@ -3539,10 +3539,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
@@ -3565,10 +3565,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
@@ -3593,10 +3593,10 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-
-
-
-
+
+
+
+
diff --git a/doc/unordered.qbk b/doc/unordered.qbk
index fb3837f4..431518ed 100644
--- a/doc/unordered.qbk
+++ b/doc/unordered.qbk
@@ -31,6 +31,7 @@
[include:unordered buckets.qbk]
[include:unordered hash_equality.qbk]
[include:unordered comparison.qbk]
+[include:unordered compliance.qbk]
[include:unordered rationale.qbk]
[include:unordered changes.qbk]
[xinclude ref.xml]
diff --git a/include/boost/unordered/detail/allocator_helpers.hpp b/include/boost/unordered/detail/allocator_helpers.hpp
index 2c642231..79f40467 100644
--- a/include/boost/unordered/detail/allocator_helpers.hpp
+++ b/include/boost/unordered/detail/allocator_helpers.hpp
@@ -1,9 +1,12 @@
-// Copyright 2005-2009 Daniel James.
+// Copyright 2005-2011 Daniel James.
+// Copyright 2009 Pablo Halpern.
+//
// 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)
-
-// A couple of templates to make using allocators easier.
+//
+// Written by Daniel James using some code from Pablo Halpern's
+// allocator traits implementation.
#ifndef BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_ALLOCATOR_UTILITIES_HPP_INCLUDED
@@ -13,6 +16,8 @@
#endif
#include
+#include
+#include
#if (defined(BOOST_NO_STD_ALLOCATOR) || defined(BOOST_DINKUMWARE_STDLIB)) \
&& !defined(__BORLANDC__)
@@ -23,24 +28,257 @@
# include
#endif
-namespace boost { namespace unordered_detail {
+#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS
+# include
+#endif
+#if !defined(BOOST_NO_0X_HDR_TYPE_TRAITS)
+#include
+namespace boost { namespace unordered { namespace detail {
+ using std::integral_constant;
+ using std::true_type;
+ using std::false_type;
+}}}
+#else
+namespace boost { namespace unordered { namespace detail {
+ template
+ struct integral_constant { enum { value = Value }; };
+ typedef integral_constant true_type;
+ typedef integral_constant false_type;
+}}}
+#endif
+
+// TODO: Use std::addressof if available?
+#include
+
+namespace boost { namespace unordered { namespace detail {
+
+#if BOOST_UNORDERED_USE_ALLOCATOR_TRAITS
+ template
+ struct allocator_traits : std::allocator_traits {};
+
+ template
+ struct rebind_wrap
+ {
+ typedef typename allocator_traits::rebind_alloc type;
+ };
+#else
// rebind_wrap
//
// Rebind allocators. For some problematic libraries, use rebind_to
// from .
-#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
- template
+# if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
+ template
struct rebind_wrap : ::boost::detail::allocator::rebind_to {};
-#else
- template
+# else
+ template
struct rebind_wrap
{
typedef BOOST_DEDUCED_TYPENAME
Alloc::BOOST_NESTED_TEMPLATE rebind::other
type;
};
+# endif
+
+ struct convertible_from_anything
+ {
+ template convertible_from_anything(T const&);
+ };
+
+ typedef char (&no_type)[1];
+ typedef char (&yes_type)[2];
+
+ template struct sfinae {
+ typedef yes_type type;
+ };
+
+ // Infrastructure for providing a default type for Tp::tname if absent.
+ #define BOOST_DEFAULT_TYPE_TMPLT(tname) \
+ template \
+ struct default_type_ ## tname { \
+ template \
+ static BOOST_DEDUCED_TYPENAME sfinae< \
+ BOOST_DEDUCED_TYPENAME T::tname>::type test(int); \
+ template \
+ static no_type test(long); \
+ \
+ enum { value = sizeof(test(0)) == sizeof(yes_type) }; \
+ \
+ struct DefaultWrap { typedef Default tname; }; \
+ \
+ typedef BOOST_DEDUCED_TYPENAME \
+ boost::detail::if_true:: \
+ BOOST_NESTED_TEMPLATE then \
+ ::type::tname type; \
+ }
+
+ #define BOOST_DEFAULT_TYPE(T,tname, arg) \
+ BOOST_DEDUCED_TYPENAME default_type_ ## tname::type
+
+ BOOST_DEFAULT_TYPE_TMPLT(pointer);
+ BOOST_DEFAULT_TYPE_TMPLT(const_pointer);
+ BOOST_DEFAULT_TYPE_TMPLT(void_pointer);
+ BOOST_DEFAULT_TYPE_TMPLT(const_void_pointer);
+ BOOST_DEFAULT_TYPE_TMPLT(difference_type);
+ BOOST_DEFAULT_TYPE_TMPLT(size_type);
+ BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_copy_assignment);
+ BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_move_assignment);
+ BOOST_DEFAULT_TYPE_TMPLT(propagate_on_container_swap);
+
+#if !defined(BOOST_NO_SFINAE_EXPR) || BOOST_WORKAROUND(BOOST_MSVC, >= 1500)
+ // Specialization is only needed for Visual C++. Without it SFINAE doesn't
+ // kick in.
+ template
+ struct expr_sfinae;
+
+ template <>
+ struct expr_sfinae {
+ typedef yes_type type;
+ };
+
+ template
+ struct has_select_on_container_copy_construction
+ {
+ // This needs to be a template for Visual C++.
+ template
+ static yes_type to_yes_type(const T2&);
+
+ template
+ static typename expr_sfinaeselect_on_container_copy_construction()
+ ))>::type check(T2*);
+
+ static no_type check(void*);
+
+ enum { value = sizeof(check((T*) 0)) == sizeof(yes_type) };
+ };
+#else
+ template
+ struct has_select_on_container_copy_construction
+ {
+ typedef T (T::*SelectFunc)() const;
+
+ template struct sfinae { typedef yes_type type; };
+
+ template
+ static typename sfinae<&U::select_on_container_copy_construction>::type
+ test(int);
+ template
+ static no_type test(...);
+
+ enum { value = sizeof(test(1)) == sizeof(yes_type) };
+ };
+
+#endif
+
+ template
+ inline BOOST_DEDUCED_TYPENAME boost::enable_if<
+ has_select_on_container_copy_construction, Alloc
+ >::type call_select_on_container_copy_construction(const Alloc& rhs)
+ {
+ return rhs.select_on_container_copy_construction();
+ }
+
+ template
+ inline BOOST_DEDUCED_TYPENAME boost::disable_if<
+ has_select_on_container_copy_construction, Alloc
+ >::type call_select_on_container_copy_construction(const Alloc& rhs)
+ {
+ return rhs;
+ }
+
+ template
+ struct allocator_traits
+ {
+ typedef Alloc allocator_type;
+ typedef typename Alloc::value_type value_type;
+
+ typedef BOOST_DEFAULT_TYPE(Alloc, pointer, value_type*)
+ pointer;
+
+ // For now always use the allocator's const_pointer.
+
+ //typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer,
+ // BOOST_DEDUCED_TYPENAME pointer_traits::
+ // BOOST_NESTED_TEMPLATE rebind::other)
+ // const_pointer;
+
+ typedef BOOST_DEFAULT_TYPE(Alloc, const_pointer, value_type const*)
+ const_pointer;
+
+ // I'm not using void pointers for now.
+
+ //typedef BOOST_DEFAULT_TYPE(Alloc, void_pointer,
+ // BOOST_NESTED_TEMPLATE pointer_traits::
+ // BOOST_NESTED_TEMPLATE rebind::other)
+ // void_pointer;
+
+ //typedef BOOST_DEFAULT_TYPE(Alloc, const_void_pointer,
+ // BOOST_DEDUCED_TYPENAME pointer_traits::
+ // BOOST_NESTED_TEMPLATE rebind::other)
+ // const_void_pointer;
+
+ typedef BOOST_DEFAULT_TYPE(Alloc, difference_type, std::ptrdiff_t)
+ difference_type;
+
+ typedef BOOST_DEFAULT_TYPE(Alloc, size_type, std::size_t)
+ size_type;
+
+ // TODO: rebind_alloc and rebind_traits
+
+ static pointer allocate(Alloc& a, size_type n)
+ { return a.allocate(n); }
+
+ // I never use this, so I'll just comment it out for now.
+ //
+ //static pointer allocate(Alloc& a, size_type n, const_void_pointer hint)
+ // { return DEFAULT_FUNC(allocate, pointer)(a, n, hint); }
+
+ static void deallocate(Alloc& a, pointer p, size_type n)
+ { a.deallocate(p, n); }
+
+ // Only support the basic copy constructor
+
+ // template
+ // static void construct(Alloc& a, T* p, Args&&... args) {
+ // DEFAULT_FUNC(construct,void)(a, p, std::forward(args)...);
+ // }
+
+ template
+ static void construct(Alloc& a, T* p, T const& x) {
+ a.construct(p, x);
+ }
+
+ template
+ static void destroy(Alloc& a, T* p) {
+ // DEFAULT_FUNC(destroy,void)(a, p);
+ a.destroy(p);
+ }
+
+ static size_type max_size(const Alloc& a)
+ { return a.max_size(); }
+
+ // Allocator propagation on construction
+
+ static Alloc select_on_container_copy_construction(Alloc const& rhs)
+ {
+ return boost::unordered::detail::
+ call_select_on_container_copy_construction(rhs);
+ }
+
+ // Allocator propagation on assignment and swap.
+ // Return true if lhs is modified.
+ typedef BOOST_DEFAULT_TYPE(
+ Alloc, propagate_on_container_copy_assignment, false_type)
+ propagate_on_container_copy_assignment;
+ typedef BOOST_DEFAULT_TYPE(
+ Alloc,propagate_on_container_move_assignment, false_type)
+ propagate_on_container_move_assignment;
+ typedef BOOST_DEFAULT_TYPE(
+ Alloc,propagate_on_container_swap,false_type)
+ propagate_on_container_swap;
+ };
#endif
// allocator_array_constructor
@@ -49,10 +287,11 @@ namespace boost { namespace unordered_detail {
// clean up if an exception is thrown before the container takes charge
// of it.
- template
+ template
struct allocator_array_constructor
{
- typedef BOOST_DEDUCED_TYPENAME Allocator::pointer pointer;
+ typedef BOOST_DEDUCED_TYPENAME allocator_traits::pointer
+ pointer;
Allocator& alloc_;
pointer ptr_;
@@ -69,21 +308,23 @@ namespace boost { namespace unordered_detail {
~allocator_array_constructor() {
if (ptr_) {
for(pointer p = ptr_; p != constructed_; ++p)
- alloc_.destroy(p);
+ allocator_traits::destroy(alloc_,
+ boost::addressof(*p));
- alloc_.deallocate(ptr_, length_);
+ allocator_traits::deallocate(alloc_, ptr_, length_);
}
}
- template
+ template
void construct(V const& v, std::size_t l)
{
BOOST_ASSERT(!ptr_);
length_ = l;
- ptr_ = alloc_.allocate(length_);
+ ptr_ = allocator_traits::allocate(alloc_, length_);
pointer end = ptr_ + static_cast(length_);
for(constructed_ = ptr_; constructed_ != end; ++constructed_)
- alloc_.construct(constructed_, v);
+ allocator_traits::construct(alloc_,
+ boost::addressof(*constructed_), v);
}
pointer get() const
@@ -102,7 +343,7 @@ namespace boost { namespace unordered_detail {
allocator_array_constructor& operator=(
allocator_array_constructor const&);
};
-}}
+}}}
#if defined(BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES)
# undef BOOST_UNORDERED_USE_ALLOCATOR_UTILITIES
diff --git a/include/boost/unordered/detail/buckets.hpp b/include/boost/unordered/detail/buckets.hpp
index 913dbcd2..de942c37 100644
--- a/include/boost/unordered/detail/buckets.hpp
+++ b/include/boost/unordered/detail/buckets.hpp
@@ -1,183 +1,808 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
-// Copyright (C) 2005-2009 Daniel James
+// 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_MANAGER_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_MANAGER_HPP_INCLUDED
-#include
-#include
#include
-#include
-namespace boost { namespace unordered_detail {
+namespace boost { namespace unordered { namespace detail {
////////////////////////////////////////////////////////////////////////////
- // Buckets
+ //
+ // Now the main data structure:
+ //
+ // buckets functions
+ // | |
+ // +---------------+--------------+
+ // |
+ // table
+ //
+ // T is a class which contains typedefs for all the types we need.
- template
- inline std::size_t hash_buckets::max_bucket_count() const {
- // -1 to account for the sentinel.
- return prev_prime(this->bucket_alloc().max_size() - 1);
- }
+ // buckets
+ //
+ // This is responsible for allocating and deallocating buckets and nodes.
+ //
+ // Notes:
+ // 1. For the sake exception safety the consturctors don't allocate
+ // anything.
+ // 2. It's the callers responsibility to allocate the buckets before calling
+ // any of the methods (other than getters and setters).
- template
- inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr
- hash_buckets::get_bucket(std::size_t num) const
+ template
+ class buckets
{
- return buckets_ + static_cast(num);
- }
+ buckets(buckets const&);
+ buckets& operator=(buckets const&);
+ public:
+ // Types
- template
- inline BOOST_DEDUCED_TYPENAME hash_buckets::bucket_ptr
- hash_buckets::bucket_ptr_from_hash(std::size_t hashed) const
- {
- return get_bucket(hashed % bucket_count_);
- }
+ typedef BOOST_DEDUCED_TYPENAME ::boost::detail::if_true::
+ BOOST_NESTED_TEMPLATE then<
+ ::boost::unordered::detail::ungrouped_node,
+ ::boost::unordered::detail::grouped_node
+ >::type node;
+
+ typedef A value_allocator;
+ typedef ::boost::unordered::detail::bucket bucket;
+ typedef BOOST_DEDUCED_TYPENAME allocator_traits::value_type value_type;
+
+ typedef BOOST_DEDUCED_TYPENAME bucket::bucket_allocator
+ bucket_allocator;
+ typedef BOOST_DEDUCED_TYPENAME allocator_traits::pointer bucket_ptr;
+ typedef BOOST_DEDUCED_TYPENAME bucket::node_ptr node_ptr;
+
+ typedef BOOST_DEDUCED_TYPENAME rebind_wrap::type
+ node_allocator;
+ typedef BOOST_DEDUCED_TYPENAME allocator_traits::pointer real_node_ptr;
+
+ // Members
+
+ bucket_ptr buckets_;
+ std::size_t bucket_count_;
+ std::size_t size_;
+ compressed_pair allocators_;
+
+ // Data access
+
+ bucket_allocator const& bucket_alloc() const
+ {
+ return allocators_.first();
+ }
+
+ node_allocator const& node_alloc() const
+ {
+ return allocators_.second();
+ }
+
+ bucket_allocator& bucket_alloc()
+ {
+ return allocators_.first();
+ }
+
+ node_allocator& node_alloc()
+ {
+ return allocators_.second();
+ }
+
+ std::size_t max_bucket_count() const
+ {
+ // -1 to account for the start bucket.
+ return prev_prime(allocator_traits::max_size(bucket_alloc()) - 1);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Constructors and Destructors
+
+ buckets(node_allocator const& a, std::size_t bucket_count)
+ : buckets_(),
+ bucket_count_(bucket_count),
+ size_(),
+ allocators_(a,a)
+ {
+ }
+
+ buckets(buckets& b, move_tag m)
+ : buckets_(),
+ bucket_count_(b.bucket_count_),
+ size_(),
+ allocators_(b.allocators_, m)
+ {
+ swap(b);
+ }
+
+ template
+ buckets(table& x, move_tag m)
+ : buckets_(),
+ bucket_count_(x.bucket_count_),
+ allocators_(x.allocators_, m)
+ {
+ swap(x);
+ x.size_ = 0;
+ }
+
+ inline ~buckets()
+ {
+ if(this->buckets_) { this->delete_buckets(); }
+ }
+
+ void create_buckets()
+ {
+ // The array constructor will clean up in the event of an
+ // exception.
+ allocator_array_constructor
+ constructor(bucket_alloc());
- template
- std::size_t hash_buckets::bucket_size(std::size_t index) const
- {
- if(!buckets_) return 0;
- bucket_ptr ptr = get_bucket(index)->next_;
- std::size_t count = 0;
- while(ptr) {
- ++count;
+ // Creates an extra bucket to act as the start node.
+ constructor.construct(bucket(), this->bucket_count_ + 1);
+
+ // Only release the buckets once everything is successfully
+ // done.
+ this->buckets_ = constructor.release();
+ }
+
+ void swap(buckets& other, false_type = false_type())
+ {
+ BOOST_ASSERT(node_alloc() == other.node_alloc());
+ std::swap(buckets_, other.buckets_);
+ std::swap(bucket_count_, other.bucket_count_);
+ std::swap(size_, other.size_);
+ }
+
+ void swap(buckets& other, true_type)
+ {
+ allocators_.swap(other.allocators_);
+ std::swap(buckets_, other.buckets_);
+ std::swap(bucket_count_, other.bucket_count_);
+ std::swap(size_, other.size_);
+ }
+
+ void move_buckets_from(buckets& other)
+ {
+ BOOST_ASSERT(node_alloc() == other.node_alloc());
+ BOOST_ASSERT(!this->buckets_);
+ this->buckets_ = other.buckets_;
+ this->bucket_count_ = other.bucket_count_;
+ this->size_ = other.size_;
+ other.buckets_ = bucket_ptr();
+ other.bucket_count_ = 0;
+ other.size_ = 0;
+ }
+
+ std::size_t bucket_size(std::size_t index) const
+ {
+ if (!this->size_) return 0;
+ node_ptr ptr = this->buckets_[index].next_;
+ if (!ptr) return 0;
ptr = ptr->next_;
- }
- return count;
- }
-
- template
- inline BOOST_DEDUCED_TYPENAME hash_buckets::node_ptr
- hash_buckets::bucket_begin(std::size_t num) const
- {
- return buckets_ ? get_bucket(num)->next_ : node_ptr();
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Delete
-
- template
- inline void hash_buckets::delete_node(node_ptr b)
- {
- node* raw_ptr = static_cast(&*b);
- boost::unordered_detail::destroy(raw_ptr->value_ptr());
- real_node_ptr n(node_alloc().address(*raw_ptr));
- node_alloc().destroy(n);
- node_alloc().deallocate(n, 1);
- }
-
- template
- inline void hash_buckets::clear_bucket(bucket_ptr b)
- {
- node_ptr node_it = b->next_;
- b->next_ = node_ptr();
-
- while(node_it) {
- node_ptr node_to_delete = node_it;
- node_it = node_it->next_;
- delete_node(node_to_delete);
- }
- }
-
- template
- inline void hash_buckets::delete_buckets()
- {
- bucket_ptr end = this->get_bucket(this->bucket_count_);
-
- for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
- clear_bucket(begin);
- }
-
- // Destroy the buckets (including the sentinel bucket).
- ++end;
- for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
- bucket_alloc().destroy(begin);
- }
-
- bucket_alloc().deallocate(this->buckets_, this->bucket_count_ + 1);
-
- this->buckets_ = bucket_ptr();
- }
-
- template
- inline std::size_t hash_buckets::delete_nodes(
- node_ptr begin, node_ptr end)
- {
- std::size_t count = 0;
- while(begin != end) {
- node_ptr n = begin;
- begin = begin->next_;
- delete_node(n);
- ++count;
- }
- return count;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Constructors and Destructors
-
- template
- inline hash_buckets::hash_buckets(
- node_allocator const& a, std::size_t bucket_count)
- : buckets_(),
- bucket_count_(bucket_count),
- allocators_(a,a)
- {
- }
-
- template
- inline hash_buckets::~hash_buckets()
- {
- if(this->buckets_) { this->delete_buckets(); }
- }
- template
- inline void hash_buckets::create_buckets()
+ std::size_t count = 0;
+ while(BOOST_UNORDERED_BORLAND_BOOL(ptr) &&
+ node::get_hash(ptr) % this->bucket_count_ == index)
+ {
+ ++count;
+ ptr = ptr->next_;
+ }
+
+ return count;
+ }
+
+ node_ptr bucket_begin(std::size_t bucket_index) const
+ {
+ if (!this->size_) return node_ptr();
+ bucket& b = this->buckets_[bucket_index];
+ if (!b.next_) return node_ptr();
+ return b.next_->next_;
+ }
+
+ // For the remaining functions, buckets_ must not be null.
+
+ bucket_ptr get_bucket(std::size_t bucket_index) const
+ {
+ return buckets_ + static_cast(bucket_index);
+ }
+
+ float load_factor() const
+ {
+ BOOST_ASSERT(this->bucket_count_ != 0);
+ return static_cast(this->size_)
+ / static_cast(this->bucket_count_);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Delete
+
+ void delete_node(node_ptr n)
+ {
+ node* raw_ptr = static_cast(boost::addressof(*n));
+ real_node_ptr real_ptr(node_alloc().address(*raw_ptr));
+
+ ::boost::unordered::detail::destroy(raw_ptr->value_ptr());
+ allocator_traits::destroy(node_alloc(), raw_ptr);
+ allocator_traits::deallocate(node_alloc(), real_ptr, 1);
+
+ --this->size_;
+ }
+
+ void delete_buckets()
+ {
+ bucket_ptr end = this->get_bucket(this->bucket_count_);
+ node_ptr n = (end)->next_;
+ while(BOOST_UNORDERED_BORLAND_BOOL(n))
+ {
+ node_ptr node_to_delete = n;
+ n = n->next_;
+ delete_node(node_to_delete);
+ }
+
+ ++end;
+ for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
+ allocator_traits::destroy(bucket_alloc(),
+ boost::addressof(*begin));
+ }
+
+ allocator_traits::deallocate(bucket_alloc(), this->buckets_, this->bucket_count_ + 1);
+
+ this->buckets_ = bucket_ptr();
+ BOOST_ASSERT(this->size_ == 0);
+ }
+
+ std::size_t delete_nodes(node_ptr begin, node_ptr end)
+ {
+ std::size_t count = 0;
+ while(begin != end) {
+ node_ptr n = begin;
+ begin = begin->next_;
+ delete_node(n);
+ ++count;
+ }
+ return count;
+ }
+
+ void clear()
+ {
+ if(!this->size_) return;
+
+ bucket_ptr end = this->get_bucket(this->bucket_count_);
+
+ node_ptr n = (end)->next_;
+ while(BOOST_UNORDERED_BORLAND_BOOL(n))
+ {
+ node_ptr node_to_delete = n;
+ n = n->next_;
+ this->delete_node(node_to_delete);
+ }
+
+ ++end;
+ for(bucket_ptr begin = this->buckets_; begin != end; ++begin) {
+ begin->next_ = bucket_ptr();
+ }
+
+ this->size_ = 0;
+ }
+
+ node_ptr erase(node_ptr r)
+ {
+ BOOST_ASSERT(r);
+ node_ptr next = r->next_;
+
+ bucket_ptr bucket = this->get_bucket(
+ node::get_hash(r) % this->bucket_count_);
+ node_ptr prev = node::unlink_node(*bucket, r);
+
+ this->fix_buckets(bucket, prev, next);
+
+ this->delete_node(r);
+
+ return next;
+ }
+
+ node_ptr erase_range(node_ptr r1, node_ptr r2)
+ {
+ if (r1 == r2) return r2;
+
+ std::size_t bucket_index = node::get_hash(r1) % this->bucket_count_;
+ node_ptr prev = node::unlink_nodes(
+ this->buckets_[bucket_index], r1, r2);
+ this->fix_buckets_range(bucket_index, prev, r1, r2);
+ this->delete_nodes(r1, r2);
+
+ return r2;
+ }
+
+ // This is called after erasing a node or group of nodes to fix up
+ // the bucket pointers.
+ void fix_buckets(bucket_ptr bucket, node_ptr prev, node_ptr next)
+ {
+ if (!next)
+ {
+ if (bucket->next_ == prev) bucket->next_ = node_ptr();
+ }
+ else
+ {
+ bucket_ptr next_bucket = this->get_bucket(
+ node::get_hash(next) % this->bucket_count_);
+ if (next_bucket != bucket)
+ {
+ next_bucket->next_ = prev;
+ if (bucket->next_ == prev) bucket->next_ = node_ptr();
+ }
+ }
+ }
+
+ // This is called after erasing a range of nodes to fix any bucket
+ // pointers into that range.
+ void fix_buckets_range(
+ std::size_t bucket_index, node_ptr prev, node_ptr begin, node_ptr end)
+ {
+ node_ptr n = begin;
+
+ // If we're not at the start of the current bucket, then
+ // go to the start of the next bucket.
+ if (this->get_bucket(bucket_index)->next_ != prev)
+ {
+ for(;;) {
+ n = n->next_;
+ if (n == end) return;
+
+ std::size_t new_bucket_index =
+ node::get_hash(n) % this->bucket_count_;
+ if (bucket_index != new_bucket_index) {
+ bucket_index = new_bucket_index;
+ break;
+ }
+ }
+ }
+
+ // Iterate through the remaining nodes, clearing out the bucket
+ // pointers.
+ this->buckets_[bucket_index].next_ = bucket_ptr();
+ for(;;) {
+ n = n->next_;
+ if (n == end) break;
+
+ std::size_t new_bucket_index =
+ node::get_hash(n) % this->bucket_count_;
+ if (bucket_index != new_bucket_index) {
+ bucket_index = new_bucket_index;
+ this->buckets_[bucket_index].next_ = bucket_ptr();
+ }
+ };
+
+ // Finally fix the bucket containing the trailing node.
+ if (BOOST_UNORDERED_BORLAND_BOOL(n)) {
+ this->buckets_[node::get_hash(n) % this->bucket_count_].next_
+ = prev;
+ }
+ }
+
+ // Iterate through the nodes placing them in the correct buckets.
+ // pre: prev->next_ is not null.
+ node_ptr place_in_bucket(node_ptr prev, node_ptr end) {
+ bucket_ptr b = this->get_bucket(node::get_hash(prev->next_) % this->bucket_count_);
+
+ if (!b->next_) {
+ b->next_ = prev;
+ return end;
+ }
+ else {
+ node_ptr next = end->next_;
+ end->next_ = b->next_->next_;
+ b->next_->next_ = prev->next_;
+ prev->next_ = next;
+ return prev;
+ }
+ }
+
+ void copy_buckets_to(buckets&) const;
+ void move_buckets_to(buckets&) const;
+ void rehash_impl(std::size_t);
+ };
+
+ // Assigning and swapping the equality and hash function objects
+ // needs strong exception safety. To implement that normally we'd
+ // require one of them to be known to not throw and the other to
+ // guarantee strong exception safety. Unfortunately they both only
+ // have basic exception safety. So to acheive strong exception
+ // safety we have storage space for two copies, and assign the new
+ // copies to the unused space. Then switch to using that to use
+ // them. This is implemented in 'set_hash_functions' which
+ // atomically assigns the new function objects in a strongly
+ // exception safe manner.
+
+ template class set_hash_functions;
+
+ template
+ class functions
{
- // The array constructor will clean up in the event of an
- // exception.
- allocator_array_constructor
- constructor(bucket_alloc());
+ friend class set_hash_functions;
+ functions& operator=(functions const&);
- // Creates an extra bucket to act as a sentinel.
- constructor.construct(bucket(), this->bucket_count_ + 1);
+ typedef compressed_pair function_pair;
+ typedef BOOST_DEDUCED_TYPENAME ::boost::aligned_storage<
+ sizeof(function_pair),
+ ::boost::alignment_of::value>::type aligned_function;
- // Set up the sentinel (node_ptr cast)
- bucket_ptr sentinel = constructor.get() +
- static_cast(this->bucket_count_);
- sentinel->next_ = sentinel;
+ bool current_; // The currently active functions.
+ aligned_function funcs_[2];
- // Only release the buckets once everything is successfully
- // done.
- this->buckets_ = constructor.release();
- }
+ function_pair const& current() const {
+ return *static_cast(
+ static_cast(&funcs_[current_]));
+ }
+
+ void construct(bool which, H const& hf, P const& eq)
+ {
+ new((void*) &funcs_[which]) function_pair(hf, eq);
+ }
+
+ void construct(bool which, function_pair const& f)
+ {
+ new((void*) &funcs_[which]) function_pair(f);
+ }
+
+ void destroy(bool which)
+ {
+ ::boost::unordered::detail::destroy((function_pair*)(&funcs_[which]));
+ }
+
+ public:
+
+ functions(H const& hf, P const& eq)
+ : current_(false)
+ {
+ construct(current_, hf, eq);
+ }
+
+ functions(functions const& bf)
+ : current_(false)
+ {
+ construct(current_, bf.current());
+ }
+
+ ~functions() {
+ destroy(current_);
+ }
+
+ H const& hash_function() const {
+ return current().first();
+ }
+
+ P const& key_eq() const {
+ return current().second();
+ }
+ };
+
+ template
+ class set_hash_functions
+ {
+ set_hash_functions(set_hash_functions const&);
+ set_hash_functions& operator=(set_hash_functions const&);
+
+ functions& functions_;
+ bool tmp_functions_;
+
+ public:
+
+ set_hash_functions(functions& f, H const& h, P const& p)
+ : functions_(f),
+ tmp_functions_(!f.current_)
+ {
+ f.construct(tmp_functions_, h, p);
+ }
+
+ set_hash_functions(functions& f, functions const& other)
+ : functions_(f),
+ tmp_functions_(!f.current_)
+ {
+ f.construct(tmp_functions_, other.current());
+ }
+
+ ~set_hash_functions()
+ {
+ functions_.destroy(tmp_functions_);
+ }
+
+ void commit()
+ {
+ functions_.current_ = tmp_functions_;
+ tmp_functions_ = !tmp_functions_;
+ }
+ };
////////////////////////////////////////////////////////////////////////////
- // Constructors and Destructors
+ // Node Constructors
- // no throw
- template
- inline void hash_buckets::move(hash_buckets& other)
+#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
+
+ template
+ inline void construct_impl(T*, void* address, Args&&... args)
{
- BOOST_ASSERT(node_alloc() == other.node_alloc());
- if(this->buckets_) { this->delete_buckets(); }
- this->buckets_ = other.buckets_;
- this->bucket_count_ = other.bucket_count_;
- other.buckets_ = bucket_ptr();
- other.bucket_count_ = 0;
+ new(address) T(std::forward(args)...);
}
- template
- inline void hash_buckets::swap(hash_buckets& other)
- {
- BOOST_ASSERT(node_alloc() == other.node_alloc());
- std::swap(buckets_, other.buckets_);
- std::swap(bucket_count_, other.bucket_count_);
+#else
+
+#define BOOST_UNORDERED_CONSTRUCT_IMPL(z, num_params, _) \
+ template < \
+ class T, \
+ BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
+ > \
+ inline void construct_impl( \
+ T*, void* address, \
+ BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
+ ) \
+ { \
+ new(address) T( \
+ BOOST_UNORDERED_CALL_PARAMS(z, num_params)); \
+ } \
+ \
+ template \
+ inline void construct_impl( \
+ std::pair*, void* address, \
+ Key const& k, BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params)) \
+ { \
+ new(address) std::pair(k, \
+ Second(BOOST_UNORDERED_CALL_PARAMS(z, num_params))); \
}
-}}
+
+ BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
+ BOOST_UNORDERED_CONSTRUCT_IMPL, _)
+
+#undef BOOST_UNORDERED_CONSTRUCT_IMPL
+#endif
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // Node construction
+
+ template
+ class node_constructor
+ {
+ typedef ::boost::unordered::detail::buckets buckets;
+ typedef BOOST_DEDUCED_TYPENAME buckets::node node;
+ typedef BOOST_DEDUCED_TYPENAME buckets::real_node_ptr real_node_ptr;
+ typedef BOOST_DEDUCED_TYPENAME buckets::value_type value_type;
+ typedef BOOST_DEDUCED_TYPENAME buckets::node_allocator node_allocator;
+
+ buckets& buckets_;
+ real_node_ptr node_;
+ bool node_constructed_;
+ bool value_constructed_;
+
+ public:
+
+ node_constructor(buckets& m) :
+ buckets_(m),
+ node_(),
+ node_constructed_(false),
+ value_constructed_(false)
+ {
+ }
+
+ ~node_constructor();
+ void construct_preamble();
+
+#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
+ template
+ void construct(Args&&... args)
+ {
+ construct_preamble();
+ construct_impl((value_type*) 0, node_->address(),
+ std::forward(args)...);
+ value_constructed_ = true;
+ }
+#else
+
+#define BOOST_UNORDERED_CONSTRUCT(z, num_params, _) \
+ template < \
+ BOOST_UNORDERED_TEMPLATE_ARGS(z, num_params) \
+ > \
+ void construct( \
+ BOOST_UNORDERED_FUNCTION_PARAMS(z, num_params) \
+ ) \
+ { \
+ construct_preamble(); \
+ construct_impl( \
+ (value_type*) 0, node_->address(), \
+ BOOST_UNORDERED_CALL_PARAMS(z, num_params) \
+ ); \
+ value_constructed_ = true; \
+ }
+
+ BOOST_PP_REPEAT_FROM_TO(1, BOOST_UNORDERED_EMPLACE_LIMIT,
+ BOOST_UNORDERED_CONSTRUCT, _)
+
+#undef BOOST_UNORDERED_CONSTRUCT
+
+#endif
+ template
+ void construct_pair(K const& k, M*)
+ {
+ construct_preamble();
+ new(node_->address()) value_type(k, M());
+ value_constructed_ = true;
+ }
+
+ value_type& value() const
+ {
+ BOOST_ASSERT(node_);
+ return node_->value();
+ }
+
+ // no throw
+ BOOST_DEDUCED_TYPENAME buckets::node_ptr release()
+ {
+ real_node_ptr p = node_;
+ node_ = real_node_ptr();
+ // node_ptr cast
+ return buckets_.bucket_alloc().address(*p);
+ }
+
+ private:
+ node_constructor(node_constructor const&);
+ node_constructor& operator=(node_constructor const&);
+ };
+
+ // node_constructor
+
+ template
+ inline node_constructor::~node_constructor()
+ {
+ if (node_) {
+ if (value_constructed_) {
+#if BOOST_WORKAROUND(__CODEGEARC__, BOOST_TESTED_AT(0x0613))
+ struct dummy { node x; };
+#endif
+ ::boost::unordered::detail::destroy(node_->value_ptr());
+ }
+
+ if (node_constructed_)
+ allocator_traits::destroy(buckets_.node_alloc(),
+ boost::addressof(*node_));
+
+ allocator_traits::deallocate(buckets_.node_alloc(), node_, 1);
+ }
+ }
+
+ template
+ inline void node_constructor::construct_preamble()
+ {
+ if(!node_) {
+ node_constructed_ = false;
+ value_constructed_ = false;
+
+ node_ = allocator_traits::allocate(buckets_.node_alloc(), 1);
+ allocator_traits::construct(buckets_.node_alloc(),
+ boost::addressof(*node_), node());
+ node_->init(buckets_.bucket_alloc().address(*node_));
+
+ node_constructed_ = true;
+ }
+ else {
+ BOOST_ASSERT(node_constructed_ && value_constructed_);
+ ::boost::unordered::detail::destroy(node_->value_ptr());
+ value_constructed_ = false;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // copy_buckets_to
+ //
+ // basic exception safety. If an exception is thrown this will
+ // leave dst partially filled and the buckets unset.
+
+ template
+ void buckets::copy_buckets_to(buckets& dst) const
+ {
+ BOOST_ASSERT(!dst.buckets_);
+
+ dst.create_buckets();
+ bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
+
+ {
+ node_constructor a(dst);
+
+ node_ptr n = this->buckets_[this->bucket_count_].next_;
+ node_ptr prev = dst_start;
+
+ while(n) {
+ std::size_t hash = node::get_hash(n);
+ node_ptr group_end = node::next_group(n);
+
+ a.construct(node::get_value(n));
+ node_ptr first_node = a.release();
+ node::set_hash(first_node, hash);
+ node_ptr end = prev->next_ = first_node;
+ ++dst.size_;
+
+ for(n = n->next_; n != group_end; n = n->next_) {
+ a.construct(node::get_value(n));
+ end = a.release();
+ node::set_hash(end, hash);
+ node::add_after_node(end, first_node);
+ ++dst.size_;
+ }
+
+ prev = dst.place_in_bucket(prev, end);
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // move_buckets_to
+ //
+ // Basic exception safety. The source nodes are left in an unusable state
+ // if an exception throws.
+
+ template
+ void buckets::move_buckets_to(buckets& dst) const
+ {
+ BOOST_ASSERT(!dst.buckets_);
+
+ dst.create_buckets();
+ bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
+
+ {
+ node_constructor a(dst);
+
+ node_ptr n = this->buckets_[this->bucket_count_].next_;
+ node_ptr prev = dst_start;
+
+ while(n) {
+ std::size_t hash = node::get_hash(n);
+ node_ptr group_end = node::next_group(n);
+
+ a.construct(boost::move(node::get_value(n)));
+ node_ptr first_node = a.release();
+ node::set_hash(first_node, hash);
+ node_ptr end = prev->next_ = first_node;
+ ++dst.size_;
+
+ for(n = n->next_; n != group_end; n = n->next_) {
+ a.construct(boost::move(node::get_value(n)));
+ end = a.release();
+ node::set_hash(end, hash);
+ node::add_after_node(end, first_node);
+ ++dst.size_;
+ }
+
+ prev = dst.place_in_bucket(prev, end);
+ }
+ }
+ }
+
+ // strong otherwise exception safety
+ template
+ void buckets::rehash_impl(std::size_t num_buckets)
+ {
+ BOOST_ASSERT(this->size_);
+
+ buckets dst(this->node_alloc(), num_buckets);
+ dst.create_buckets();
+
+ bucket_ptr src_start = this->get_bucket(this->bucket_count_);
+ bucket_ptr dst_start = dst.get_bucket(dst.bucket_count_);
+
+ dst_start->next_ = src_start->next_;
+ src_start->next_ = bucket_ptr();
+ dst.size_ = this->size_;
+ this->size_ = 0;
+
+ node_ptr prev = dst_start;
+ while (BOOST_UNORDERED_BORLAND_BOOL(prev->next_))
+ prev = dst.place_in_bucket(prev, node::next_group2(prev));
+
+ // Swap the new nodes back into the container and setup the
+ // variables.
+ dst.swap(*this); // no throw
+ }
+}}}
#endif
diff --git a/include/boost/unordered/detail/equivalent.hpp b/include/boost/unordered/detail/equivalent.hpp
index 1c497c32..66183a67 100644
--- a/include/boost/unordered/detail/equivalent.hpp
+++ b/include/boost/unordered/detail/equivalent.hpp
@@ -1,19 +1,18 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
-// Copyright (C) 2005-2009 Daniel James
+// 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_EQUIVALENT_HPP_INCLUDED
#define BOOST_UNORDERED_DETAIL_EQUIVALENT_HPP_INCLUDED
-#include
#include
-namespace boost { namespace unordered_detail {
+namespace boost { namespace unordered { namespace detail {
template
- class hash_equivalent_table : public T::table
+ class equivalent_table : public T::table_base
{
public:
typedef BOOST_DEDUCED_TYPENAME T::hasher hasher;
@@ -21,54 +20,212 @@ namespace boost { namespace unordered_detail {
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::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::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)
- : table(x, x.node_alloc()) {}
- hash_equivalent_table(hash_equivalent_table const& x,
+ : table_base(n, hf, eq, a) {}
+ equivalent_table(equivalent_table const& x)
+ : table_base(x,
+ allocator_traits::
+ select_on_container_copy_construction(x.node_alloc())) {}
+ equivalent_table(equivalent_table const& x,
value_allocator const& a)
- : table(x, a) {}
- hash_equivalent_table(hash_equivalent_table& x, move_tag m)
- : table(x, m) {}
- hash_equivalent_table(hash_equivalent_table& x,
+ : table_base(x, a) {}
+ equivalent_table(equivalent_table& x, move_tag m)
+ : table_base(x, m) {}
+ equivalent_table(equivalent_table& x,
value_allocator const& a, move_tag m)
- : table(x, a, m) {}
- ~hash_equivalent_table() {}
+ : table_base(x, a, m) {}
+ ~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);
+ if (!group_equals(n1, end1, n2, end2)) return false;
+ n1 = end1;
+ }
+
+ return true;
+ }
+
+ static bool group_equals(node_ptr n1, node_ptr end1,
+ node_ptr n2, node_ptr end2)
+ {
+ for(;;)
+ {
+ if (node::get_value(n1) != node::get_value(n2))
+ break;
+
+ n1 = n1->next_;
+ n2 = n2->next_;
+
+ if (n1 == end1) return n2 == end2;
+ if (n2 == end2) return false;
+ }
+
+ for(node_ptr n1a = n1, n2a = n2;;)
+ {
+ n1a = n1a->next_;
+ n2a = n2a->next_;
+
+ if (n1a == end1)
+ {
+ if (n2a == end2) break;
+ else return false;
+ }
+ if (n2a == end2) return false;
+ }
+
+ node_ptr start = n1;
+ for(;n1 != end2; n1 = n1->next_)
+ {
+ value_type const& v = node::get_value(n1);
+ if (find(start, n1, v)) continue;
+ std::size_t matches = count_equal(n2, end2, v);
+ if (!matches || matches != 1 + count_equal(n1->next_, end1, v))
+ return false;
+ }
+
+ return true;
+ }
+
+ static bool find(node_ptr n, node_ptr end, value_type const& v)
+ {
+ for(;n != end; n = n->next_)
+ if (node::get_value(n) == v)
+ return true;
+ return false;
+ }
+
+ static std::size_t count_equal(node_ptr n, node_ptr end, value_type const& v)
+ {
+ std::size_t count = 0;
+ for(;n != end; n = n->next_)
+ if (node::get_value(n) == v) ++count;
+ return count;
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // 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
+ 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));
+ }
- bool equals(hash_equivalent_table const&) const;
-
- inline node_ptr add_node(node_constructor& a,
- bucket_ptr bucket, node_ptr pos);
-
-#if defined(BOOST_UNORDERED_STD_FORWARD)
+#if defined(BOOST_UNORDERED_STD_FORWARD_MOVE)
template
- 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)...);
+
+ return emplace_impl(a);
+ }
#else
-#define BOOST_UNORDERED_INSERT_IMPL(z, n, _) \
- template \
- iterator_base emplace(BOOST_UNORDERED_FUNCTION_PARAMS(z, n));
+#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
+ template \
+ 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,229 +233,75 @@ 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
- 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
- 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
- void insert_range(I i, I j);
+ void insert_range(I i, I j)
+ {
+ BOOST_DEDUCED_TYPENAME ::boost::iterator_traversal::type
+ iterator_traversal_tag;
+ insert_for_range(i, j, iterator_traversal_tag);
+ }
+
};
template
struct multiset : public types<
- BOOST_DEDUCED_TYPENAME A::value_type,
- BOOST_DEDUCED_TYPENAME A::value_type,
+ BOOST_DEDUCED_TYPENAME allocator_traits::value_type,
+ BOOST_DEDUCED_TYPENAME allocator_traits::value_type,
H, P, A,
- set_extractor,
- grouped>
+ set_extractor::value_type>,
+ false>
{
- typedef hash_equivalent_table > impl;
- typedef hash_table > table;
+ typedef equivalent_table > impl;
+ typedef table > table_base;
};
template
struct multimap : public types<
- K, BOOST_DEDUCED_TYPENAME A::value_type,
+ K, BOOST_DEDUCED_TYPENAME allocator_traits::value_type,
H, P, A,
- map_extractor,
- grouped>
+ map_extractor::value_type>,
+ false>
{
- typedef hash_equivalent_table > impl;
- typedef hash_table > table;
+ typedef equivalent_table > impl;
+ typedef table > table_base;
};
-
- ////////////////////////////////////////////////////////////////////////////
- // Equality
-
- template
- bool hash_equivalent_table
- ::equals(hash_equivalent_table 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
- inline BOOST_DEDUCED_TYPENAME hash_equivalent_table::node_ptr
- hash_equivalent_table
- ::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
- inline BOOST_DEDUCED_TYPENAME
- hash_equivalent_table::iterator_base
- hash_equivalent_table::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
- inline void hash_equivalent_table
- ::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
- template
- BOOST_DEDUCED_TYPENAME hash_equivalent_table::iterator_base
- hash_equivalent_table
- ::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)...);
-
- return emplace_impl(a);
- }
-
-#else
-
-#define BOOST_UNORDERED_INSERT_IMPL(z, num_params, _) \
- template \
- template