Merge pull request #112 from boostorg/feature/fca-unordered

Update internal implementation to use FCA
This commit is contained in:
Christian Mazakas
2022-06-01 15:44:05 -07:00
committed by GitHub
71 changed files with 3294 additions and 2421 deletions

View File

@ -157,10 +157,6 @@ jobs:
cxxstd: 14,latest
addrmd: 32,64
os: windows-2019
- toolset: msvc-14.1
cxxstd: "14,17,latest"
addrmd: 32,64
os: windows-2016
- toolset: msvc-14.2
cxxstd: "14,17,20,latest"
addrmd: 32,64

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
doc/diagrams/fca.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

188
doc/roadmap.md Normal file
View File

@ -0,0 +1,188 @@
# Refactoring Roadmap
[Proof of concept](https://github.com/joaquintides/fca_unordered) implementation for a fast closed-addressing implementation.
## Plan of Refactoring
* remove `ptr_node` and `ptr_bucket`
* see if the code can survive a lack of the `extra_node` or maybe we hard-code it in
* implement bucket groups as they are in `fca` but don't use them directly yet, add alongside the `buckets_` data member in `struct table`
* try to remove `bucket_info_` from the node structure (breaks all call-sites that use `get_bucket()` and dependents)
* make sure `fca` can successfully handle multi-variants at this stage + supports mutable iterators for `map`/`multimap`
* do a hard-break:
* update code to no longer use one single linked list across all buckets (each bucket contains its own unique list)
* integrate the `bucket_group<Node>` structure into the `table` (update iterator call-sites to include `bucket_iterator`s)
Blockers:
* how to handle `multi` variants with new `fca` prototype
## Implementation Differences
### Unordered
### Node Type
Bullet Points:
* reify node type into a single one
* come up with implementation for multi- variants
* code that touches `get_bucket()` and `*_in_group()` member functions may need updating
There are two node types in Unordered, `struct node` and `struct ptr_node`, and the node type is selected conditionally based on the Allocator's pointer type:
```c++
template <typename A, typename T, typename NodePtr, typename BucketPtr>
struct pick_node2
{
typedef boost::unordered::detail::node<A, T> node;
// ...
};
template <typename A, typename T>
struct pick_node2<A, T, boost::unordered::detail::ptr_node<T>*,
boost::unordered::detail::ptr_bucket*>
{
typedef boost::unordered::detail::ptr_node<T> node;
// ...
};
template <typename A, typename T> struct pick_node
{
typedef typename boost::remove_const<T>::type nonconst;
typedef boost::unordered::detail::allocator_traits<
typename boost::unordered::detail::rebind_wrap<A,
boost::unordered::detail::ptr_node<nonconst> >::type>
tentative_node_traits;
typedef boost::unordered::detail::allocator_traits<
typename boost::unordered::detail::rebind_wrap<A,
boost::unordered::detail::ptr_bucket>::type>
tentative_bucket_traits;
typedef pick_node2<A, nonconst, typename tentative_node_traits::pointer,
typename tentative_bucket_traits::pointer>
pick;
typedef typename pick::node node;
typedef typename pick::bucket bucket;
typedef typename pick::link_pointer link_pointer;
};
```
The node types are identical in terms of interface and the only difference is that `node` is chosen when the Allocator uses fancy pointers and `ptr_node` is chosen when the Allocator's pointer type is `T*`.
Nodes in Unorderd store `bucket_info_`:
```cpp
template <typename A, typename T>
struct node : boost::unordered::detail::value_base<T>
{
link_pointer next_;
std::size_t bucket_info_;
node() : next_(), bucket_info_(0) {}
// ...
};
```
`bucket_info_` maps each node back to its corresponding bucket via the member function:
```cpp
std::size_t get_bucket() const
{
return bucket_info_ & ((std::size_t)-1 >> 1);
}
```
`bucket_info_` is also used to demarcate the start of equivalent nodes in the containers via:
```cpp
// Note that nodes start out as the first in their group, as `bucket_info_` defaults to 0.
std::size_t is_first_in_group() const
{ return !(bucket_info_ & ~((std::size_t)-1 >> 1)); }
void set_first_in_group()
{ bucket_info_ = bucket_info_ & ((std::size_t)-1 >> 1); }
void reset_first_in_group()
{ bucket_info_ = bucket_info_ | ~((std::size_t)-1 >> 1); }
```
A goal of refactoring is to simply have one node type:
```cpp
template<class T>
struct node {
node *next;
T value;
};
```
that is used unconditionally. This also requires updating the code that touches the `bucket_info_` along with the code that that touches the `*_in_group()` member functions.
### Bucket Type
Bullet points:
* reify bucket structure into a single one
* figure out how to add `bucket_group`s to the table struct
Buckets are similar to nodes in that there are two variations: `template<class NodePointer> struct bucket` and `struct ptr_bucket`.
The buckets exist to contain a pointer to a node, however they contain an `enum { extra_node = true };` or `enum { extra_node = false }` to determine whether or not the code should explicitly allocate a default constructed node whose address assigned as the dummy node at the end of the bucket array.
`extra_node` is used in the creation and deletion of the bucket array but it is not inherently clear what its intended purpose is.
### Iterators
Iterators are currently templated on the type of Node they store. Because `fca` constructs iterators with two arguments, all the call-sites that instantiate iterators will need to be updated but this a straight-forward mechanical change.
Iterators are selected, as of now, via the `detail::map` and `detail::set` class templates.
For example, for `unordered_map`, `iterator` is defined as:
```cpp
typedef boost::unordered::detail::map<A, K, T, H, P> types;
typedef typename types::table table;
typedef typename table::iterator iterator;
```
The iterator is a member typedef of the `table` which is `types::table`. Examining `types` (aka `detail::map<...>`), we see:
```cpp
template <typename A, typename K, typename M, typename H, typename P>
struct map {
// ...
typedef boost::unordered::detail::table<types> table;
// ...
};
```
Examining the `detail::table<types>` struct, we see:
```cpp
template <typename Types>
struct table {
// ...
typedef typename Types::iterator iterator;
// ...
}
```
Collapsing all of this, we see that our iterator types are defined here:
```cpp
template <typename A, typename K, typename M, typename H, typename P>
struct map
{
// ...
typedef boost::unordered::detail::pick_node<A, value_type> pick;
typedef typename pick::node node;
typedef boost::unordered::iterator_detail::iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
// ...
};
```
This is similarly designed for `detail::set`:
```cpp
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
```
The only difference here is that `set::iterator` is always a `c_iterator`, a `const_iterator` type.

View File

@ -1,5 +1,6 @@
[#buckets]
:idprefix: buckets_
:imagesdir: ../diagrams
= The Data Structure
@ -8,7 +9,7 @@ any number of elements. For example, the following diagram shows an <<unordered_
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
have more buckets).
image::../diagrams/buckets.png[]
image::buckets.png[]
In order to decide which bucket to place an element in, the container applies
the hash function, `Hash`, to the element's key (for `unordered_set` and
@ -150,3 +151,334 @@ x.rehash(std::ceil(n / x.max_load_factor()))
+
See the <<unordered_map_rehash,reference for more details>> on the `rehash` function.
== Fast Closed Addressing Implementation
++++
<style>
.imageblock > .title {
text-align: inherit;
}
</style>
++++
Boost.Unordered sports one of the fastest implementations of closed addressing, also commonly known as https://en.wikipedia.org/wiki/Hash_table#Separate_chaining[separate chaining]. An example figure representing the data structure is below:
[#img-bucket-groups,.text-center]
.A simple bucket group approach
image::bucket-groups.png[align=center]
An array of "buckets" is allocated and each bucket in turn points to its own individual linked list. This makes meeting the standard requirements of bucket iteration straight-forward. Unfortunately, iteration of the entire container is often times slow using this layout as each bucket must be examined for occupancy, yielding a time complexity of `O(bucket_count() + size())` when the standard requires complexity to be `O(size())`.
Canonical standard implementations will wind up looking like the diagram below:
[.text-center]
.The canonical standard approach
image::singly-linked.png[align=center,link=../diagrams/singly-linked.png,window=_blank]
It's worth noting that this approach is only used by pass:[libc++] and pass:[libstdc++]; the MSVC Dinkumware implementation uses a different one. A more detailed analysis of the standard containers can be found http://bannalia.blogspot.com/2013/10/implementation-of-c-unordered.html[here].
This unusually laid out data structure is chosen to make iteration of the entire container efficient by inter-connecting all of the nodes into a singly-linked list. One might also notice that buckets point to the node _before_ the start of the bucket's elements. This is done so that removing elements from the list can be done efficiently without introducing the need for a doubly-linked list. Unfortunately, this data structure introduces a guaranteed extra indirection. For example, to access the first element of a bucket, something like this must be done:
```c++
auto const idx = get_bucket_idx(hash_function(key));
node* p = buckets[idx]; // first load
node* n = p->next; // second load
if (n && is_in_bucket(n, idx)) {
value_type const& v = *n; // third load
// ...
}
```
With a simple bucket group layout, this is all that must be done:
```c++
auto const idx = get_bucket_idx(hash_function(key));
node* n = buckets[idx]; // first load
if (n) {
value_type const& v = *n; // second load
// ...
}
```
In practice, the extra indirection can have a dramatic performance impact to common operations such as `insert`, `find` and `erase`. But to keep iteration of the container fast, Boost.Unordered introduces a novel data structure, a "bucket group". A bucket group is a fixed-width view of a subsection of the buckets array. It contains a bitmask (a `std::size_t`) which it uses to track occupancy of buckets and contains two pointers so that it can form a doubly-linked list with non-empty groups. An example diagram is below:
[#img-fca-layout]
.The new layout used by Boost
image::fca.png[align=center]
Thus container-wide iteration is turned into traversing the non-empty bucket groups (an operation with constant time complexity) which reduces the time complexity back to `O(size())`. In total, a bucket group is only 4 words in size and it views `sizeof(std::size_t) * CHAR_BIT` buckets meaning that for all common implementations, there's only 4 bits of space overhead per bucket introduced by the bucket groups.
For more information on implementation rationale, read the <<Implementation Rationale, corresponding section>>.
= Benchmarks
All benchmarks were created using `unordered_set<unsigned int>` (non-duplicate) and `unordered_multiset<unsigned int>` (duplicate). The source code can be https://github.com/joaquintides/boost_unordered_benchmark[found here].
The insertion benchmarks insert `n` random values, where `n` is between 10,000 and 3 million. For the duplicated benchmarks, the same random values are repeated an average of 5 times.
The erasure benchmarks erase all `n` elements randomly until the container is empty.
The successful lookup benchmarks are done by looking up all `n` values, in the their original insertion order.
The unsuccessful lookup benchmarks use `n` randomly generated integers but using a different seed value.
== GCC 11 + libstdc++-v3
=== Insertion
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/gcc/running insertion.xlsx.practice.png[width=250,link=../diagrams/benchmarks/gcc/running insertion.xlsx.practice.png,window=_blank]
|image::benchmarks/gcc/running%20insertion.xlsx.practice non-unique.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice non-unique.png,window=_blank]
|image::benchmarks/gcc/running%20insertion.xlsx.practice non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice non-unique 5.png,window=_blank]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements +
max load factor 5
|===
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash.png,window=_blank]
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique.png,window=_blank]
|image::benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/running%20insertion.xlsx.practice norehash non-unique 5.png,window=_blank]
h|non-duplicate elements, +
prior `reserve`
h|duplicate elements, +
prior `reserve`
h|duplicate elements, +
max load factor 5, +
prior `reserve`
|===
=== Erasure
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice.png,window=_blank]
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique.png,window=_blank]
|image::benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,link=../diagrams/benchmarks/gcc/scattered%20erasure.xlsx.practice non-unique 5.png,window=_blank]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements +
max load factor 5
|===
=== Successful Lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice.png]
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
=== Unsuccessful lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/gcc/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
== Clang 12 + libc++
=== Insertion
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice.png]
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique.png]
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique 5.png[width=250, window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash.png]
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique.png]
|image::benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/running%20insertion.xlsx.practice norehash non-unique 5.png]
h|non-duplicate elements, +
prior `reserve`
h|duplicate elements, +
prior `reserve`
h|duplicate elements, +
max load factor 5, +
prior `reserve`
|===
=== Erasure
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice.png]
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique.png]
|image::benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20erasure.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
=== Successful lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice.png]
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
=== Unsuccessful lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/clang_libcpp/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
== Visual Studio 2019 + Dinkumware
=== Insertion
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/vs/running%20insertion.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice.png]
|image::benchmarks/vs/running%20insertion.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice non-unique.png]
|image::benchmarks/vs/running%20insertion.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash.png]
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique.png]
|image::benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/running%20insertion.xlsx.practice norehash non-unique 5.png]
h|non-duplicate elements, +
prior `reserve`
h|duplicate elements, +
prior `reserve`
h|duplicate elements, +
max load factor 5, +
prior `reserve`
|===
=== Erasure
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/vs/scattered%20erasure.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice.png]
|image::benchmarks/vs/scattered%20erasure.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice non-unique.png]
|image::benchmarks/vs/scattered%20erasure.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20erasure.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
=== Successful lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice.png]
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/benchmarks/vs/scattered%20successful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===
=== Unsuccessful lookup
[caption=]
[cols="3*^.^a", frame=all, grid=all]
|===
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice.png]
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique.png]
|image::benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png[width=250,window=_blank,link=../diagrams/benchmarks/vs/scattered%20unsuccessful%20looukp.xlsx.practice non-unique 5.png]
h|non-duplicate elements
h|duplicate elements
h|duplicate elements, +
max load factor 5
|===

