Merge pull request #112 from boostorg/feature/fca-unordered
Update internal implementation to use FCA
4
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 35 KiB |
BIN
doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice.png
Normal file
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 36 KiB |
BIN
doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice.png
Normal file
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 42 KiB |
BIN
doc/diagrams/benchmarks/vs/running insertion.xlsx.practice.png
Normal file
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 38 KiB |
BIN
doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice.png
Normal file
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 46 KiB |
BIN
doc/diagrams/bucket-groups.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
doc/diagrams/fca.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
doc/diagrams/singly-linked.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
188
doc/roadmap.md
Normal 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.
|
@ -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
|
||||
|
||||
|===
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
1000
include/boost/unordered/detail/fca.hpp
Normal 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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
@ -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();
|
||||
}
|
@ -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;
|
||||
|
326
test/unordered/scary_tests.cpp
Normal 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()
|