View File

@ -6,6 +6,12 @@
:github-pr-url: https://github.com/boostorg/unordered/pull
:cpp: C++
== Release 1.80.0
* Refactor internal implementation to be dramatically faster
* Fix long-standing bug where `final`-qualified Hasher and KeyEqual couldn't be
used
== Release 1.79.0
* Improved {cpp}20 support:

View File

@ -66,3 +66,7 @@ by using `(h * m) >> (w - k)`, where `h` is the hash value, `m` is the golden
ratio multiplied by `2^w`, `w` is the word size (32 or 64), and `2^k` is the
number of buckets. This provides a good compromise between speed and
distribution.
Since release 1.80.0, prime numbers are chosen for the number of buckets in
tandem with sophisticated modulo arithmetic. This removes the need for "mixing"
the result of the user's hash function as was used for release 1.79.0.

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
// Copyright (C) 2008-2016 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -31,7 +32,7 @@
// 2012 = VC+11 = BOOST_MSVC 1700 Hopefully!
// I have no idea when Dinkumware added it, probably a lot
// earlier than this check.
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 50, 0) || \
#if BOOST_LIB_STD_DINKUMWARE >= BOOST_VERSION_NUMBER(6, 10, 0) || \
BOOST_COMP_MSVC >= BOOST_VERSION_NUMBER(17, 0, 0)
#define BOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT 1
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -25,25 +26,18 @@ namespace boost {
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::pick_node<A, value_type> pick;
typedef typename pick::node node;
typedef typename pick::bucket bucket;
typedef typename pick::link_pointer link_pointer;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::map_extractor<value_type> extractor;
typedef typename boost::unordered::detail::pick_policy<K>::type policy;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::iterator_detail::iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::l_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
typedef boost::unordered::node_handle_map<
node<value_type, void_pointer>, K, M, A>
node_type;
typedef boost::unordered::node_handle_map<node, K, M, A> node_type;
typedef boost::unordered::insert_return_type_map<node, K, M, A>
insert_return_type;
typedef typename table::iterator iterator;
typedef boost::unordered::insert_return_type_map<iterator, node_type> insert_return_type;
};
template <typename K, typename M, typename H, typename P, typename A>

View File

@ -1,5 +1,6 @@
// Copyright (C) 2005-2016 Daniel James
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -24,24 +25,18 @@ namespace boost {
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef boost::unordered::detail::pick_node<A, value_type> pick;
typedef typename pick::node node;
typedef typename pick::bucket bucket;
typedef typename pick::link_pointer link_pointer;
typedef boost::unordered::detail::table<types> table;
typedef boost::unordered::detail::set_extractor<value_type> extractor;
typedef typename boost::unordered::detail::pick_policy<T>::type policy;
typedef typename boost::allocator_void_pointer<value_allocator>::type
void_pointer;
typedef boost::unordered::iterator_detail::c_iterator<node> iterator;
typedef boost::unordered::iterator_detail::c_iterator<node> c_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node> l_iterator;
typedef boost::unordered::iterator_detail::cl_iterator<node>
cl_iterator;
typedef boost::unordered::node_handle_set<
node<value_type, void_pointer>, T, A>
node_type;
typedef boost::unordered::node_handle_set<node, T, A> node_type;
typedef boost::unordered::insert_return_type_set<node, T, A>
typedef typename table::c_iterator iterator;
typedef boost::unordered::insert_return_type_set<iterator, node_type>
insert_return_type;
};

View File

@ -1,6 +1,7 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -57,8 +58,6 @@ namespace boost {
typedef boost::unordered::detail::map<A, K, T, H, P> types;
typedef typename types::value_allocator_traits value_allocator_traits;
typedef typename types::table table;
typedef typename table::node_pointer node_pointer;
typedef typename table::link_pointer link_pointer;
public:
typedef typename value_allocator_traits::pointer pointer;
@ -97,7 +96,7 @@ namespace boost {
unordered_map(unordered_map const&);
#if defined(BOOST_UNORDERED_USE_MOVE) || \
#if defined(BOOST_UNORDERED_USE_MOVE) || \
!defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
unordered_map(BOOST_RV_REF(unordered_map) other)
BOOST_NOEXCEPT_IF(table::nothrow_move_constructible)
@ -188,9 +187,9 @@ namespace boost {
return table_.node_alloc();
}
// iterators
// // iterators
iterator begin() BOOST_NOEXCEPT { return iterator(table_.begin()); }
iterator begin() BOOST_NOEXCEPT { return table_.begin(); }
const_iterator begin() const BOOST_NOEXCEPT
{
@ -445,7 +444,7 @@ namespace boost {
insert_return_type insert(BOOST_RV_REF(node_type) np)
{
insert_return_type result;
table_.move_insert_node_type_unique(np, result);
table_.move_insert_node_type_unique((node_type&)np, result);
return boost::move(result);
}
@ -728,8 +727,7 @@ namespace boost {
size_type>::type
erase(BOOST_FWD_REF(Key) k)
{
return table_.erase_key_unique_impl(
this->key_eq(), boost::forward<Key>(k));
return table_.erase_key_unique_impl(boost::forward<Key>(k));
}
BOOST_UNORDERED_DEPRECATED("Use erase instead")
@ -774,9 +772,7 @@ namespace boost {
iterator>::type
find(const Key& key)
{
return iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq()));
return table_.find(key);
}
template <class Key>
@ -784,9 +780,7 @@ namespace boost {
const_iterator>::type
find(const Key& key) const
{
return const_iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq()));
return const_iterator(table_.find(key));
}
template <class CompatibleKey, class CompatibleHash,
@ -801,9 +795,7 @@ namespace boost {
bool contains(const key_type& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
template <class Key>
@ -811,9 +803,7 @@ namespace boost {
bool>::type
contains(const Key& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
size_type count(const key_type&) const;
@ -823,14 +813,7 @@ namespace boost {
size_type>::type
count(const Key& k) const
{
std::size_t const key_hash =
table::policy::apply_hash(this->hash_function(), k);
P const& eq = this->key_eq();
node_pointer p = table_.find_node_impl(key_hash, k, eq);
return (p ? 1 : 0);
return (table_.find(k) != this->end() ? 1 : 0);
}
std::pair<iterator, iterator> equal_range(const key_type&);
@ -842,12 +825,13 @@ namespace boost {
std::pair<iterator, iterator> >::type
equal_range(const Key& key)
{
node_pointer p = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq());
iterator first = table_.find(key);
iterator last = first;
if (last != this->end()) {
++last;
}
return std::make_pair(
iterator(p), iterator(p ? table::next_node(p) : p));
return std::make_pair(first, last);
}
template <class Key>
@ -855,12 +839,13 @@ namespace boost {
std::pair<const_iterator, const_iterator> >::type
equal_range(const Key& key) const
{
node_pointer p = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq());
iterator first = table_.find(key);
iterator last = first;
if (last != this->end()) {
++last;
}
return std::make_pair(
const_iterator(p), const_iterator(p ? table::next_node(p) : p));
return std::make_pair(first, last);
}
mapped_type& operator[](const key_type&);
@ -872,7 +857,7 @@ namespace boost {
size_type bucket_count() const BOOST_NOEXCEPT
{
return table_.bucket_count_;
return table_.bucket_count();
}
size_type max_bucket_count() const BOOST_NOEXCEPT
@ -889,16 +874,15 @@ namespace boost {
local_iterator begin(size_type n)
{
return local_iterator(table_.begin(n), n, table_.bucket_count_);
return table_.begin(n);
}
const_local_iterator begin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
local_iterator end(size_type) { return local_iterator(); }
const_local_iterator end(size_type) const
{
return const_local_iterator();
@ -906,7 +890,7 @@ namespace boost {
const_local_iterator cbegin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
const_local_iterator cend(size_type) const
@ -1026,8 +1010,6 @@ namespace boost {
typedef boost::unordered::detail::map<A, K, T, H, P> types;
typedef typename types::value_allocator_traits value_allocator_traits;
typedef typename types::table table;
typedef typename table::node_pointer node_pointer;
typedef typename table::link_pointer link_pointer;
public:
typedef typename value_allocator_traits::pointer pointer;
@ -1394,7 +1376,7 @@ namespace boost {
node_type extract(const key_type& k)
{
return node_type(table_.extract_by_key(k), table_.node_alloc());
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
}
template <class Key>
@ -1437,8 +1419,7 @@ namespace boost {
size_type>::type
erase(BOOST_FWD_REF(Key) k)
{
return table_.erase_key_equiv_impl(
this->key_eq(), boost::forward<Key>(k));
return table_.erase_key_equiv_impl(boost::forward<Key>(k));
}
BOOST_UNORDERED_DEPRECATED("Use erase instead")
@ -1483,9 +1464,7 @@ namespace boost {
iterator>::type
find(const Key& key)
{
return iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq()));
return table_.find(key);
}
template <class Key>
@ -1493,9 +1472,7 @@ namespace boost {
const_iterator>::type
find(const Key& key) const
{
return const_iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq()));
return const_iterator(table_.find(key));
}
template <class CompatibleKey, class CompatibleHash,
@ -1510,9 +1487,7 @@ namespace boost {
bool contains(key_type const& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
template <class Key>
@ -1520,9 +1495,7 @@ namespace boost {
bool>::type
contains(const Key& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
size_type count(const key_type&) const;
@ -1532,11 +1505,7 @@ namespace boost {
size_type>::type
count(const Key& k) const
{
node_pointer n = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return n ? table_.group_count(n) : 0;
return table_.group_count(k);
}
std::pair<iterator, iterator> equal_range(const key_type&);
@ -1548,12 +1517,8 @@ namespace boost {
std::pair<iterator, iterator> >::type
equal_range(const Key& key)
{
node_pointer p = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq());
return std::make_pair(
iterator(p), iterator(p ? table_.next_group(p) : p));
iterator p = table_.find(key);
return std::make_pair(p, table_.next_group(key, p));
}
template <class Key>
@ -1561,19 +1526,16 @@ namespace boost {
std::pair<const_iterator, const_iterator> >::type
equal_range(const Key& key) const
{
node_pointer p = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), key), key,
this->key_eq());
iterator p = table_.find(key);
return std::make_pair(
const_iterator(p), const_iterator(p ? table_.next_group(p) : p));
const_iterator(p), const_iterator(table_.next_group(key, p)));
}
// bucket interface
size_type bucket_count() const BOOST_NOEXCEPT
{
return table_.bucket_count_;
return table_.bucket_count();
}
size_type max_bucket_count() const BOOST_NOEXCEPT
@ -1590,12 +1552,12 @@ namespace boost {
local_iterator begin(size_type n)
{
return local_iterator(table_.begin(n), n, table_.bucket_count_);
return local_iterator(table_.begin(n));
}
const_local_iterator begin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
local_iterator end(size_type) { return local_iterator(); }
@ -1607,7 +1569,7 @@ namespace boost {
const_local_iterator cbegin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
const_local_iterator cend(size_type) const
@ -1730,7 +1692,7 @@ namespace boost {
unordered_map::value_allocator_traits::
select_on_container_copy_construction(other.get_allocator()))
{
if (other.table_.size_) {
if (other.size()) {
table_.copy_buckets(
other.table_, boost::unordered::detail::true_type());
}
@ -1894,29 +1856,25 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::iterator
unordered_map<K, T, H, P, A>::erase(iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_unique(node, next);
return iterator(next);
const_iterator last = position;
++last;
return table_.erase_nodes_range(position, last);
}
template <class K, class T, class H, class P, class A>
typename unordered_map<K, T, H, P, A>::iterator
unordered_map<K, T, H, P, A>::erase(const_iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_unique(node, next);
return iterator(next);
const_iterator last = position;
++last;
return table_.erase_nodes_range(position, last);
}
template <class K, class T, class H, class P, class A>
typename unordered_map<K, T, H, P, A>::size_type
unordered_map<K, T, H, P, A>::erase(const key_type& k)
{
return table_.erase_key_unique_impl(this->key_eq(), k);
return table_.erase_key_unique_impl(k);
}
template <class K, class T, class H, class P, class A>
@ -1924,11 +1882,7 @@ namespace boost {
unordered_map<K, T, H, P, A>::erase(
const_iterator first, const_iterator last)
{
node_pointer last_node = table::get_node(last);
if (first == last)
return iterator(last_node);
table_.erase_nodes_unique(table::get_node(first), last_node);
return iterator(last_node);
return table_.erase_nodes_range(first, last);
}
template <class K, class T, class H, class P, class A>
@ -1998,14 +1952,14 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::iterator
unordered_map<K, T, H, P, A>::find(const key_type& k)
{
return iterator(table_.find_node(k));
return iterator(table_.find(k));
}
template <class K, class T, class H, class P, class A>
typename unordered_map<K, T, H, P, A>::const_iterator
unordered_map<K, T, H, P, A>::find(const key_type& k) const
{
return const_iterator(table_.find_node(k));
return const_iterator(table_.find(k));
}
template <class K, class T, class H, class P, class A>
@ -2015,8 +1969,7 @@ namespace boost {
unordered_map<K, T, H, P, A>::find(CompatibleKey const& k,
CompatibleHash const& hash, CompatiblePredicate const& eq)
{
return iterator(
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
return table_.transparent_find(k, hash, eq);
}
template <class K, class T, class H, class P, class A>
@ -2026,8 +1979,7 @@ namespace boost {
unordered_map<K, T, H, P, A>::find(CompatibleKey const& k,
CompatibleHash const& hash, CompatiblePredicate const& eq) const
{
return const_iterator(
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
return table_.transparent_find(k, hash, eq);
}
template <class K, class T, class H, class P, class A>
@ -2042,8 +1994,12 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::iterator>
unordered_map<K, T, H, P, A>::equal_range(const key_type& k)
{
node_pointer n = table_.find_node(k);
return std::make_pair(iterator(n), iterator(n ? table::next_node(n) : n));
iterator first = table_.find(k);
iterator second = first;
if (second != this->end()) {
++second;
}
return std::make_pair(first, second);
}
template <class K, class T, class H, class P, class A>
@ -2051,9 +2007,12 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::const_iterator>
unordered_map<K, T, H, P, A>::equal_range(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return std::make_pair(
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
iterator first = table_.find(k);
iterator second = first;
if (second != this->end()) {
++second;
}
return std::make_pair(const_iterator(first), const_iterator(second));
}
template <class K, class T, class H, class P, class A>
@ -2074,10 +2033,12 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::mapped_type&
unordered_map<K, T, H, P, A>::at(const key_type& k)
{
typedef typename table::node_pointer node_pointer;
if (table_.size_) {
node_pointer n = table_.find_node(k);
if (n)
return n->value().second;
node_pointer p = table_.find_node(k);
if (p)
return p->value().second;
}
boost::throw_exception(
@ -2088,10 +2049,12 @@ namespace boost {
typename unordered_map<K, T, H, P, A>::mapped_type const&
unordered_map<K, T, H, P, A>::at(const key_type& k) const
{
typedef typename table::node_pointer node_pointer;
if (table_.size_) {
node_pointer n = table_.find_node(k);
if (n)
return n->value().second;
node_pointer p = table_.find_node(k);
if (p)
return p->value().second;
}
boost::throw_exception(
@ -2110,9 +2073,9 @@ namespace boost {
template <class K, class T, class H, class P, class A>
float unordered_map<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
BOOST_ASSERT(table_.bucket_count_ != 0);
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count_);
static_cast<float>(table_.bucket_count());
}
template <class K, class T, class H, class P, class A>
@ -2130,8 +2093,7 @@ namespace boost {
template <class K, class T, class H, class P, class A>
void unordered_map<K, T, H, P, A>::reserve(size_type n)
{
table_.rehash(static_cast<std::size_t>(
std::ceil(static_cast<double>(n) / table_.mlf_)));
table_.reserve(n);
}
template <class K, class T, class H, class P, class A>
@ -2377,22 +2339,20 @@ namespace boost {
typename unordered_multimap<K, T, H, P, A>::iterator
unordered_multimap<K, T, H, P, A>::erase(iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_equiv(node, next);
return iterator(next);
BOOST_ASSERT(position != this->end());
iterator next = position;
++next;
return table_.erase_nodes_range(position, next);
}
template <class K, class T, class H, class P, class A>
typename unordered_multimap<K, T, H, P, A>::iterator
unordered_multimap<K, T, H, P, A>::erase(const_iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_equiv(node, next);
return iterator(next);
BOOST_ASSERT(position != this->end());
const_iterator next = position;
++next;
return table_.erase_nodes_range(position, next);
}
template <class K, class T, class H, class P, class A>
@ -2407,11 +2367,7 @@ namespace boost {
unordered_multimap<K, T, H, P, A>::erase(
const_iterator first, const_iterator last)
{
node_pointer last_node = table::get_node(last);
if (first == last)
return iterator(last_node);
table_.erase_nodes_equiv(table::get_node(first), last_node);
return iterator(last_node);
return table_.erase_nodes_range(first, last);
}
template <class K, class T, class H, class P, class A>
@ -2489,14 +2445,14 @@ namespace boost {
typename unordered_multimap<K, T, H, P, A>::iterator
unordered_multimap<K, T, H, P, A>::find(const key_type& k)
{
return iterator(table_.find_node(k));
return iterator(table_.find(k));
}
template <class K, class T, class H, class P, class A>
typename unordered_multimap<K, T, H, P, A>::const_iterator
unordered_multimap<K, T, H, P, A>::find(const key_type& k) const
{
return const_iterator(table_.find_node(k));
return const_iterator(table_.find(k));
}
template <class K, class T, class H, class P, class A>
@ -2506,8 +2462,7 @@ namespace boost {
unordered_multimap<K, T, H, P, A>::find(CompatibleKey const& k,
CompatibleHash const& hash, CompatiblePredicate const& eq)
{
return iterator(
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
return table_.transparent_find(k, hash, eq);
}
template <class K, class T, class H, class P, class A>
@ -2525,8 +2480,7 @@ namespace boost {
typename unordered_multimap<K, T, H, P, A>::size_type
unordered_multimap<K, T, H, P, A>::count(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return n ? table_.group_count(n) : 0;
return table_.group_count(k);
}
template <class K, class T, class H, class P, class A>
@ -2534,9 +2488,8 @@ namespace boost {
typename unordered_multimap<K, T, H, P, A>::iterator>
unordered_multimap<K, T, H, P, A>::equal_range(const key_type& k)
{
node_pointer n = table_.find_node(k);
return std::make_pair(
iterator(n), iterator(n ? table_.next_group(n) : n));
iterator n = table_.find(k);
return std::make_pair(n, (n == end() ? n : table_.next_group(k, n)));
}
template <class K, class T, class H, class P, class A>
@ -2544,9 +2497,9 @@ namespace boost {
typename unordered_multimap<K, T, H, P, A>::const_iterator>
unordered_multimap<K, T, H, P, A>::equal_range(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return std::make_pair(
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
iterator n = table_.find(k);
return std::make_pair(const_iterator(n),
const_iterator(n == end() ? n : table_.next_group(k, n)));
}
template <class K, class T, class H, class P, class A>
@ -2561,9 +2514,9 @@ namespace boost {
template <class K, class T, class H, class P, class A>
float unordered_multimap<K, T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
BOOST_ASSERT(table_.bucket_count_ != 0);
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count_);
static_cast<float>(table_.bucket_count());
}
template <class K, class T, class H, class P, class A>
@ -2582,8 +2535,7 @@ namespace boost {
template <class K, class T, class H, class P, class A>
void unordered_multimap<K, T, H, P, A>::reserve(size_type n)
{
table_.rehash(static_cast<std::size_t>(
std::ceil(static_cast<double>(n) / table_.mlf_)));
table_.reserve(n);
}
template <class K, class T, class H, class P, class A>
@ -2643,16 +2595,14 @@ namespace boost {
template <class K2, class T2, class H2, class P2, class A2>
friend class boost::unordered::unordered_multimap;
typedef typename boost::unordered::detail::rebind_wrap<A,
std::pair<K const, T> >::type value_allocator;
typedef boost::unordered::detail::allocator_traits<value_allocator>
value_allocator_traits;
typedef typename boost::allocator_rebind<A, std::pair<K const, T> >::type
value_allocator;
typedef N node;
typedef typename boost::unordered::detail::rebind_wrap<A, node>::type
node_allocator;
typedef boost::unordered::detail::allocator_traits<node_allocator>
node_allocator_traits;
typedef typename node_allocator_traits::pointer node_pointer;
typedef typename boost::allocator_rebind<A, node>::type node_allocator;
typedef
typename boost::allocator_pointer<node_allocator>::type node_pointer;
public:
typedef K key_type;
@ -2681,8 +2631,8 @@ namespace boost {
}
node_handle_map(BOOST_RV_REF(node_handle_map) n) BOOST_NOEXCEPT
: ptr_(n.ptr_),
alloc_(boost::move(n.alloc_))
: ptr_(n.ptr_),
alloc_(boost::move(n.alloc_))
{
n.ptr_ = node_pointer();
}
@ -2690,8 +2640,8 @@ namespace boost {
node_handle_map& operator=(BOOST_RV_REF(node_handle_map) n)
{
BOOST_ASSERT(!alloc_.has_value() ||
value_allocator_traits::
propagate_on_container_move_assignment::value ||
boost::allocator_propagate_on_container_move_assignment<
value_allocator>::type::value ||
(n.alloc_.has_value() && alloc_ == n.alloc_));
if (ptr_) {
@ -2702,8 +2652,8 @@ namespace boost {
}
if (!alloc_.has_value() ||
value_allocator_traits::propagate_on_container_move_assignment::
value) {
boost::allocator_propagate_on_container_move_assignment<
value_allocator>::type::value) {
alloc_ = boost::move(n.alloc_);
}
ptr_ = n.ptr_;
@ -2731,14 +2681,17 @@ namespace boost {
}
void swap(node_handle_map& n) BOOST_NOEXCEPT_IF(
value_allocator_traits::propagate_on_container_swap::value ||
value_allocator_traits::is_always_equal::value)
boost::allocator_propagate_on_container_swap<
value_allocator>::type::value ||
boost::allocator_is_always_equal<value_allocator>::type::value)
{
BOOST_ASSERT(
!alloc_.has_value() || !n.alloc_.has_value() ||
value_allocator_traits::propagate_on_container_swap::value ||
alloc_ == n.alloc_);
if (value_allocator_traits::propagate_on_container_swap::value ||
BOOST_ASSERT(!alloc_.has_value() || !n.alloc_.has_value() ||
boost::allocator_propagate_on_container_swap<
value_allocator>::type::value ||
alloc_ == n.alloc_);
if (boost::allocator_propagate_on_container_swap<
value_allocator>::type::value ||
!alloc_.has_value() || !n.alloc_.has_value()) {
boost::swap(alloc_, n.alloc_);
}
@ -2753,25 +2706,25 @@ namespace boost {
x.swap(y);
}
template <class N, class K, class T, class A> struct insert_return_type_map
template <class Iter, class NodeType> struct insert_return_type_map
{
private:
BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_map)
typedef typename boost::unordered::detail::rebind_wrap<A,
std::pair<K const, T> >::type value_allocator;
typedef N node_;
// typedef typename boost::allocator_rebind<A,
// std::pair<K const, T> >::type value_allocator;
// typedef N node_;
public:
Iter position;
bool inserted;
boost::unordered::iterator_detail::iterator<node_> position;
boost::unordered::node_handle_map<N, K, T, A> node;
NodeType node;
insert_return_type_map() : inserted(false), position(), node() {}
insert_return_type_map() : position(), inserted(false), node() {}
insert_return_type_map(BOOST_RV_REF(insert_return_type_map)
x) BOOST_NOEXCEPT : inserted(x.inserted),
position(x.position),
x) BOOST_NOEXCEPT : position(x.position),
inserted(x.inserted),
node(boost::move(x.node))
{
}
@ -2785,9 +2738,9 @@ namespace boost {
}
};
template <class N, class K, class T, class A>
void swap(insert_return_type_map<N, K, T, A>& x,
insert_return_type_map<N, K, T, A>& y)
template <class Iter, class NodeType>
void swap(insert_return_type_map<Iter, NodeType>& x,
insert_return_type_map<Iter, NodeType>& y)
{
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);

View File

@ -1,5 +1,6 @@
// Copyright (C) 2008-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -59,7 +60,7 @@ namespace boost {
unordered_multimap<K, T, H, P, A>& c, Predicate pred);
template <class N, class K, class T, class A> class node_handle_map;
template <class N, class K, class T, class A> struct insert_return_type_map;
template <class Iter, class NodeType> struct insert_return_type_map;
}
using boost::unordered::unordered_map;

View File

@ -1,6 +1,7 @@
// Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard.
// Copyright (C) 2005-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -55,8 +56,6 @@ namespace boost {
typedef boost::unordered::detail::set<A, T, H, P> types;
typedef typename types::value_allocator_traits value_allocator_traits;
typedef typename types::table table;
typedef typename table::node_pointer node_pointer;
typedef typename table::link_pointer link_pointer;
public:
typedef typename value_allocator_traits::pointer pointer;
@ -68,9 +67,9 @@ namespace boost {
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef typename table::iterator iterator;
typedef typename table::c_iterator iterator;
typedef typename table::c_iterator const_iterator;
typedef typename table::l_iterator local_iterator;
typedef typename table::cl_iterator local_iterator;
typedef typename table::cl_iterator const_local_iterator;
typedef typename types::node_type node_type;
typedef typename types::insert_return_type insert_return_type;
@ -411,7 +410,7 @@ namespace boost {
node_type extract(const key_type& k)
{
return node_type(table_.extract_by_key(k), table_.node_alloc());
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
}
template <class Key>
@ -455,8 +454,7 @@ namespace boost {
size_type>::type
erase(BOOST_FWD_REF(Key) k)
{
return table_.erase_key_unique_impl(
this->key_eq(), boost::forward<Key>(k));
return table_.erase_key_unique_impl(boost::forward<Key>(k));
}
BOOST_UNORDERED_DEPRECATED("Use erase instead")
@ -500,9 +498,7 @@ namespace boost {
const_iterator>::type
find(const Key& k) const
{
return const_iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq()));
return const_iterator(table_.find(k));
}
template <class CompatibleKey, class CompatibleHash,
@ -512,9 +508,7 @@ namespace boost {
bool contains(key_type const& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
template <class Key>
@ -522,9 +516,7 @@ namespace boost {
bool>::type
contains(const Key& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
size_type count(const key_type&) const;
@ -534,11 +526,7 @@ namespace boost {
size_type>::type
count(const Key& k) const
{
node_pointer n = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return n ? 1 : 0;
return table_.find(k) != this->end() ? 1 : 0;
}
std::pair<const_iterator, const_iterator> equal_range(
@ -549,19 +537,20 @@ namespace boost {
std::pair<const_iterator, const_iterator> >::type
equal_range(Key const& k) const
{
node_pointer n = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
iterator n = table_.find(k);
iterator m = n;
if (m != this->end()) {
++m;
}
return std::make_pair(
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
return std::make_pair(const_iterator(n), const_iterator(m));
}
// bucket interface
size_type bucket_count() const BOOST_NOEXCEPT
{
return table_.bucket_count_;
return table_.bucket_count();
}
size_type max_bucket_count() const BOOST_NOEXCEPT
@ -578,12 +567,12 @@ namespace boost {
local_iterator begin(size_type n)
{
return local_iterator(table_.begin(n), n, table_.bucket_count_);
return local_iterator(table_.begin(n));
}
const_local_iterator begin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
local_iterator end(size_type) { return local_iterator(); }
@ -595,7 +584,7 @@ namespace boost {
const_local_iterator cbegin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
const_local_iterator cend(size_type) const
@ -684,8 +673,6 @@ namespace boost {
typedef boost::unordered::detail::set<A, T, H, P> types;
typedef typename types::value_allocator_traits value_allocator_traits;
typedef typename types::table table;
typedef typename table::node_pointer node_pointer;
typedef typename table::link_pointer link_pointer;
public:
typedef typename value_allocator_traits::pointer pointer;
@ -697,9 +684,9 @@ namespace boost {
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef typename table::iterator iterator;
typedef typename table::c_iterator iterator;
typedef typename table::c_iterator const_iterator;
typedef typename table::l_iterator local_iterator;
typedef typename table::cl_iterator local_iterator;
typedef typename table::cl_iterator const_local_iterator;
typedef typename types::node_type node_type;
@ -1035,7 +1022,7 @@ namespace boost {
node_type extract(const key_type& k)
{
return node_type(table_.extract_by_key(k), table_.node_alloc());
return node_type(table_.extract_by_key_impl(k), table_.node_alloc());
}
template <class Key>
@ -1076,7 +1063,7 @@ namespace boost {
size_type>::type
erase(const Key& k)
{
return table_.erase_key_equiv_impl(this->key_eq(), k);
return table_.erase_key_equiv_impl(k);
}
iterator erase(const_iterator, const_iterator);
@ -1121,9 +1108,7 @@ namespace boost {
const_iterator>::type
find(const Key& k) const
{
return const_iterator(table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq()));
return table_.find(k);
}
template <class CompatibleKey, class CompatibleHash,
@ -1133,9 +1118,7 @@ namespace boost {
bool contains(const key_type& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
template <class Key>
@ -1143,9 +1126,7 @@ namespace boost {
bool>::type
contains(const Key& k) const
{
return 0 != table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return table_.find(k) != this->end();
}
size_type count(const key_type&) const;
@ -1155,11 +1136,7 @@ namespace boost {
size_type>::type
count(const Key& k) const
{
node_pointer n = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return n ? table_.group_count(n) : 0;
return table_.group_count(k);
}
std::pair<const_iterator, const_iterator> equal_range(
@ -1170,19 +1147,16 @@ namespace boost {
std::pair<const_iterator, const_iterator> >::type
equal_range(const Key& k) const
{
node_pointer n = table_.find_node_impl(
table::policy::apply_hash(this->hash_function(), k), k,
this->key_eq());
return std::make_pair(
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
iterator first = table_.find(k);
iterator last = table_.next_group(k, first);
return std::make_pair(const_iterator(first), const_iterator(last));
}
// bucket interface
size_type bucket_count() const BOOST_NOEXCEPT
{
return table_.bucket_count_;
return table_.bucket_count();
}
size_type max_bucket_count() const BOOST_NOEXCEPT
@ -1199,12 +1173,12 @@ namespace boost {
local_iterator begin(size_type n)
{
return local_iterator(table_.begin(n), n, table_.bucket_count_);
return local_iterator(table_.begin(n));
}
const_local_iterator begin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
local_iterator end(size_type) { return local_iterator(); }
@ -1216,7 +1190,7 @@ namespace boost {
const_local_iterator cbegin(size_type n) const
{
return const_local_iterator(table_.begin(n), n, table_.bucket_count_);
return const_local_iterator(table_.begin(n));
}
const_local_iterator cend(size_type) const
@ -1319,7 +1293,7 @@ namespace boost {
unordered_set::value_allocator_traits::
select_on_container_copy_construction(other.get_allocator()))
{
if (other.table_.size_) {
if (other.size()) {
table_.copy_buckets(
other.table_, boost::unordered::detail::true_type());
}
@ -1483,29 +1457,23 @@ namespace boost {
typename unordered_set<T, H, P, A>::iterator
unordered_set<T, H, P, A>::erase(const_iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_unique(node, next);
return iterator(next);
const_iterator last = position;
++last;
return table_.erase_nodes_range(position, last);
}
template <class T, class H, class P, class A>
typename unordered_set<T, H, P, A>::size_type
unordered_set<T, H, P, A>::erase(const key_type& k)
{
return table_.erase_key_unique_impl(this->key_eq(), k);
return table_.erase_key_unique_impl(k);
}
template <class T, class H, class P, class A>
typename unordered_set<T, H, P, A>::iterator
unordered_set<T, H, P, A>::erase(const_iterator first, const_iterator last)
{
node_pointer last_node = table::get_node(last);
if (first == last)
return iterator(last_node);
table_.erase_nodes_unique(table::get_node(first), last_node);
return iterator(last_node);
return table_.erase_nodes_range(first, last);
}
template <class T, class H, class P, class A>
@ -1575,7 +1543,7 @@ namespace boost {
typename unordered_set<T, H, P, A>::const_iterator
unordered_set<T, H, P, A>::find(const key_type& k) const
{
return const_iterator(table_.find_node(k));
return const_iterator(table_.find(k));
}
template <class T, class H, class P, class A>
@ -1585,8 +1553,7 @@ namespace boost {
unordered_set<T, H, P, A>::find(CompatibleKey const& k,
CompatibleHash const& hash, CompatiblePredicate const& eq) const
{
return const_iterator(
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
return table_.transparent_find(k, hash, eq);
}
template <class T, class H, class P, class A>
@ -1601,9 +1568,12 @@ namespace boost {
typename unordered_set<T, H, P, A>::const_iterator>
unordered_set<T, H, P, A>::equal_range(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return std::make_pair(
const_iterator(n), const_iterator(n ? table::next_node(n) : n));
iterator first = table_.find(k);
iterator second = first;
if (second != this->end()) {
++second;
}
return std::make_pair(first, second);
}
template <class T, class H, class P, class A>
@ -1618,9 +1588,9 @@ namespace boost {
template <class T, class H, class P, class A>
float unordered_set<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
BOOST_ASSERT(table_.bucket_count_ != 0);
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count_);
static_cast<float>(table_.bucket_count());
}
template <class T, class H, class P, class A>
@ -1638,8 +1608,7 @@ namespace boost {
template <class T, class H, class P, class A>
void unordered_set<T, H, P, A>::reserve(size_type n)
{
table_.rehash(static_cast<std::size_t>(
std::ceil(static_cast<double>(n) / table_.mlf_)));
table_.reserve(n);
}
template <class T, class H, class P, class A>
@ -1883,11 +1852,11 @@ namespace boost {
typename unordered_multiset<T, H, P, A>::iterator
unordered_multiset<T, H, P, A>::erase(const_iterator position)
{
node_pointer node = table::get_node(position);
BOOST_ASSERT(node);
node_pointer next = table::next_node(node);
table_.erase_nodes_equiv(node, next);
return iterator(next);
BOOST_ASSERT(position != this->end());
iterator next = position;
++next;
table_.erase_nodes_range(position, next);
return next;
}
template <class T, class H, class P, class A>
@ -1902,11 +1871,7 @@ namespace boost {
unordered_multiset<T, H, P, A>::erase(
const_iterator first, const_iterator last)
{
node_pointer last_node = table::get_node(last);
if (first == last)
return iterator(last_node);
table_.erase_nodes_equiv(table::get_node(first), last_node);
return iterator(last_node);
return table_.erase_nodes_range(first, last);
}
template <class T, class H, class P, class A>
@ -1984,7 +1949,7 @@ namespace boost {
typename unordered_multiset<T, H, P, A>::const_iterator
unordered_multiset<T, H, P, A>::find(const key_type& k) const
{
return const_iterator(table_.find_node(k));
return const_iterator(table_.find(k));
}
template <class T, class H, class P, class A>
@ -1994,16 +1959,14 @@ namespace boost {
unordered_multiset<T, H, P, A>::find(CompatibleKey const& k,
CompatibleHash const& hash, CompatiblePredicate const& eq) const
{
return const_iterator(
table_.find_node_impl(table::policy::apply_hash(hash, k), k, eq));
return table_.transparent_find(k, hash, eq);
}
template <class T, class H, class P, class A>
typename unordered_multiset<T, H, P, A>::size_type
unordered_multiset<T, H, P, A>::count(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return n ? table_.group_count(n) : 0;
return table_.group_count(k);
}
template <class T, class H, class P, class A>
@ -2011,9 +1974,9 @@ namespace boost {
typename unordered_multiset<T, H, P, A>::const_iterator>
unordered_multiset<T, H, P, A>::equal_range(const key_type& k) const
{
node_pointer n = table_.find_node(k);
return std::make_pair(
const_iterator(n), const_iterator(n ? table_.next_group(n) : n));
iterator n = table_.find(k);
return std::make_pair(const_iterator(n),
const_iterator(n == end() ? n : table_.next_group(k, n)));
}
template <class T, class H, class P, class A>
@ -2028,9 +1991,9 @@ namespace boost {
template <class T, class H, class P, class A>
float unordered_multiset<T, H, P, A>::load_factor() const BOOST_NOEXCEPT
{
BOOST_ASSERT(table_.bucket_count_ != 0);
BOOST_ASSERT(table_.bucket_count() != 0);
return static_cast<float>(table_.size_) /
static_cast<float>(table_.bucket_count_);
static_cast<float>(table_.bucket_count());
}
template <class T, class H, class P, class A>
@ -2048,8 +2011,7 @@ namespace boost {
template <class T, class H, class P, class A>
void unordered_multiset<T, H, P, A>::reserve(size_type n)
{
table_.rehash(static_cast<std::size_t>(
std::ceil(static_cast<double>(n) / table_.mlf_)));
table_.reserve(n);
}
template <class T, class H, class P, class A>
@ -2217,25 +2179,25 @@ namespace boost {
x.swap(y);
}
template <typename N, typename T, typename A> struct insert_return_type_set
template <class Iter, class NodeType> struct insert_return_type_set
{
private:
BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_set)
typedef typename boost::unordered::detail::rebind_wrap<A, T>::type
value_allocator;
typedef N node_;
// typedef typename boost::unordered::detail::rebind_wrap<A, T>::type
// value_allocator;
// typedef N node_;
public:
Iter position;
bool inserted;
boost::unordered::iterator_detail::c_iterator<node_> position;
boost::unordered::node_handle_set<N, T, A> node;
NodeType node;
insert_return_type_set() : inserted(false), position(), node() {}
insert_return_type_set() : position(), inserted(false), node() {}
insert_return_type_set(BOOST_RV_REF(insert_return_type_set)
x) BOOST_NOEXCEPT : inserted(x.inserted),
position(x.position),
x) BOOST_NOEXCEPT : position(x.position),
inserted(x.inserted),
node(boost::move(x.node))
{
}
@ -2249,9 +2211,9 @@ namespace boost {
}
};
template <typename N, typename T, typename A>
template <class Iter, class NodeType>
void swap(
insert_return_type_set<N, T, A>& x, insert_return_type_set<N, T, A>& y)
insert_return_type_set<Iter, NodeType>& x, insert_return_type_set<Iter, NodeType>& y)
{
boost::swap(x.node, y.node);
boost::swap(x.inserted, y.inserted);

View File

@ -1,5 +1,6 @@
// Copyright (C) 2008-2011 Daniel James.
// Copyright (C) 2022 Christian Mazakas
// 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)
@ -57,7 +58,7 @@ namespace boost {
unordered_multiset<K, H, P, A>& c, Predicate pred);
template <class N, class T, class A> class node_handle_set;
template <class N, class T, class A> struct insert_return_type_set;
template <class Iter, class NodeType> struct insert_return_type_set;
}
using boost::unordered::unordered_set;

View File

@ -1,5 +1,6 @@
# Copyright 2006-2008 Daniel James.
# Copyright 2022 Christian Mazakas
# 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)
@ -69,14 +70,13 @@ test-suite unordered
[ run unordered/rehash_tests.cpp ]
[ run unordered/equality_tests.cpp ]
[ run unordered/swap_tests.cpp ]
[ run unordered/detail_tests.cpp ]
[ run unordered/deduction_tests.cpp ]
[ run unordered/scoped_allocator.cpp : : : <toolset>msvc-14.0:<build>no ]
[ run unordered/transparent_tests.cpp ]
[ run unordered/reserve_tests.cpp ]
[ run unordered/contains_tests.cpp ]
[ run unordered/mix_policy.cpp ]
[ run unordered/erase_if.cpp ]
[ run unordered/scary_tests.cpp ]
[ run unordered/compile_set.cpp : :
: <define>BOOST_UNORDERED_USE_MOVE

View File

@ -1,5 +1,6 @@
// Copyright 2006-2011 Daniel James.
// Copyright 2022 Christian Mazakas
// 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)
@ -213,25 +214,27 @@ namespace test
::operator delete((void*)p);
}
void construct(T* p, T const& t)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V>
void construct(U* p, V const& v)
{
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
new (p) T(t);
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
new (p) U(v);
}
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <typename... Args>
void construct(T* p, BOOST_FWD_REF(Args)... args)
#else
template <class U, typename... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
new (p) T(boost::forward<Args>(args)...);
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
new (p) U(boost::forward<Args>(args)...);
}
#endif
void destroy(T* p)
template <class U>
void destroy(U* p)
{
detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
p->~T();
detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
p->~U();
}
size_type max_size() const

View File

@ -1,5 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas
// 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)
@ -475,32 +476,34 @@ namespace test {
}
}
void construct(pointer p, T const& t)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class Arg>
void construct(U* p, Arg const& t)
{
UNORDERED_SCOPE(allocator::construct(T*, T))
UNORDERED_SCOPE(allocator::construct(U*, Arg))
{
UNORDERED_EPOINT("Mock allocator construct function.");
new (p) T(t);
new (p) U(t);
}
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
}
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
#else
template <class U, class... Args> void construct(U* p, BOOST_FWD_REF(Args)... args)
{
UNORDERED_SCOPE(allocator::construct(pointer, BOOST_FWD_REF(Args)...))
UNORDERED_SCOPE(allocator::construct(U*, BOOST_FWD_REF(Args)...))
{
UNORDERED_EPOINT("Mock allocator construct function.");
new (p) T(boost::forward<Args>(args)...);
new (p) U(boost::forward<Args>(args)...);
}
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
}
#endif
void destroy(T* p)
template <class U>
void destroy(U* p)
{
test::detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
p->~T();
test::detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
p->~U();
}
size_type max_size() const
@ -654,32 +657,35 @@ namespace test {
}
}
void construct(pointer p, T const& t)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V>
void construct(U* p, V const& v)
{
UNORDERED_SCOPE(allocator2::construct(T*, T))
UNORDERED_SCOPE(allocator2::construct(U*, V))
{
UNORDERED_EPOINT("Mock allocator2 construct function.");
new (p) T(t);
new (p) U(v);
}
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
}
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
UNORDERED_SCOPE(allocator2::construct(pointer, BOOST_FWD_REF(Args)...))
UNORDERED_SCOPE(allocator2::construct(U*, BOOST_FWD_REF(Args)...))
{
UNORDERED_EPOINT("Mock allocator2 construct function.");
new (p) T(boost::forward<Args>(args)...);
new (p) U(boost::forward<Args>(args)...);
}
test::detail::tracker.track_construct((void*)p, sizeof(T), tag_);
test::detail::tracker.track_construct((void*)p, sizeof(U), tag_);
}
#endif
void destroy(T* p)
template <class U>
void destroy(U* p)
{
test::detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
p->~T();
test::detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
p->~U();
}
size_type max_size() const

View File

@ -1,5 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas
// 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)
@ -10,6 +11,8 @@
#if !defined(BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER)
#define BOOST_UNORDERED_OBJECTS_MINIMAL_HEADER
#include <boost/core/lightweight_test.hpp>
#include <boost/core/pointer_traits.hpp>
#include <boost/move/move.hpp>
#include <cstddef>
#include <utility>
@ -309,13 +312,17 @@ namespace test {
return tmp;
}
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
friend ptr operator+(std::ptrdiff_t s, ptr p)
{
return ptr<T>(s + p.ptr_);
}
friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); }
std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
bool operator!() const { return !ptr_; }
static ptr pointer_to(T& p) {
return ptr(&p);
}
// I'm not using the safe bool idiom because the containers should be
// able to cope with bool conversions.
operator bool() const { return !!ptr_; }
@ -428,16 +435,20 @@ namespace test {
::operator delete((void*)p.ptr_);
}
void construct(T* p, T const& t) { new ((void*)p) T(t); }
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V> void construct(U* p, V const& v)
{
new ((void*)p) T(boost::forward<Args>(args)...);
new ((void*)p) U(v);
}
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
new ((void*)p) U(boost::forward<Args>(args)...);
}
#endif
void destroy(T* p) { p->~T(); }
template <class U> void destroy(U* p) { p->~U(); }
size_type max_size() const { return 1000; }
@ -498,17 +509,20 @@ namespace test {
::operator delete((void*)p.ptr_);
}
void construct(T const* p, T const& t) { new ((void*)p) T(t); }
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args>
void construct(T const* p, BOOST_FWD_REF(Args)... args)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U> void construct(U* p, U const& t)
{
new ((void*)p) T(boost::forward<Args>(args)...);
new (p) U(t);
}
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
new (p) U(boost::forward<Args>(args)...);
}
#endif
void destroy(T const* p) { p->~T(); }
template <class U> void destroy(U* p) { p->~U(); }
size_type max_size() const { return 1000; }
@ -573,16 +587,20 @@ namespace test {
void deallocate(T* p, std::size_t) { ::operator delete((void*)p); }
void construct(T* p, T const& t) { new ((void*)p) T(t); }
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V> void construct(U* p, V const& v)
{
new ((void*)p) T(boost::forward<Args>(args)...);
new ((void*)p) U(v);
}
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
new ((void*)p) U(boost::forward<Args>(args)...);
}
#endif
void destroy(T* p) { p->~T(); }
template <class U> void destroy(U* p) { p->~U(); }
std::size_t max_size() const { return 1000u; }
};
@ -624,4 +642,14 @@ namespace test {
#pragma warning(pop)
#endif
namespace boost {
template <> struct pointer_traits< ::test::minimal::void_ptr>
{
template <class U> struct rebind_to
{
typedef ::test::minimal::ptr<U> type;
};
};
}
#endif

View File

@ -1,5 +1,6 @@
// Copyright 2006-2009 Daniel James.
// Copyright 2022 Christian Mazakas
// 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)
@ -184,7 +185,7 @@ namespace test {
};
// Note: This is a deliberately bad hash function.
class hash
class hash BOOST_FINAL
{
int type_;
@ -289,7 +290,7 @@ namespace test {
}
}
std::size_t operator()(int x1, int x2) const { return x1 < x2; }
bool operator()(int x1, int x2) const { return x1 < x2; }
friend bool operator==(less const& x1, less const& x2)
{
@ -297,7 +298,7 @@ namespace test {
}
};
class equal_to
class equal_to BOOST_FINAL
{
int type_;
@ -330,7 +331,7 @@ namespace test {
}
}
std::size_t operator()(int x1, int x2) const { return x1 == x2; }
bool operator()(int x1, int x2) const { return x1 == x2; }
friend bool operator==(equal_to const& x1, equal_to const& x2)
{
@ -499,11 +500,9 @@ namespace test {
friend class const_ptr<T>;
friend struct void_ptr;
T* ptr_;
ptr(T* x) : ptr_(x) {}
public:
T* ptr_;
ptr(T* x) : ptr_(x) {}
ptr() : ptr_(0) {}
explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {}
@ -520,11 +519,22 @@ namespace test {
++ptr_;
return tmp;
}
ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); }
friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); }
std::ptrdiff_t operator-(ptr p) const { return ptr_ - p.ptr_; }
ptr operator-(std::ptrdiff_t s) const { return ptr(ptr_ - s); }
ptr& operator+=(std::ptrdiff_t s) { ptr_ += s; return *this; }
T& operator[](std::ptrdiff_t s) const { return ptr_[s]; }
bool operator!() const { return !ptr_; }
static ptr pointer_to(T& p) {
return ptr(&p);
}
// I'm not using the safe bool idiom because the containers should be
// able to cope with bool conversions.
operator bool() const { return !!ptr_; }
@ -646,24 +656,25 @@ namespace test {
::operator delete((void*)p.ptr_);
}
void construct(T* p, T const& t)
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V> void construct(U* p, V const& v)
{
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
new (p) T(t);
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
new (p) U(v);
}
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args)
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
detail::tracker.track_construct((void*)p, sizeof(T), tag_);
new (p) T(boost::forward<Args>(args)...);
detail::tracker.track_construct((void*)p, sizeof(U), tag_);
new (p) U(boost::forward<Args>(args)...);
}
#endif
void destroy(T* p)
template <class U> void destroy(U* p)
{
detail::tracker.track_destroy((void*)p, sizeof(T), tag_);
p->~T();
detail::tracker.track_destroy((void*)p, sizeof(U), tag_);
p->~U();
}
size_type max_size() const
@ -699,4 +710,14 @@ namespace test {
}
}
namespace boost {
template <> struct pointer_traits< ::test::void_ptr>
{
template <class U> struct rebind_to
{
typedef ::test::ptr<U> type;
};
};
} // namespace boost
#endif

View File

@ -1,101 +0,0 @@
// Copyright 2017 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)
// clang-format off
#include "../helpers/prefix.hpp"
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include "../helpers/postfix.hpp"
// clang-format on
#include "../helpers/test.hpp"
#include <map>
// Pretty inefficient, but the test is fast enough.
// Might be too slow if we had larger primes?
bool is_prime(std::size_t x)
{
if (x == 2) {
return true;
} else if (x == 1 || x % 2 == 0) {
return false;
} else {
// y*y <= x had rounding errors, so instead use y <= (x/y).
for (std::size_t y = 3; y <= (x / y); y += 2) {
if (x % y == 0) {
return false;
break;
}
}
return true;
}
}
void test_next_prime(std::size_t value)
{
std::size_t x = boost::unordered::detail::next_prime(value);
BOOST_TEST(is_prime(x));
BOOST_TEST(x >= value);
}
void test_prev_prime(std::size_t value)
{
std::size_t x = boost::unordered::detail::prev_prime(value);
BOOST_TEST(is_prime(x));
BOOST_TEST(x <= value);
if (x > value) {
BOOST_LIGHTWEIGHT_TEST_OSTREAM << x << "," << value << std::endl;
}
}
UNORDERED_AUTO_TEST (next_prime_test) {
BOOST_TEST(!is_prime(0));
BOOST_TEST(!is_prime(1));
BOOST_TEST(is_prime(2));
BOOST_TEST(is_prime(3));
BOOST_TEST(is_prime(13));
BOOST_TEST(!is_prime(4));
BOOST_TEST(!is_prime(100));
BOOST_TEST(boost::unordered::detail::next_prime(0) > 0);
// test_prev_prime doesn't work for values less than 17.
// Which should be okay, unless an allocator has a really tiny
// max_size?
const std::size_t min_prime = 17;
// test_next_prime doesn't work for values greater than this,
// which might be a problem if you've got terrabytes of memory?
// I seriously doubt the container would work well at such sizes
// regardless.
const std::size_t max_prime = 4294967291ul;
std::size_t i;
BOOST_TEST(is_prime(min_prime));
BOOST_TEST(is_prime(max_prime));
for (i = 0; i < 10000; ++i) {
if (i < min_prime) {
BOOST_TEST(boost::unordered::detail::prev_prime(i) == min_prime);
} else {
test_prev_prime(i);
}
test_next_prime(i);
}
std::size_t last = i - 1;
for (; i > last; last = i, i += i / 5) {
if (i > max_prime) {
BOOST_TEST(boost::unordered::detail::next_prime(i) == max_prime);
} else {
test_next_prime(i);
}
test_prev_prime(i);
}
}
RUN_TESTS()

View File

@ -1,65 +0,0 @@
// Copyright 2022 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/unordered/detail/implementation.hpp>
#include <boost/core/bit.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/cstdint.hpp>
template<class Policy, class SizeT> void test( SizeT x )
{
if( x <= 4 )
{
BOOST_TEST_EQ( Policy::new_bucket_count( x ), 4u );
}
else
{
BOOST_TEST_EQ( Policy::new_bucket_count( x ), boost::core::bit_ceil( x ) );
}
BOOST_TEST_EQ( Policy::prev_bucket_count( x ), boost::core::bit_floor( x ) );
}
int main()
{
{
typedef boost::uint64_t SizeT;
typedef boost::unordered::detail::mix64_policy<SizeT> policy;
for( SizeT i = 1; i < 200; ++i )
{
test<policy>( i );
}
for( int i = 8; i < 64; ++i )
{
SizeT x = SizeT( 1 ) << i;
test<policy>( x - 1 );
test<policy>( x );
test<policy>( x + 1 );
}
}
{
typedef boost::uint32_t SizeT;
typedef boost::unordered::detail::mix32_policy<SizeT> policy;
for( SizeT i = 1; i < 200; ++i )
{
test<policy>( i );
}
for( int i = 8; i < 32; ++i )
{
SizeT x = SizeT( 1 ) << i;
test<policy>( x - 1 );
test<policy>( x );
test<policy>( x + 1 );
}
}
return boost::report_errors();
}

View File

@ -1,4 +1,4 @@
// Copyright 2021 Christian Mazakas.
// Copyright 2021-2022 Christian Mazakas.
// 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)
@ -20,24 +20,16 @@
std::size_t total_allocation = 0;
std::size_t num_allocations = 0;
struct B
{
B() : i(++count) {}
static int count;
int i;
bool operator==(B const& b) const { return i == b.i; };
bool operator!=(B const& b) const { return i != b.i; };
};
int B::count = 0;
template <typename T> struct A : B
template <typename T> struct A
{
typedef T value_type;
A() {}
static int count;
int i;
template <class U> A(const A<U>&) BOOST_NOEXCEPT {}
A() : i(++count) {}
template <class U> A(const A<U>& a) BOOST_NOEXCEPT : i(a.i) {}
T* allocate(std::size_t n)
{
@ -51,8 +43,13 @@ template <typename T> struct A : B
total_allocation -= n * sizeof(T);
std::free(p);
}
bool operator==(A const& a) const { return i == a.i; };
bool operator!=(A const& a) const { return i != a.i; };
};
template <class T> int A<T>::count = 0;
template <class UnorderedContainer> void bucket_count_constructor()
{
BOOST_TEST_EQ(num_allocations, 0u);
@ -152,9 +149,11 @@ template <class UnorderedContainer> void rehash_tests()
// no reallocations
//
std::size_t prev_allocations = num_allocations;
std::size_t prev_total_allocation = total_allocation;
s.rehash(count);
BOOST_TEST_EQ(num_allocations, prev_allocations);
BOOST_TEST_EQ(total_allocation, prev_total_allocation);
// prove that when we rehash, exceeding the current bucket count, that we
// properly deallocate the current bucket array and then reallocate the
@ -174,8 +173,15 @@ template <class UnorderedContainer> void rehash_tests()
// note, the test is vulnerable to cases where the next calculated bucket
// count can exceed `prev_count + count`
//
std::size_t const estimated_bucket_group_size =
3 * sizeof(void*) + sizeof(std::size_t);
std::size_t const estimated_bucket_groups =
s.bucket_count() / (sizeof(std::size_t) * 8);
BOOST_TEST_LT(s.bucket_count(), prev_count + count);
BOOST_TEST_LT(total_allocation, (prev_count + count) * sizeof(void*));
BOOST_TEST_LE(total_allocation,
(prev_count + count) * sizeof(void*) +
estimated_bucket_group_size * estimated_bucket_groups);
}
BOOST_TEST_GT(num_allocations, 0u);
@ -184,6 +190,30 @@ template <class UnorderedContainer> void rehash_tests()
}
UNORDERED_AUTO_TEST (unordered_set_reserve) {
{
// prove Allocator invariants
// from cppref:
// Given:
// * A, an Allocator type for type T
// * B, the corresponding Allocator type for some cv-unqualified object type
// U (as obtained by rebinding A)
//
// Expression:
// A a(b)
//
// Return Type:
// Constructs `a` such that `B(a)==b` and `A(b)==a`.
// (Note: This implies that all allocators related by rebind maintain each
// other's resources, such as memory pools.)
//
//
typedef boost::allocator_rebind<A<int>, float>::type alloc_rebound;
alloc_rebound b;
A<int> a(b);
BOOST_ASSERT(alloc_rebound(a) == b);
BOOST_ASSERT(A<int>(b) == a);
}
typedef boost::unordered_set<int, boost::hash<int>, std::equal_to<int>,
A<int> >
unordered_set;

View File

@ -0,0 +1,326 @@
// Copyright 2022 Christian Mazakas.
// 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)
// clang-format off
#include "../helpers/prefix.hpp"
#include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp>
#include "../helpers/postfix.hpp"
// clang-format on
#include "../helpers/test.hpp"
#include "../objects/test.hpp"
#include <memory>
struct hash1
{
template <class Key> std::size_t operator()(Key const&) const { return 1337; }
};
struct hash2
{
template <class Key> std::size_t operator()(Key const&) const { return 7331; }
};
struct equal1
{
template <class T> bool operator==(T const&) const { return true; }
};
struct equal2
{
template <class T> bool operator==(T const&) const { return true; }
};
///////////////////////////////////////////////////////////////////////////////
// we define two duplicated allocators here, each one having the same smart
// pointer type
// we want to prove in our test that different allocators with the same pointers
// (either fancy or raw) work equally well for our SCARY iterators
//
template <class T> struct allocator1
{
#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
public:
#else
template <class> friend struct allocator1;
#endif
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef test::void_ptr void_pointer;
typedef test::void_const_ptr const_void_pointer;
typedef test::ptr<T> pointer;
typedef test::const_ptr<T> const_pointer;
typedef T& reference;
typedef T const& const_reference;
typedef T value_type;
template <class U> struct rebind
{
typedef allocator1<U> other;
};
allocator1() {}
template <class Y> allocator1(allocator1<Y> const&) {}
allocator1(allocator1 const&) {}
~allocator1() {}
pointer address(reference r) { return pointer(&r); }
const_pointer address(const_reference r) { return const_pointer(&r); }
pointer allocate(size_type n)
{
pointer p(static_cast<T*>(::operator new(n * sizeof(T))));
return p;
}
pointer allocate(size_type n, void const*)
{
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
return ptr;
}
void deallocate(pointer p, size_type)
{
::operator delete((void*)p.operator->());
}
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V> void construct(U* p, V const& v) { new (p) U(v); }
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
new (p) U(boost::forward<Args>(args)...);
}
#endif
// msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only
// ever using it with an int with has a trivial destructor so it eliminates
// the code and generates a warning about an unused variable
//
template <class U>
void destroy(U* p)
{
(void)p;
p->~U();
}
size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
bool operator==(allocator1 const&) const { return true; }
bool operator!=(allocator1 const&) const { return false; }
enum
{
is_select_on_copy = false,
is_propagate_on_swap = false,
is_propagate_on_assign = false,
is_propagate_on_move = false
};
};
template <class T> struct allocator2
{
#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
public:
#else
template <class> friend struct allocator2;
#endif
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef test::void_ptr void_pointer;
typedef test::void_const_ptr const_void_pointer;
typedef test::ptr<T> pointer;
typedef test::const_ptr<T> const_pointer;
typedef T& reference;
typedef T const& const_reference;
typedef T value_type;
template <class U> struct rebind
{
typedef allocator2<U> other;
};
allocator2() {}
template <class Y> allocator2(allocator2<Y> const&) {}
allocator2(allocator2 const&) {}
~allocator2() {}
pointer address(reference r) { return pointer(&r); }
const_pointer address(const_reference r) { return const_pointer(&r); }
pointer allocate(size_type n)
{
pointer p(static_cast<T*>(::operator new(n * sizeof(T))));
return p;
}
pointer allocate(size_type n, void const*)
{
pointer ptr(static_cast<T*>(::operator new(n * sizeof(T))));
return ptr;
}
void deallocate(pointer p, size_type) { ::operator delete((void*)p.ptr_); }
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <class U, class V> void construct(U* p, V const& v) { new (p) U(v); }
#else
template <class U, class... Args>
void construct(U* p, BOOST_FWD_REF(Args)... args)
{
new (p) U(boost::forward<Args>(args)...);
}
#endif
// msvc-12.0 and msvc-14.0 seem to eliminate the destructor call as we're only
// ever using it with an int with has a trivial destructor so it eliminates
// the code and generates a warning about an unused variable
//
template <class U>
void destroy(U* p)
{
(void)p;
p->~U();
}
size_type max_size() const { return (std::numeric_limits<size_type>::max)(); }
bool operator==(allocator2 const&) const { return true; }
bool operator!=(allocator2 const&) const { return false; }
enum
{
is_select_on_copy = false,
is_propagate_on_swap = false,
is_propagate_on_assign = false,
is_propagate_on_move = false
};
};
template <class C1, class C2> void scary_test()
{
C1 x;
C2 y;
typename C2::iterator begin(x.begin());
BOOST_TEST(begin == x.end());
typename C2::const_iterator cbegin(x.cbegin());
BOOST_TEST(cbegin == x.cend());
BOOST_ASSERT(x.bucket_count() > 0);
typename C2::local_iterator lbegin(x.begin(0));
BOOST_TEST(lbegin == x.end(0));
typename C2::const_local_iterator clbegin(x.cbegin(0));
BOOST_TEST(clbegin == x.cend(0));
}
template <
template <class Key, class T, class Hash, class KeyEqual, class Allocator>
class Map>
void map_scary_test()
{
typedef std::pair<int const, int> value_type;
typedef std::allocator<value_type> std_allocator_type;
typedef Map<int, int, hash1, std::equal_to<int>, std_allocator_type>
hash1_unordered_map;
typedef Map<int, int, hash2, std::equal_to<int>, std_allocator_type>
hash2_unordered_map;
typedef Map<int, int, boost::hash<int>, equal1, std_allocator_type>
equal1_unordered_map;
typedef Map<int, int, boost::hash<int>, equal2, std_allocator_type>
equal2_unordered_map;
// test allocators with a raw pointer as their `::pointer` type
//
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
test::allocator1<value_type> >
alloc1_unordered_map;
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
std_allocator_type>
std_alloc_unordered_map;
// test allocators with a fancy pointer as their `::pointer` type
//
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
allocator1<value_type> >
fancy1_unordered_map;
typedef Map<int, int, boost::hash<int>, std::equal_to<int>,
allocator2<value_type> >
fancy2_unordered_map;
scary_test<alloc1_unordered_map, std_alloc_unordered_map>();
scary_test<hash1_unordered_map, hash2_unordered_map>();
scary_test<equal1_unordered_map, equal2_unordered_map>();
scary_test<fancy1_unordered_map, fancy2_unordered_map>();
}
template <template <class Key, class Hash, class KeyEqual, class Allocator>
class Set>
void set_scary_test()
{
typedef int value_type;
typedef std::allocator<value_type> std_allocator_type;
typedef Set<int, hash1, std::equal_to<int>, std_allocator_type>
hash1_unordered_set;
typedef Set<int, hash2, std::equal_to<int>, std_allocator_type>
hash2_unordered_set;
typedef Set<int, boost::hash<int>, equal1, std_allocator_type>
equal1_unordered_set;
typedef Set<int, boost::hash<int>, equal2, std_allocator_type>
equal2_unordered_set;
// test allocators with a raw pointer as their `::pointer` type
//
typedef Set<int, boost::hash<int>, std::equal_to<int>,
test::allocator1<value_type> >
alloc1_unordered_set;
typedef Set<int, boost::hash<int>, std::equal_to<int>, std_allocator_type>
std_alloc_unordered_set;
// test allocators with a fancy pointer as their `::pointer` type
//
typedef Set<int, boost::hash<int>, std::equal_to<int>,
allocator1<value_type> >
fancy1_unordered_set;
typedef Set<int, boost::hash<int>, std::equal_to<int>,
allocator2<value_type> >
fancy2_unordered_set;
scary_test<alloc1_unordered_set, std_alloc_unordered_set>();
scary_test<hash1_unordered_set, hash2_unordered_set>();
scary_test<equal1_unordered_set, equal2_unordered_set>();
scary_test<fancy1_unordered_set, fancy2_unordered_set>();
}
UNORDERED_AUTO_TEST (scary_tests) {
map_scary_test<boost::unordered_map>();
map_scary_test<boost::unordered_multimap>();
set_scary_test<boost::unordered_set>();
set_scary_test<boost::unordered_multiset>();
}
RUN_TESTS()