diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67ac6a04..e58e8782 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..75389666 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique.png new file mode 100644 index 00000000..b40a5f47 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique 5.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique 5.png new file mode 100644 index 00000000..9aedb625 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique.png new file mode 100644 index 00000000..3843b6de Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash non-unique.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash.png new file mode 100644 index 00000000..7b74a09b Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice norehash.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice.png b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice.png new file mode 100644 index 00000000..d7333431 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/running insertion.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..bf0bd7d2 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique.png new file mode 100644 index 00000000..dfa05ea8 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice.png b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice.png new file mode 100644 index 00000000..510cff26 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered erasure.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..f1696a5c Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..cdd6094e Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice.png new file mode 100644 index 00000000..6c9bdb1c Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered successful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..b15214ed Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..34a7a226 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice.png new file mode 100644 index 00000000..b595ba59 Binary files /dev/null and b/doc/diagrams/benchmarks/clang_libcpp/scattered unsuccessful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..d23363c5 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique.png new file mode 100644 index 00000000..9fe81db6 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique 5.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique 5.png new file mode 100644 index 00000000..805832f8 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique.png new file mode 100644 index 00000000..0804d890 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash non-unique.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash.png new file mode 100644 index 00000000..69009334 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice norehash.png differ diff --git a/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice.png b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice.png new file mode 100644 index 00000000..64dce253 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/running insertion.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..2c916c5a Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique.png new file mode 100644 index 00000000..33a719ef Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice.png b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice.png new file mode 100644 index 00000000..e7109cc3 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered erasure.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..0c0d0442 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..2404d59a Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice.png new file mode 100644 index 00000000..aa2f2670 Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered successful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..7baa248e Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..1ada54ff Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice.png new file mode 100644 index 00000000..bd2402de Binary files /dev/null and b/doc/diagrams/benchmarks/gcc/scattered unsuccessful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..b32e8cf2 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique.png new file mode 100644 index 00000000..c2df113c Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique 5.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique 5.png new file mode 100644 index 00000000..4f70d8b0 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique.png new file mode 100644 index 00000000..f8a1710f Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash non-unique.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash.png new file mode 100644 index 00000000..f9407d3a Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice norehash.png differ diff --git a/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice.png b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice.png new file mode 100644 index 00000000..05b6ef77 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/running insertion.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..73d1703c Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique.png new file mode 100644 index 00000000..475e570f Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice.png b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice.png new file mode 100644 index 00000000..2f2bc13c Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered erasure.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..df655dec Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..e9ae4f25 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice.png new file mode 100644 index 00000000..60e85ba5 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered successful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique 5.png b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique 5.png new file mode 100644 index 00000000..6273e2bb Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique 5.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique.png b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique.png new file mode 100644 index 00000000..2724d755 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice non-unique.png differ diff --git a/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice.png b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice.png new file mode 100644 index 00000000..31be4e90 Binary files /dev/null and b/doc/diagrams/benchmarks/vs/scattered unsuccessful looukp.xlsx.practice.png differ diff --git a/doc/diagrams/bucket-groups.png b/doc/diagrams/bucket-groups.png new file mode 100644 index 00000000..d9c5e96d Binary files /dev/null and b/doc/diagrams/bucket-groups.png differ diff --git a/doc/diagrams/fca.png b/doc/diagrams/fca.png new file mode 100644 index 00000000..d1ecb63c Binary files /dev/null and b/doc/diagrams/fca.png differ diff --git a/doc/diagrams/singly-linked.png b/doc/diagrams/singly-linked.png new file mode 100644 index 00000000..ae3cf61a Binary files /dev/null and b/doc/diagrams/singly-linked.png differ diff --git a/doc/roadmap.md b/doc/roadmap.md new file mode 100644 index 00000000..88267547 --- /dev/null +++ b/doc/roadmap.md @@ -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` 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 +struct pick_node2 +{ + typedef boost::unordered::detail::node node; + // ... +}; + +template +struct pick_node2*, + boost::unordered::detail::ptr_bucket*> +{ + typedef boost::unordered::detail::ptr_node node; + // ... +}; + +template struct pick_node +{ + typedef typename boost::remove_const::type nonconst; + + typedef boost::unordered::detail::allocator_traits< + typename boost::unordered::detail::rebind_wrap >::type> + tentative_node_traits; + + typedef boost::unordered::detail::allocator_traits< + typename boost::unordered::detail::rebind_wrap::type> + tentative_bucket_traits; + + typedef pick_node2 + 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 +struct node : boost::unordered::detail::value_base +{ + 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 +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 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 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 +struct map { + // ... + typedef boost::unordered::detail::table table; + // ... +}; +``` + +Examining the `detail::table` struct, we see: +```cpp +template +struct table { + // ... + typedef typename Types::iterator iterator; + // ... +} +``` + +Collapsing all of this, we see that our iterator types are defined here: +```cpp +template +struct map +{ + // ... + typedef boost::unordered::detail::pick_node pick; + typedef typename pick::node node; + + typedef boost::unordered::iterator_detail::iterator iterator; + typedef boost::unordered::iterator_detail::c_iterator c_iterator; + typedef boost::unordered::iterator_detail::l_iterator l_iterator; + typedef boost::unordered::iterator_detail::cl_iterator + cl_iterator; + // ... +}; +``` + +This is similarly designed for `detail::set`: +```cpp +typedef boost::unordered::iterator_detail::c_iterator iterator; +typedef boost::unordered::iterator_detail::c_iterator c_iterator; +typedef boost::unordered::iterator_detail::cl_iterator l_iterator; +typedef boost::unordered::iterator_detail::cl_iterator + cl_iterator; +``` + +The only difference here is that `set::iterator` is always a `c_iterator`, a `const_iterator` type. diff --git a/doc/unordered/buckets.adoc b/doc/unordered/buckets.adoc index 28cca9b9..fcaf516c 100644 --- a/doc/unordered/buckets.adoc +++ b/doc/unordered/buckets.adoc @@ -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 <> on the `rehash` function. +== Fast Closed Addressing Implementation + +++++ + +++++ + +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 <>. + += Benchmarks + +All benchmarks were created using `unordered_set` (non-duplicate) and `unordered_multiset` (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 + +|=== diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 79a28a51..f930e680 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -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: diff --git a/doc/unordered/rationale.adoc b/doc/unordered/rationale.adoc index fe4a7707..43617a66 100644 --- a/doc/unordered/rationale.adoc +++ b/doc/unordered/rationale.adoc @@ -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. diff --git a/include/boost/unordered/detail/fca.hpp b/include/boost/unordered/detail/fca.hpp new file mode 100644 index 00000000..900b61ea --- /dev/null +++ b/include/boost/unordered/detail/fca.hpp @@ -0,0 +1,1000 @@ +// Copyright (C) 2022 Joaquin M Lopez Munoz. +// 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) + +#ifndef BOOST_UNORDERED_DETAIL_FCA_HPP +#define BOOST_UNORDERED_DETAIL_FCA_HPP + +/* + +The general structure of the fast closed addressing implementation is that we +use straight-forward separate chaining (i.e. each bucket contains its own linked +list) and then improve iteration time by adding an array of "bucket groups". + +A bucket group is a constant-width view into a subsection of the buckets array, +containing a bitmask that indicates which one of the buckets in the subsection +contains a list of nodes. This allows the code to test N buckets for occupancy +in a single operation. Additional speed can be found by inter-linking occupied +bucket groups with one another in a doubly-linked list. To this end, large +swathes of the bucket groups array no longer need to be iterated and have their +bitmasks examined for occupancy. + +A bucket group iterator contains a pointer to a bucket group along with a +pointer into the buckets array. The iterator's bucket pointer is guaranteed to +point to a bucket within the bucket group's view of the array. To advance the +iterator, we need to determine if we need to skip to the next bucket group or +simply move to the next occupied bucket as denoted by the bitmask. + +To accomplish this, we perform something roughly equivalent to this: +``` +bucket_iterator itb = ... +bucket_pointer p = itb.p +bucket_group_pointer pbg = itb.pbg + +offset = p - pbg->buckets +// because we wish to see if the _next_ bit in the mask is occupied, we'll +// generate a testing mask from the current offset + 1 +// +testing_mask = reset_first_bits(offset + 1) +n = ctz(pbg->bitmask & testing_mask) + +if (n < N) { + p = pbg->buckets + n +} else { + pbg = pbg->next + p = pbg->buckets + ctz(pbg->bitmask) +} +``` + +`reset_first_bits` yields an unsigned integral with the first n bits set to 0 +and then by counting the number of trailing zeroes when AND'd against the bucket +group's bitmask, we can derive the offset into the buckets array. When the +calculated offset is equal to N, we know we've reached the end of a bucket group +and we can advance to the next one. + +This is a rough explanation for how iterator incrementation should work for a +fixed width size of N as 3 for the bucket groups +``` +N = 3 +p = buckets +pbg->bitmask = 0b101 +pbg->buckets = buckets + +offset = p - pbg->buckets // => 0 +testing_mask = reset_first_bits(offset + 1) // reset_first_bits(1) => 0b110 + +x = bitmask & testing_mask // => 0b101 & 0b110 => 0b100 +ctz(x) // ctz(0b100) => 2 +// 2 < 3 +=> p = pbg->buckets + 2 + +// increment again... +offset = p - pbg->buckets // => 2 +testing_mask = reset_first_bits(offset + 1) // reset_first_bits(3) => 0b000 + +bitmask & testing_mask // 0b101 & 0b000 => 0b000 +ctz(0b000) => 3 +// 3 < 3 is false now +pbg = pbg->next +initial_offset = ctz(pbg->bitmask) +p = pbg->buckets + initial_offset +``` + +For `size_` number of buckets, there are `1 + (size_ / N)` bucket groups where +`N` is the width of a bucket group, determined at compile-time. + +We allocate space for `size_ + 1` buckets, using the last one as a dummy bucket +which is kept permanently empty so it can act as a sentinel value in the +implementation of `iterator end();`. We set the last bucket group to act as a +sentinel. + +``` +num_groups = size_ / N + 1 +groups = allocate(num_groups) +pbg = groups + (num_groups - 1) + +// not guaranteed to point to exactly N buckets +pbg->buckets = buckets + N * (size_ / N) + +// this marks the true end of the bucket array +buckets pbg->bitmask = set_bit(size_ % N) + +// links in on itself +pbg->next = pbg->prev = pbg +``` + +To this end, we can devise a safe iteration scheme while also creating a useful +sentinel to use as the end iterator. + +Otherwise, usage of the data structure is relatively straight-forward compared +to normal separate chaining implementations. + +*/ + +#include +#if defined(BOOST_HAS_PRAGMA_ONCE) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include + +// `iterator_facade` has transitive dependencies on Boost.MPL; one of the +// headers is generating a `-Wsign-conversion` warning which has an open PR to +// address the issue but merging does not seem likely so for now create a rote +// workaround. +// +// TODO: eventually remove this once MPL is fixed or we decide to migrate off of +// the Boost.Iterator dependency. +// +#if defined(BOOST_GCC) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wconversion" +#include +#pragma GCC diagnostic pop +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost { + namespace unordered { + namespace detail { + +#if defined(SIZE_MAX) +#if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T +#endif +#elif defined(UINTPTR_MAX) /* used as proxy for std::size_t */ +#if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0 +#define BOOST_UNORDERED_FCA_HAS_64B_SIZE_T +#endif +#endif + +#if !defined(BOOST_NO_INT64_T) && \ + (defined(BOOST_HAS_INT128) || (defined(BOOST_MSVC) && defined(_WIN64))) +#define BOOST_UNORDERED_FCA_FASTMOD_SUPPORT +#endif + + template struct prime_fmod_size + { + // Because we compile for C++03, we don't have access to any inline + // initialization for array data members so the definitions must exist + // out-of-line. To keep the library header-only, we introduce a dummy + // template parameter which permits the definition to be included in + // multiple TUs without conflict. + // + static std::size_t sizes[]; + static std::size_t const sizes_len; + static std::size_t (*positions[])(std::size_t); + +#if defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) + static uint64_t inv_sizes32[]; + static std::size_t const inv_sizes32_len; +#endif /* defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) */ + + static inline std::size_t size_index(std::size_t n) + { + std::size_t i = 0; + for (; i < (sizes_len - 1); ++i) { + if (sizes[i] >= n) { + break; + } + } + return i; + } + + static inline std::size_t size(std::size_t size_index) + { + return sizes[size_index]; + } + + template static std::size_t modulo(std::size_t hash) + { + return hash % Size; + } + +#if defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) + // https://github.com/lemire/fastmod + +#if defined(_MSC_VER) + static inline uint64_t mul128_u32(uint64_t lowbits, uint32_t d) + { + return __umulh(lowbits, d); + } +#else + static inline uint64_t mul128_u32(uint64_t lowbits, uint32_t d) + { + __extension__ typedef unsigned __int128 uint128; + return static_cast(((uint128)lowbits * d) >> 64); + } +#endif /* defined(_MSC_VER) */ + + static inline uint32_t fastmod_u32(uint32_t a, uint64_t M, uint32_t d) + { + uint64_t lowbits = M * a; + return (uint32_t)(mul128_u32(lowbits, d)); + } +#endif /* defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) */ + + static inline std::size_t position( + std::size_t hash, std::size_t size_index) + { +#if defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) +#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) + std::size_t sizes_under_32bit = inv_sizes32_len; + if (BOOST_LIKELY(size_index < sizes_under_32bit)) { + return fastmod_u32(uint32_t(hash) + uint32_t(hash >> 32), + inv_sizes32[size_index], uint32_t(sizes[size_index])); + } else { + return positions[size_index - sizes_under_32bit](hash); + } +#else + return fastmod_u32( + hash, inv_sizes32[size_index], uint32_t(sizes[size_index])); +#endif /* defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */ +#else + return positions[size_index](hash); +#endif /* defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) */ + } + }; // prime_fmod_size + +#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE \ + (13ul)(29ul)(53ul)(97ul)(193ul)(389ul)(769ul)(1543ul)(3079ul)(6151ul)( \ + 12289ul)(24593ul)(49157ul)(98317ul)(196613ul)(393241ul)(786433ul)( \ + 1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul)(50331653ul)( \ + 100663319ul)(201326611ul)(402653189ul)(805306457ul)(1610612741ul)( \ + 3221225473ul) + +#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) + +#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \ + BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE(4294967291ul) + +#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT + +#else + +#define BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT \ + BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT_INCOMPLETE + +// The original sequence here is this: +// (6442450939ul) +// (12884901893ul) +// (25769803751ul) +// (51539607551ul) +// (103079215111ul) +// (206158430209ul) +// (412316860441ul) +// (824633720831ul) +// (1649267441651ul) +// +// but this causes problems on versions of mingw where the `long` type is 32 +// bits, even for 64-bit targets. We work around this by replacing the literals +// with compile-time arithmetic, using bitshifts to reconstruct the number. +// + +// clang-format off +#define BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT \ + ((boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(2147483643ul)) \ + ((boost::ulong_long_type(3ul) << 32) + boost::ulong_long_type(5ul)) \ + ((boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(4294967271ul)) \ + ((boost::ulong_long_type(11ul) << 32) + boost::ulong_long_type(4294967295ul)) \ + ((boost::ulong_long_type(24ul) << 32) + boost::ulong_long_type(7ul)) \ + ((boost::ulong_long_type(48ul) << 32) + boost::ulong_long_type(1ul)) \ + ((boost::ulong_long_type(96ul) << 32) + boost::ulong_long_type(25ul)) \ + ((boost::ulong_long_type(191ul) << 32) + boost::ulong_long_type(4294967295ul)) \ + ((boost::ulong_long_type(383ul) << 32) + boost::ulong_long_type(4294967283ul)) + // clang-format on + +#endif /* BOOST_UNORDERED_FCA_HAS_64B_SIZE_T */ + +#define BOOST_UNORDERED_PRIME_FMOD_SIZES \ + BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT + + template + std::size_t prime_fmod_size::sizes[] = { + BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIME_FMOD_SIZES)}; + + template + std::size_t const prime_fmod_size::sizes_len = BOOST_PP_SEQ_SIZE( + BOOST_UNORDERED_PRIME_FMOD_SIZES); + +// Similarly here, we have to re-express the integer initialization using +// arithmetic such that each literal can fit in a 32-bit value. +// +#if defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) + // clang-format off + template + uint64_t prime_fmod_size::inv_sizes32[] = { + (boost::ulong_long_type(330382099ul) << 32) + boost::ulong_long_type(2973438898ul) /* = 1418980313362273202 */, + (boost::ulong_long_type(148102320ul) << 32) + boost::ulong_long_type(2369637129ul) /* = 636094623231363849 */, + (boost::ulong_long_type(81037118ul) << 32) + boost::ulong_long_type(3403558990ul) /* = 348051774975651918 */, + (boost::ulong_long_type(44278013ul) << 32) + boost::ulong_long_type(1549730468ul) /* = 190172619316593316 */, + (boost::ulong_long_type(22253716ul) << 32) + boost::ulong_long_type(2403401389ul) /* = 95578984837873325 */, + (boost::ulong_long_type(11041047ul) << 32) + boost::ulong_long_type(143533612ul) /* = 47420935922132524 */, + (boost::ulong_long_type(5585133ul) << 32) + boost::ulong_long_type(106117528ul) /* = 23987963684927896 */, + (boost::ulong_long_type(2783517ul) << 32) + boost::ulong_long_type(1572687312ul) /* = 11955116055547344 */, + (boost::ulong_long_type(1394922ul) << 32) + boost::ulong_long_type(3428720239ul) /* = 5991147799191151 */, + (boost::ulong_long_type(698255ul) << 32) + boost::ulong_long_type(552319807ul) /* = 2998982941588287 */, + (boost::ulong_long_type(349496ul) << 32) + boost::ulong_long_type(3827689953ul) /* = 1501077717772769 */, + (boost::ulong_long_type(174641ul) << 32) + boost::ulong_long_type(3699438549ul) /* = 750081082979285 */, + (boost::ulong_long_type(87372ul) << 32) + boost::ulong_long_type(1912757574ul) /* = 375261795343686 */, + (boost::ulong_long_type(43684ul) << 32) + boost::ulong_long_type(3821029929ul) /* = 187625172388393 */, + (boost::ulong_long_type(21844ul) << 32) + boost::ulong_long_type(3340590800ul) /* = 93822606204624 */, + (boost::ulong_long_type(10921ul) << 32) + boost::ulong_long_type(4175852267ul) /* = 46909513691883 */, + (boost::ulong_long_type(5461ul) << 32) + boost::ulong_long_type(1401829642ul) /* = 23456218233098 */, + (boost::ulong_long_type(2730ul) << 32) + boost::ulong_long_type(2826028947ul) /* = 11728086747027 */, + (boost::ulong_long_type(1365ul) << 32) + boost::ulong_long_type(1411150351ul) /* = 5864041509391 */, + (boost::ulong_long_type(682ul) << 32) + boost::ulong_long_type(2857253105ul) /* = 2932024948977 */, + (boost::ulong_long_type(341ul) << 32) + boost::ulong_long_type(1431073224ul) /* = 1466014921160 */, + (boost::ulong_long_type(170ul) << 32) + boost::ulong_long_type(2862758116ul) /* = 733007198436 */, + (boost::ulong_long_type(85ul) << 32) + boost::ulong_long_type(1431619357ul) /* = 366503839517 */, + (boost::ulong_long_type(42ul) << 32) + boost::ulong_long_type(2863269661ul) /* = 183251896093 */, + (boost::ulong_long_type(21ul) << 32) + boost::ulong_long_type(1431647119ul) /* = 91625960335 */, + (boost::ulong_long_type(10ul) << 32) + boost::ulong_long_type(2863310962ul) /* = 45812983922 */, + (boost::ulong_long_type(5ul) << 32) + boost::ulong_long_type(1431653234ul) /* = 22906489714 */, + (boost::ulong_long_type(2ul) << 32) + boost::ulong_long_type(2863311496ul) /* = 11453246088 */, + (boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(1431655764ul) /* = 5726623060 */, +#if !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) + }; +#else + (boost::ulong_long_type(1ul) << 32) + boost::ulong_long_type(6ul) /* 4294967302 */ + }; + // clang-format on +#endif /* !defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) */ + + template + std::size_t const + prime_fmod_size::inv_sizes32_len = sizeof(inv_sizes32) / + sizeof(inv_sizes32[0]); + +#endif /* defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) */ + +#define BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT(z, _, n) \ + prime_fmod_size::template modulo, + + template + std::size_t (*prime_fmod_size::positions[])(std::size_t) = { +#if !defined(BOOST_UNORDERED_FCA_FASTMOD_SUPPORT) + BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~, + BOOST_UNORDERED_PRIME_FMOD_SIZES_32BIT) +#endif + +#if defined(BOOST_UNORDERED_FCA_HAS_64B_SIZE_T) + BOOST_PP_SEQ_FOR_EACH(BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT, ~, + BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT) +#endif + }; + +#undef BOOST_UNORDERED_PRIME_FMOD_POSITIONS_ELEMENT +#undef BOOST_UNORDERED_PRIME_FMOD_SIZES +#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_64BIT +#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_34BIT +#undef BOOST_UNORDERED_PRIME_FMOD_SIZES_34BIT_INCOMPLETE + +#ifdef BOOST_UNORDERED_FCA_FASTMOD_SUPPORT +#undef BOOST_UNORDERED_FCA_FASTMOD_SUPPORT +#endif + +#ifdef BOOST_UNORDERED_FCA_HAS_64B_SIZE_T +#undef BOOST_UNORDERED_FCA_HAS_64B_SIZE_T +#endif + + template struct node + { + typedef ValueType value_type; + typedef typename boost::pointer_traits::template rebind_to< + node>::type node_pointer; + + node_pointer next; + typename boost::aligned_storage::value>::type buf; + + node() BOOST_NOEXCEPT : next(), buf() {} + + value_type* value_ptr() BOOST_NOEXCEPT + { + return reinterpret_cast(buf.address()); + } + + value_type& value() BOOST_NOEXCEPT + { + return *reinterpret_cast(buf.address()); + } + }; + + template struct bucket + { + typedef typename boost::pointer_traits::template rebind_to< + Node>::type node_pointer; + + typedef typename boost::pointer_traits::template rebind_to< + bucket>::type bucket_pointer; + + node_pointer next; + + bucket() BOOST_NOEXCEPT : next() {} + }; + + template struct bucket_group + { + typedef typename Bucket::bucket_pointer bucket_pointer; + typedef + typename boost::pointer_traits::template rebind_to< + bucket_group>::type bucket_group_pointer; + + BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT); + + bucket_pointer buckets; + std::size_t bitmask; + bucket_group_pointer next, prev; + + bucket_group() BOOST_NOEXCEPT : buckets(), bitmask(0), next(), prev() {} + ~bucket_group() {} + }; + + inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; } + + inline std::size_t reset_bit(std::size_t n) + { + return ~(std::size_t(1) << n); + } + + inline std::size_t reset_first_bits(std::size_t n) // n>0 + { + return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n)); + } + + template + struct grouped_bucket_iterator + : public boost::iterator_facade, + Bucket, boost::forward_traversal_tag> + { + public: + typedef typename Bucket::bucket_pointer bucket_pointer; + typedef + typename boost::pointer_traits::template rebind_to< + bucket_group >::type bucket_group_pointer; + + typedef typename boost::pointer_traits::difference_type + difference_type; + + private: + bucket_pointer p; + bucket_group_pointer pbg; + + public: + grouped_bucket_iterator() : p(), pbg() {} + + private: + friend class boost::iterator_core_access; + + template + friend class grouped_bucket_array; + + BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group::N); + + grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_) + : p(p_), pbg(pbg_) + { + } + + Bucket& dereference() const BOOST_NOEXCEPT { return *p; } + + bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT + { + return p == x.p; + } + + void increment() BOOST_NOEXCEPT + { + std::size_t const offset = static_cast(p - pbg->buckets); + + std::size_t n = std::size_t(boost::core::countr_zero( + pbg->bitmask & reset_first_bits(offset + 1))); + + if (n < N) { + p = pbg->buckets + static_cast(n); + } else { + pbg = pbg->next; + + std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask); + p = pbg->buckets + x; + } + } + }; + + template struct const_grouped_local_bucket_iterator; + + template + struct grouped_local_bucket_iterator + : public boost::iterator_facade, + typename Node::value_type, boost::forward_traversal_tag> + { + + typedef typename Node::node_pointer node_pointer; + + public: + typedef typename Node::value_type value_type; + typedef value_type element_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + grouped_local_bucket_iterator() : p() {} + + private: + friend class boost::iterator_core_access; + + template + friend class grouped_bucket_array; + + template friend struct const_grouped_local_bucket_iterator; + + grouped_local_bucket_iterator(node_pointer p_) : p(p_) {} + + value_type& dereference() const BOOST_NOEXCEPT { return p->value(); } + + bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT + { + return p == x.p; + } + + void increment() BOOST_NOEXCEPT { p = p->next; } + + node_pointer p; + }; + + template + struct const_grouped_local_bucket_iterator + : public boost::iterator_facade< + const_grouped_local_bucket_iterator, + typename Node::value_type const, boost::forward_traversal_tag> + { + typedef typename Node::node_pointer node_pointer; + + public: + typedef typename Node::value_type const value_type; + typedef value_type const element_type; + typedef value_type const* pointer; + typedef value_type const& reference; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + const_grouped_local_bucket_iterator() : p() {} + const_grouped_local_bucket_iterator( + grouped_local_bucket_iterator it) + : p(it.p) + { + } + + private: + friend class boost::iterator_core_access; + + template + friend class grouped_bucket_array; + + const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {} + + value_type& dereference() const BOOST_NOEXCEPT { return p->value(); } + + bool equal( + const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT + { + return p == x.p; + } + + void increment() BOOST_NOEXCEPT { p = p->next; } + + node_pointer p; + }; + + template struct span + { + T* begin() const BOOST_NOEXCEPT { return data; } + T* end() const BOOST_NOEXCEPT { return data + size; } + + T* data; + std::size_t size; + + span(T* data_, std::size_t size_) : data(data_), size(size_) {} + }; + + template + class grouped_bucket_array + : boost::empty_value::type, + typename boost::allocator_void_pointer::type> >:: + type> + { + BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array) + + typedef typename boost::allocator_value_type::type + allocator_value_type; + typedef + typename boost::allocator_void_pointer::type void_pointer; + typedef typename boost::allocator_difference_type::type + difference_type; + + public: + typedef typename boost::allocator_rebind >::type node_allocator_type; + + typedef node node_type; + typedef typename boost::allocator_pointer::type + node_pointer; + typedef SizePolicy size_policy; + + private: + typedef typename boost::allocator_rebind::type + bucket_allocator_type; + typedef typename boost::allocator_pointer::type + bucket_pointer; + typedef boost::pointer_traits bucket_pointer_traits; + + typedef bucket_group group; + typedef typename boost::allocator_rebind::type + group_allocator_type; + typedef typename boost::allocator_pointer::type + group_pointer; + typedef typename boost::pointer_traits + group_pointer_traits; + + public: + typedef Bucket value_type; + typedef Bucket bucket_type; + typedef std::size_t size_type; + typedef Allocator allocator_type; + typedef grouped_bucket_iterator iterator; + typedef grouped_local_bucket_iterator local_iterator; + typedef const_grouped_local_bucket_iterator + const_local_iterator; + + private: + std::size_t size_index_, size_; + bucket_pointer buckets; + group_pointer groups; + + public: + grouped_bucket_array(size_type n, const Allocator& al) + : empty_value(empty_init_t(), al), + size_index_(size_policy::size_index(n)), + size_(size_policy::size(size_index_)), buckets(), groups() + { + bucket_allocator_type bucket_alloc = this->get_bucket_allocator(); + group_allocator_type group_alloc = this->get_group_allocator(); + + size_type const num_buckets = buckets_len(); + size_type const num_groups = groups_len(); + + buckets = boost::allocator_allocate(bucket_alloc, num_buckets); + BOOST_TRY + { + groups = boost::allocator_allocate(group_alloc, num_groups); + + bucket_type* pb = boost::to_address(buckets); + for (size_type i = 0; i < num_buckets; ++i) { + new (pb + i) bucket_type(); + } + + group* pg = boost::to_address(groups); + for (size_type i = 0; i < num_groups; ++i) { + new (pg + i) group(); + } + } + BOOST_CATCH(...) + { + boost::allocator_deallocate(bucket_alloc, buckets, num_buckets); + BOOST_RETHROW + } + BOOST_CATCH_END + + size_type const N = group::N; + group_pointer pbg = + groups + static_cast(num_groups - 1); + + pbg->buckets = + buckets + static_cast(N * (size_ / N)); + pbg->bitmask = set_bit(size_ % N); + pbg->next = pbg->prev = pbg; + } + + ~grouped_bucket_array() { this->deallocate(); } + + grouped_bucket_array( + BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT + : empty_value( + empty_init_t(), other.get_node_allocator()), + size_index_(other.size_index_), + size_(other.size_), + buckets(other.buckets), + groups(other.groups) + { + other.size_ = 0; + other.size_index_ = 0; + other.buckets = bucket_pointer(); + other.groups = group_pointer(); + } + + grouped_bucket_array& operator=( + BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT + { + BOOST_ASSERT( + this->get_node_allocator() == other.get_node_allocator()); + + if (this == boost::addressof(other)) { + return *this; + } + + this->deallocate(); + size_index_ = other.size_index_; + size_ = other.size_; + + buckets = other.buckets; + groups = other.groups; + + other.size_index_ = 0; + other.size_ = 0; + other.buckets = bucket_pointer(); + other.groups = group_pointer(); + + return *this; + } + + void deallocate() BOOST_NOEXCEPT + { + if (buckets) { + bucket_allocator_type bucket_alloc = this->get_bucket_allocator(); + boost::allocator_deallocate( + bucket_alloc, buckets, this->buckets_len()); + + buckets = bucket_pointer(); + } + + if (groups) { + group_allocator_type group_alloc = this->get_group_allocator(); + boost::allocator_deallocate( + group_alloc, groups, this->groups_len()); + + groups = group_pointer(); + } + } + + void swap(grouped_bucket_array& other) + { + std::swap(size_index_, other.size_index_); + std::swap(size_, other.size_); + std::swap(buckets, other.buckets); + std::swap(groups, other.groups); + + bool b = boost::allocator_propagate_on_container_swap< + allocator_type>::type::value; + if (b) { + boost::swap(get_node_allocator(), other.get_node_allocator()); + } + } + + node_allocator_type const& get_node_allocator() const + { + return empty_value::get(); + } + + node_allocator_type& get_node_allocator() + { + return empty_value::get(); + } + + bucket_allocator_type get_bucket_allocator() const + { + return this->get_node_allocator(); + } + + group_allocator_type get_group_allocator() const + { + return this->get_node_allocator(); + } + + size_type buckets_len() const BOOST_NOEXCEPT { return size_ + 1; } + + size_type groups_len() const BOOST_NOEXCEPT + { + return size_ / group::N + 1; + } + + void reset_allocator(Allocator const& allocator_) + { + this->get_node_allocator() = node_allocator_type(allocator_); + } + + size_type bucket_count() const { return size_; } + + iterator begin() const { return ++at(size_); } + + iterator end() const + { + // micro optimization: no need to return the bucket group + // as end() is not incrementable + iterator pbg; + pbg.p = + buckets + static_cast(this->buckets_len() - 1); + return pbg; + } + + local_iterator begin(size_type n) const + { + return local_iterator( + (buckets + static_cast(n))->next); + } + + local_iterator end(size_type) const { return local_iterator(); } + + size_type capacity() const BOOST_NOEXCEPT { return size_; } + + iterator at(size_type n) const + { + std::size_t const N = group::N; + + iterator pbg(buckets + static_cast(n), + groups + static_cast(n / N)); + + return pbg; + } + + span raw() + { + BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len()); + return span(boost::to_address(buckets), size_); + } + + size_type position(std::size_t hash) const + { + return size_policy::position(hash, size_index_); + } + + void clear() + { + this->deallocate(); + size_index_ = 0; + size_ = 0; + } + + void append_bucket_group(iterator itb) BOOST_NOEXCEPT + { + std::size_t const N = group::N; + + bool const is_empty_bucket = (!itb->next); + if (is_empty_bucket) { + bucket_pointer pb = itb.p; + group_pointer pbg = itb.pbg; + + std::size_t n = + static_cast(boost::to_address(pb) - &buckets[0]); + + bool const is_empty_group = (!pbg->bitmask); + if (is_empty_group) { + size_type const num_groups = this->groups_len(); + group_pointer last_group = + groups + static_cast(num_groups - 1); + + pbg->buckets = + buckets + static_cast(N * (n / N)); + pbg->next = last_group->next; + pbg->next->prev = pbg; + pbg->prev = last_group; + pbg->prev->next = pbg; + } + + pbg->bitmask |= set_bit(n % N); + } + } + + void insert_node(iterator itb, node_pointer p) BOOST_NOEXCEPT + { + this->append_bucket_group(itb); + + p->next = itb->next; + itb->next = p; + } + + void insert_node_hint( + iterator itb, node_pointer p, node_pointer hint) BOOST_NOEXCEPT + { + this->append_bucket_group(itb); + + if (hint) { + p->next = hint->next; + hint->next = p; + } else { + p->next = itb->next; + itb->next = p; + } + } + + void extract_node(iterator itb, node_pointer p) BOOST_NOEXCEPT + { + node_pointer* pp = boost::addressof(itb->next); + while ((*pp) != p) + pp = boost::addressof((*pp)->next); + *pp = p->next; + if (!itb->next) + unlink_bucket(itb); + } + + void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT + { + *pp = (*pp)->next; + if (!itb->next) + unlink_bucket(itb); + } + + void unlink_empty_buckets() BOOST_NOEXCEPT + { + std::size_t const N = group::N; + + group_pointer pbg = groups, + last = groups + static_cast( + this->groups_len() - 1); + + for (; pbg != last; ++pbg) { + if (!pbg->buckets) { + continue; + } + + for (std::size_t n = 0; n < N; ++n) { + bucket_pointer bs = pbg->buckets; + bucket_type& b = bs[static_cast(n)]; + if (!b.next) + pbg->bitmask &= reset_bit(n); + } + if (!pbg->bitmask && pbg->next) + unlink_group(pbg); + } + + // do not check end bucket + for (std::size_t n = 0; n < size_ % N; ++n) { + if (!pbg->buckets[static_cast(n)].next) + pbg->bitmask &= reset_bit(n); + } + } + + void unlink_bucket(iterator itb) + { + typename iterator::bucket_pointer p = itb.p; + typename iterator::bucket_group_pointer pbg = itb.pbg; + if (!(pbg->bitmask &= + reset_bit(static_cast(p - pbg->buckets)))) + unlink_group(pbg); + } + + private: + void unlink_group(group_pointer pbg) + { + pbg->next->prev = pbg->prev; + pbg->prev->next = pbg->next; + pbg->prev = pbg->next = group_pointer(); + } + }; + } // namespace detail + } // namespace unordered +} // namespace boost + +#endif // BOOST_UNORDERED_DETAIL_FCA_HPP diff --git a/include/boost/unordered/detail/fwd.hpp b/include/boost/unordered/detail/fwd.hpp index e749ce67..acaa8f11 100644 --- a/include/boost/unordered/detail/fwd.hpp +++ b/include/boost/unordered/detail/fwd.hpp @@ -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 diff --git a/include/boost/unordered/detail/implementation.hpp b/include/boost/unordered/detail/implementation.hpp index 668a5984..8544917d 100644 --- a/include/boost/unordered/detail/implementation.hpp +++ b/include/boost/unordered/detail/implementation.hpp @@ -1,5 +1,7 @@ // Copyright (C) 2003-2004 Jeremy B. Maitin-Shepard. // Copyright (C) 2005-2016 Daniel James +// Copyright (C) 2022 Joaquin M Lopez Munoz. +// 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) @@ -14,9 +16,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -45,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +146,11 @@ #define BOOST_UNORDERED_CXX11_CONSTRUCTION 0 #endif +#if BOOST_UNORDERED_CXX11_CONSTRUCTION +#include +#include +#endif + // BOOST_UNORDERED_SUPPRESS_DEPRECATED // // Define to stop deprecation attributes @@ -186,27 +194,11 @@ #define BOOST_UNORDERED_TEMPLATE_DEDUCTION_GUIDES 0 #endif -namespace boost { - namespace unordered { - namespace iterator_detail { - template struct iterator; - template struct c_iterator; - template struct l_iterator; - template struct cl_iterator; - } - } -} - namespace boost { namespace unordered { namespace detail { template struct table; - template struct bucket; - struct ptr_bucket; - - template struct node; - template struct ptr_node; static const float minimum_max_load_factor = 1e-3f; static const std::size_t default_bucket_count = 11; @@ -257,75 +249,9 @@ namespace boost { } } -//////////////////////////////////////////////////////////////////////////////// -// primes - -// clang-format off -#define BOOST_UNORDERED_PRIMES \ - (17ul)(29ul)(37ul)(53ul)(67ul)(79ul) \ - (97ul)(131ul)(193ul)(257ul)(389ul)(521ul)(769ul) \ - (1031ul)(1543ul)(2053ul)(3079ul)(6151ul)(12289ul)(24593ul) \ - (49157ul)(98317ul)(196613ul)(393241ul)(786433ul) \ - (1572869ul)(3145739ul)(6291469ul)(12582917ul)(25165843ul) \ - (50331653ul)(100663319ul)(201326611ul)(402653189ul)(805306457ul) \ - (1610612741ul)(3221225473ul)(4294967291ul) -// clang-format on - namespace boost { namespace unordered { namespace detail { - template struct prime_list_template - { - static std::size_t const value[]; - -#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 - static std::ptrdiff_t const length; -#else - static std::ptrdiff_t const length = - BOOST_PP_SEQ_SIZE(BOOST_UNORDERED_PRIMES); -#endif - }; - - template - std::size_t const prime_list_template::value[] = { - BOOST_PP_SEQ_ENUM(BOOST_UNORDERED_PRIMES)}; - -#if !BOOST_UNORDERED_SUN_WORKAROUNDS1 - template - std::ptrdiff_t const prime_list_template::length = BOOST_PP_SEQ_SIZE( - BOOST_UNORDERED_PRIMES); -#endif - -#undef BOOST_UNORDERED_PRIMES - - typedef prime_list_template prime_list; - - // no throw - inline std::size_t next_prime(std::size_t num) - { - std::size_t const* const prime_list_begin = prime_list::value; - std::size_t const* const prime_list_end = - prime_list_begin + prime_list::length; - std::size_t const* bound = - std::lower_bound(prime_list_begin, prime_list_end, num); - if (bound == prime_list_end) - bound--; - return *bound; - } - - // no throw - inline std::size_t prev_prime(std::size_t num) - { - std::size_t const* const prime_list_begin = prime_list::value; - std::size_t const* const prime_list_end = - prime_list_begin + prime_list::length; - std::size_t const* bound = - std::upper_bound(prime_list_begin, prime_list_end, num); - if (bound != prime_list_begin) - bound--; - return *bound; - } - ////////////////////////////////////////////////////////////////////////// // insert_size/initial_size @@ -356,34 +282,27 @@ namespace boost { ////////////////////////////////////////////////////////////////////////// // compressed - template struct compressed_base : private T + template + struct compressed_base : boost::empty_value { - compressed_base(T const& x) : T(x) {} - compressed_base(T& x, move_tag) : T(boost::move(x)) {} + compressed_base(T const& x) : empty_value(boost::empty_init_t(), x) + { + } + compressed_base(T& x, move_tag) + : empty_value(boost::empty_init_t(), boost::move(x)) + { + } - T& get() { return *this; } - T const& get() const { return *this; } - }; - - template struct uncompressed_base - { - uncompressed_base(T const& x) : value_(x) {} - uncompressed_base(T& x, move_tag) : value_(boost::move(x)) {} - - T& get() { return value_; } - T const& get() const { return value_; } - - private: - T value_; + T& get() { return empty_value::get(); } + T const& get() const { return empty_value::get(); } }; template - struct generate_base - : boost::detail::if_true< - boost::is_empty::value>::BOOST_NESTED_TEMPLATE - then, - boost::unordered::detail::uncompressed_base > + struct generate_base : boost::unordered::detail::compressed_base { + typedef compressed_base type; + + generate_base() : type() {} }; template @@ -898,10 +817,6 @@ namespace boost { #if BOOST_UNORDERED_CXX11_CONSTRUCTION -#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - Traits::construct(alloc, address, a0) -#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) Traits::destroy(alloc, x) - #elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) namespace boost { @@ -918,11 +833,6 @@ namespace boost { } } -#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::construct_value(address, a0) -#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ - boost::unordered::detail::func::destroy(x) - #else namespace boost { @@ -944,11 +854,6 @@ namespace boost { } } -#define BOOST_UNORDERED_CALL_CONSTRUCT1(Traits, alloc, address, a0) \ - boost::unordered::detail::func::construct_value(address, a0) -#define BOOST_UNORDERED_CALL_DESTROY(Traits, alloc, x) \ - boost::unordered::detail::func::destroy(x) - #endif //////////////////////////////////////////////////////////////////////////// @@ -1130,7 +1035,7 @@ namespace boost { inline void construct_from_args( Alloc& alloc, T* address, BOOST_FWD_REF(Args)... args) { - boost::unordered::detail::allocator_traits::construct( + boost::allocator_construct( alloc, address, boost::forward(args)...); } @@ -1156,6 +1061,39 @@ namespace boost { // Special case for piecewise_construct + template + std::tuple::type...> + to_std_tuple_impl(boost::mp11::mp_list, + boost::tuple& tuple, boost::mp11::index_sequence) + { + (void) tuple; + return std::tuple::type...>( + boost::get(tuple)...); + } + + template + boost::mp11::index_sequence_for make_index_seq( + boost::mp11::mp_list) + { + return boost::mp11::index_sequence_for{}; + } + + template + using add_lvalue_reference_t = + typename std::add_lvalue_reference::type; + + template + boost::mp11::mp_transform, + boost::tuples::null_type> > + to_std_tuple(boost::tuple& tuple) + { + using list = boost::mp11::mp_remove, + boost::tuples::null_type>; + + return to_std_tuple_impl(list{}, tuple, make_index_seq(list{})); + } + template inline typename boost::enable_if_c::value && @@ -1165,20 +1103,8 @@ namespace boost { construct_from_args(Alloc& alloc, std::pair* address, BOOST_FWD_REF(A0), BOOST_FWD_REF(A1) a1, BOOST_FWD_REF(A2) a2) { - boost::unordered::detail::func::construct_from_tuple( - alloc, boost::addressof(address->first), boost::forward(a1)); - BOOST_TRY - { - boost::unordered::detail::func::construct_from_tuple( - alloc, boost::addressof(address->second), boost::forward(a2)); - } - BOOST_CATCH(...) - { - boost::unordered::detail::func::destroy( - boost::addressof(address->first)); - BOOST_RETHROW - } - BOOST_CATCH_END + boost::allocator_construct(alloc, address, std::piecewise_construct, + to_std_tuple(a1), to_std_tuple(a2)); } #elif !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) @@ -1338,14 +1264,6 @@ namespace boost { return p; } - void reclaim(node_pointer p) - { - BOOST_ASSERT(!node_); - node_ = p; - BOOST_UNORDERED_CALL_DESTROY( - node_allocator_traits, alloc_, node_->value_ptr()); - } - private: node_constructor(node_constructor const&); node_constructor& operator=(node_constructor const&); @@ -1368,10 +1286,11 @@ namespace boost { template struct node_tmp { - typedef boost::unordered::detail::allocator_traits - node_allocator_traits; - typedef typename node_allocator_traits::pointer node_pointer; - typedef typename node_allocator_traits::value_type node; + typedef typename boost::allocator_value_type::type node; + typedef typename boost::allocator_pointer::type node_pointer; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; NodeAlloc& alloc_; node_pointer node_; @@ -1392,10 +1311,9 @@ namespace boost { template node_tmp::~node_tmp() { if (node_) { - BOOST_UNORDERED_CALL_DESTROY( - node_allocator_traits, alloc_, node_->value_ptr()); - boost::unordered::detail::func::destroy(boost::to_address(node_)); - node_allocator_traits::deallocate(alloc_, node_, 1); + value_allocator val_alloc(alloc_); + boost::allocator_destroy(val_alloc, node_->value_ptr()); + boost::allocator_deallocate(alloc_, node_, 1); } } } @@ -1411,73 +1329,106 @@ namespace boost { // improve implementation later. template - inline - typename boost::unordered::detail::allocator_traits::pointer - construct_node_from_args(Alloc& alloc, BOOST_UNORDERED_EMPLACE_ARGS) + inline typename boost::allocator_pointer::type + construct_node_from_args(Alloc& alloc, BOOST_UNORDERED_EMPLACE_ARGS) { + typedef typename boost::allocator_value_type::type node; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; + + value_allocator val_alloc(alloc); + node_constructor a(alloc); a.create_node(); construct_from_args( - alloc, a.node_->value_ptr(), BOOST_UNORDERED_EMPLACE_FORWARD); + val_alloc, a.node_->value_ptr(), BOOST_UNORDERED_EMPLACE_FORWARD); return a.release(); } template - inline - typename boost::unordered::detail::allocator_traits::pointer - construct_node(Alloc& alloc, BOOST_FWD_REF(U) x) + inline typename boost::allocator_pointer::type construct_node( + Alloc& alloc, BOOST_FWD_REF(U) x) { node_constructor a(alloc); a.create_node(); - BOOST_UNORDERED_CALL_CONSTRUCT1( - boost::unordered::detail::allocator_traits, alloc, - a.node_->value_ptr(), boost::forward(x)); + + typedef typename boost::allocator_value_type::type node; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; + + value_allocator val_alloc(alloc); + + boost::allocator_construct( + val_alloc, a.node_->value_ptr(), boost::forward(x)); return a.release(); } #if BOOST_UNORDERED_CXX11_CONSTRUCTION template - inline - typename boost::unordered::detail::allocator_traits::pointer - construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) + inline typename boost::allocator_pointer::type + construct_node_pair(Alloc& alloc, BOOST_FWD_REF(Key) k) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::allocator_traits::construct(alloc, - a.node_->value_ptr(), std::piecewise_construct, + + typedef typename boost::allocator_value_type::type node; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; + + value_allocator val_alloc(alloc); + + boost::allocator_construct( + val_alloc, a.node_->value_ptr(), std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple()); return a.release(); } template - inline - typename boost::unordered::detail::allocator_traits::pointer - construct_node_pair( - Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) + inline typename boost::allocator_pointer::type + construct_node_pair( + Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Mapped) m) { node_constructor a(alloc); a.create_node(); - boost::unordered::detail::allocator_traits::construct(alloc, - a.node_->value_ptr(), std::piecewise_construct, + + typedef typename boost::allocator_value_type::type node; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; + + value_allocator val_alloc(alloc); + + boost::allocator_construct(val_alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(m))); return a.release(); } template - inline - typename boost::unordered::detail::allocator_traits::pointer - construct_node_pair_from_args( - Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Args)... args) + inline typename boost::allocator_pointer::type + construct_node_pair_from_args( + Alloc& alloc, BOOST_FWD_REF(Key) k, BOOST_FWD_REF(Args)... args) { node_constructor a(alloc); a.create_node(); #if !(BOOST_COMP_CLANG && BOOST_COMP_CLANG < BOOST_VERSION_NUMBER(3, 8, 0) && \ defined(BOOST_LIBSTDCXX11)) - boost::unordered::detail::allocator_traits::construct(alloc, - a.node_->value_ptr(), std::piecewise_construct, + + typedef typename boost::allocator_value_type::type node; + typedef typename node::value_type value_type; + typedef typename boost::allocator_rebind::type + value_allocator; + + value_allocator val_alloc(alloc); + + boost::allocator_construct(val_alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::forward_as_tuple(boost::forward(args)...)); #else @@ -1485,8 +1436,8 @@ namespace boost { // rvalue reference members when using older versions of clang with // libstdc++, so just use std::make_tuple instead of // std::forward_as_tuple. - boost::unordered::detail::allocator_traits::construct(alloc, - a.node_->value_ptr(), std::piecewise_construct, + boost::allocator_construct(val_alloc, a.node_->value_ptr(), + std::piecewise_construct, std::forward_as_tuple(boost::forward(k)), std::make_tuple(boost::forward(args)...)); #endif @@ -1585,514 +1536,9 @@ namespace boost { #pragma warning(pop) #endif -// The 'iterator_detail' namespace was a misguided attempt at avoiding ADL -// in the detail namespace. It didn't work because the template parameters -// were in detail. I'm not changing it at the moment to be safe. I might -// do in the future if I change the iterator types. -namespace boost { - namespace unordered { - namespace iterator_detail { - - ////////////////////////////////////////////////////////////////////////// - // Iterators - // - // all no throw - - template struct l_iterator - { -#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template - friend struct boost::unordered::iterator_detail::cl_iterator; - - private: -#endif - typedef typename Node::node_pointer node_pointer; - node_pointer ptr_; - std::size_t bucket_; - std::size_t bucket_count_; - - public: - typedef typename Node::value_type element_type; - typedef typename Node::value_type value_type; - typedef value_type* pointer; - typedef value_type& reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; - - l_iterator() BOOST_NOEXCEPT : ptr_() {} - - l_iterator(node_pointer n, std::size_t b, std::size_t c) BOOST_NOEXCEPT - : ptr_(n), - bucket_(b), - bucket_count_(c) - { - } - - value_type& operator*() const { return ptr_->value(); } - - value_type* operator->() const { return ptr_->value_ptr(); } - - l_iterator& operator++() - { - ptr_ = static_cast(ptr_->next_); - if (ptr_ && ptr_->get_bucket() != bucket_) - ptr_ = node_pointer(); - return *this; - } - - l_iterator operator++(int) - { - l_iterator tmp(*this); - ++(*this); - return tmp; - } - - bool operator==(l_iterator x) const BOOST_NOEXCEPT - { - return ptr_ == x.ptr_; - } - - bool operator!=(l_iterator x) const BOOST_NOEXCEPT - { - return ptr_ != x.ptr_; - } - }; - - template struct cl_iterator - { - friend struct boost::unordered::iterator_detail::l_iterator; - - private: - typedef typename Node::node_pointer node_pointer; - node_pointer ptr_; - std::size_t bucket_; - std::size_t bucket_count_; - - public: - typedef typename Node::value_type const element_type; - typedef typename Node::value_type value_type; - typedef value_type const* pointer; - typedef value_type const& reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; - - cl_iterator() BOOST_NOEXCEPT : ptr_() {} - - cl_iterator(node_pointer n, std::size_t b, std::size_t c) BOOST_NOEXCEPT - : ptr_(n), - bucket_(b), - bucket_count_(c) - { - } - - cl_iterator( - boost::unordered::iterator_detail::l_iterator const& x) - BOOST_NOEXCEPT : ptr_(x.ptr_), - bucket_(x.bucket_), - bucket_count_(x.bucket_count_) - { - } - - value_type const& operator*() const { return ptr_->value(); } - - value_type const* operator->() const { return ptr_->value_ptr(); } - - cl_iterator& operator++() - { - ptr_ = static_cast(ptr_->next_); - if (ptr_ && ptr_->get_bucket() != bucket_) - ptr_ = node_pointer(); - return *this; - } - - cl_iterator operator++(int) - { - cl_iterator tmp(*this); - ++(*this); - return tmp; - } - - friend bool operator==( - cl_iterator const& x, cl_iterator const& y) BOOST_NOEXCEPT - { - return x.ptr_ == y.ptr_; - } - - friend bool operator!=( - cl_iterator const& x, cl_iterator const& y) BOOST_NOEXCEPT - { - return x.ptr_ != y.ptr_; - } - }; - - template struct iterator - { -#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template - friend struct boost::unordered::iterator_detail::c_iterator; - template friend struct boost::unordered::detail::table; - - private: -#endif - typedef typename Node::node_pointer node_pointer; - node_pointer node_; - - public: - typedef typename Node::value_type element_type; - typedef typename Node::value_type value_type; - typedef value_type* pointer; - typedef value_type& reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; - - iterator() BOOST_NOEXCEPT : node_() {} - - explicit iterator(typename Node::link_pointer x) BOOST_NOEXCEPT - : node_(static_cast(x)) - { - } - - value_type& operator*() const { return node_->value(); } - - value_type* operator->() const { return node_->value_ptr(); } - - iterator& operator++() - { - node_ = static_cast(node_->next_); - return *this; - } - - iterator operator++(int) - { - iterator tmp(node_); - node_ = static_cast(node_->next_); - return tmp; - } - - bool operator==(iterator const& x) const BOOST_NOEXCEPT - { - return node_ == x.node_; - } - - bool operator!=(iterator const& x) const BOOST_NOEXCEPT - { - return node_ != x.node_; - } - }; - - template struct c_iterator - { - friend struct boost::unordered::iterator_detail::iterator; - -#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) - template friend struct boost::unordered::detail::table; - - private: -#endif - typedef typename Node::node_pointer node_pointer; - typedef boost::unordered::iterator_detail::iterator n_iterator; - node_pointer node_; - - public: - typedef typename Node::value_type const element_type; - typedef typename Node::value_type value_type; - typedef value_type const* pointer; - typedef value_type const& reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; - - c_iterator() BOOST_NOEXCEPT : node_() {} - - explicit c_iterator(typename Node::link_pointer x) BOOST_NOEXCEPT - : node_(static_cast(x)) - { - } - - c_iterator(n_iterator const& x) BOOST_NOEXCEPT : node_(x.node_) {} - - value_type const& operator*() const { return node_->value(); } - - value_type const* operator->() const { return node_->value_ptr(); } - - c_iterator& operator++() - { - node_ = static_cast(node_->next_); - return *this; - } - - c_iterator operator++(int) - { - c_iterator tmp(node_); - node_ = static_cast(node_->next_); - return tmp; - } - - friend bool operator==( - c_iterator const& x, c_iterator const& y) BOOST_NOEXCEPT - { - return x.node_ == y.node_; - } - - friend bool operator!=( - c_iterator const& x, c_iterator const& y) BOOST_NOEXCEPT - { - return x.node_ != y.node_; - } - }; - } - } -} - namespace boost { namespace unordered { namespace detail { - - /////////////////////////////////////////////////////////////////// - // - // Node Holder - // - // Temporary store for nodes. Deletes any that aren't used. - - template struct node_holder - { - private: - typedef NodeAlloc node_allocator; - typedef boost::unordered::detail::allocator_traits - node_allocator_traits; - typedef typename node_allocator_traits::value_type node; - typedef typename node_allocator_traits::pointer node_pointer; - typedef typename node::value_type value_type; - typedef typename node::link_pointer link_pointer; - typedef boost::unordered::iterator_detail::iterator iterator; - - node_constructor constructor_; - node_pointer nodes_; - - public: - template - explicit node_holder(Table& b) : constructor_(b.node_alloc()), nodes_() - { - if (b.size_) { - typename Table::link_pointer prev = b.get_previous_start(); - nodes_ = static_cast(prev->next_); - prev->next_ = link_pointer(); - b.size_ = 0; - } - } - - ~node_holder(); - - node_pointer pop_node() - { - node_pointer n = nodes_; - nodes_ = static_cast(nodes_->next_); - n->next_ = link_pointer(); - return n; - } - - template inline node_pointer copy_of(T const& v) - { - if (nodes_) { - constructor_.reclaim(pop_node()); - } else { - constructor_.create_node(); - } - BOOST_UNORDERED_CALL_CONSTRUCT1(node_allocator_traits, - constructor_.alloc_, constructor_.node_->value_ptr(), v); - return constructor_.release(); - } - - template inline node_pointer move_copy_of(T& v) - { - if (nodes_) { - constructor_.reclaim(pop_node()); - } else { - constructor_.create_node(); - } - BOOST_UNORDERED_CALL_CONSTRUCT1(node_allocator_traits, - constructor_.alloc_, constructor_.node_->value_ptr(), - boost::move(v)); - return constructor_.release(); - } - - iterator begin() const { return iterator(nodes_); } - }; - - template node_holder::~node_holder() - { - while (nodes_) { - node_pointer p = nodes_; - nodes_ = static_cast(p->next_); - - BOOST_UNORDERED_CALL_DESTROY( - node_allocator_traits, constructor_.alloc_, p->value_ptr()); - boost::unordered::detail::func::destroy(boost::to_address(p)); - node_allocator_traits::deallocate(constructor_.alloc_, p, 1); - } - } - - /////////////////////////////////////////////////////////////////// - // - // Bucket - - template struct bucket - { - typedef NodePointer link_pointer; - link_pointer next_; - - bucket() : next_() {} - bucket(link_pointer n) : next_(n) {} - - link_pointer first_from_start() { return next_; } - - enum - { - extra_node = true - }; - }; - - struct ptr_bucket - { - typedef ptr_bucket* link_pointer; - link_pointer next_; - - ptr_bucket() : next_(0) {} - ptr_bucket(link_pointer n) : next_(n) {} - - link_pointer first_from_start() { return this; } - - enum - { - extra_node = false - }; - }; - - /////////////////////////////////////////////////////////////////// - // - // Hash Policy - - template struct prime_policy - { - template - static inline SizeT apply_hash(Hash const& hf, T const& x) - { - return hf(x); - } - - static inline SizeT to_bucket(SizeT bucket_count, SizeT hash, int /*bcount_log2*/) - { - return hash % bucket_count; - } - - static inline SizeT new_bucket_count(SizeT min) - { - return boost::unordered::detail::next_prime(min); - } - - static inline SizeT prev_bucket_count(SizeT max) - { - return boost::unordered::detail::prev_prime(max); - } - }; - - template struct mix64_policy - { - template - static inline SizeT apply_hash(Hash const& hf, T const& x) - { - // https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing - // SizeT const m = 11400714819323198485ull; // 2^64 / phi - SizeT const m = ( SizeT(0x9e3779b9u) << 32 ) + 0x7f4a7c15u; - return hf(x) * m; - } - - static inline SizeT to_bucket(SizeT bucket_count, SizeT hash, int bcount_log2) - { - BOOST_ASSERT( bucket_count == ( SizeT(1) << bcount_log2 ) ); - (void)bucket_count; - - SizeT r = hash >> (64 - bcount_log2); - - BOOST_ASSERT( r < bucket_count ); - - return r; - } - - static inline SizeT new_bucket_count(SizeT min) - { - if (min <= 4) - return 4; - return boost::core::bit_ceil(min); - } - - static inline SizeT prev_bucket_count(SizeT max) - { - return boost::core::bit_floor(max); - } - }; - - template struct mix32_policy - { - template - static inline SizeT apply_hash(Hash const& hf, T const& x) - { - // https://en.wikipedia.org/wiki/Hash_function#Fibonacci_hashing - SizeT const m = 2654435769u; // 2^32 / phi - return hf(x) * m; - } - - static inline SizeT to_bucket(SizeT bucket_count, SizeT hash, int bcount_log2) - { - BOOST_ASSERT( bucket_count == ( SizeT(1) << bcount_log2 ) ); - (void)bucket_count; - - SizeT r = hash >> (32 - bcount_log2); - - BOOST_ASSERT( r < bucket_count ); - - return r; - } - - static inline SizeT new_bucket_count(SizeT min) - { - if (min <= 4) - return 4; - return boost::core::bit_ceil(min); - } - - static inline SizeT prev_bucket_count(SizeT max) - { - return boost::core::bit_floor(max); - } - }; - - template struct pick_policy_impl - { - typedef prime_policy type; - }; - - template <> struct pick_policy_impl<64, 2> - { - typedef mix64_policy type; - }; - - template <> struct pick_policy_impl<32, 2> - { - typedef mix32_policy type; - }; - - template - struct pick_policy2 - : pick_policy_impl::digits, - std::numeric_limits::radix> - { - }; - - template - struct pick_policy : pick_policy2::type> - { - }; - ////////////////////////////////////////////////////////////////////////// // Functions // @@ -2267,6 +1713,108 @@ namespace boost { : static_cast(f); } + ////////////////////////////////////////////////////////////////////////// + // iterator definitions + + namespace iterator_detail { + template class c_iterator; + + template + class iterator + : public boost::iterator_facade, + typename Node::value_type, boost::forward_traversal_tag> + { + public: + typedef typename Node::value_type value_type; + typedef value_type element_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + iterator() : p(), itb(){}; + + private: + typedef typename Node::node_pointer node_pointer; + typedef grouped_bucket_iterator bucket_iterator; + + node_pointer p; + bucket_iterator itb; + + template friend struct boost::unordered::detail::table; + template friend class c_iterator; + friend class boost::iterator_core_access; + + iterator(node_pointer p_, bucket_iterator itb_) : p(p_), itb(itb_) {} + + value_type& dereference() const BOOST_NOEXCEPT { return p->value(); } + + bool equal(const iterator& x) const BOOST_NOEXCEPT + { + return (p == x.p); + } + + void increment() BOOST_NOEXCEPT + { + p = p->next; + if (!p) { + p = (++itb)->next; + } + } + }; + + template + class c_iterator + : public boost::iterator_facade, + typename Node::value_type const, boost::forward_traversal_tag> + { + public: + typedef typename Node::value_type value_type; + typedef value_type const element_type; + typedef value_type const* pointer; + typedef value_type const& reference; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + c_iterator() : p(), itb(){}; + c_iterator(iterator it) : p(it.p), itb(it.itb){}; + + private: + typedef typename Node::node_pointer node_pointer; + typedef grouped_bucket_iterator bucket_iterator; + + node_pointer p; + bucket_iterator itb; + + template friend struct boost::unordered::detail::table; + friend class boost::iterator_core_access; + + c_iterator(node_pointer p_, bucket_iterator itb_) : p(p_), itb(itb_) + { + } + + value_type const& dereference() const BOOST_NOEXCEPT + { + return p->value(); + } + + bool equal(const c_iterator& x) const BOOST_NOEXCEPT + { + return (p == x.p); + } + + void increment() BOOST_NOEXCEPT + { + p = p->next; + if (!p) { + p = (++itb)->next; + } + } + }; + } // namespace iterator_detail + + ////////////////////////////////////////////////////////////////////////// + // table structure used by the containers template struct table : boost::unordered::detail::functions @@ -2276,176 +1824,145 @@ namespace boost { table& operator=(table const&); public: - typedef typename Types::node node; - typedef typename Types::bucket bucket; typedef typename Types::hasher hasher; typedef typename Types::key_equal key_equal; typedef typename Types::const_key_type const_key_type; typedef typename Types::extractor extractor; typedef typename Types::value_type value_type; typedef typename Types::table table_impl; - typedef typename Types::link_pointer link_pointer; - typedef typename Types::policy policy; - typedef typename Types::iterator iterator; - typedef typename Types::c_iterator c_iterator; - typedef typename Types::l_iterator l_iterator; - typedef typename Types::cl_iterator cl_iterator; typedef boost::unordered::detail::functions functions; typedef typename Types::value_allocator value_allocator; - typedef typename boost::unordered::detail::rebind_wrap::type node_allocator; - typedef typename boost::unordered::detail::rebind_wrap::type bucket_allocator; - typedef boost::unordered::detail::allocator_traits - node_allocator_traits; - typedef boost::unordered::detail::allocator_traits - bucket_allocator_traits; - typedef typename node_allocator_traits::pointer node_pointer; - typedef - typename node_allocator_traits::const_pointer const_node_pointer; - typedef typename bucket_allocator_traits::pointer bucket_pointer; - typedef boost::unordered::detail::node_constructor + typedef typename boost::allocator_void_pointer::type void_pointer; + typedef node node_type; + + typedef boost::unordered::detail::grouped_bucket_array< + bucket, value_allocator, prime_fmod_size<> > + bucket_array_type; + + typedef typename bucket_array_type::node_allocator_type + node_allocator_type; + typedef typename boost::allocator_pointer::type node_pointer; + + typedef boost::unordered::detail::node_constructor node_constructor; - typedef boost::unordered::detail::node_tmp node_tmp; + typedef boost::unordered::detail::node_tmp node_tmp; + + typedef typename bucket_array_type::bucket_type bucket_type; + + typedef typename bucket_array_type::iterator bucket_iterator; + + typedef typename bucket_array_type::local_iterator l_iterator; + typedef typename bucket_array_type::const_local_iterator cl_iterator; + + typedef std::size_t size_type; + + typedef iterator_detail::iterator iterator; + typedef iterator_detail::c_iterator c_iterator; typedef std::pair emplace_return; //////////////////////////////////////////////////////////////////////// // Members - boost::unordered::detail::compressed - allocators_; - std::size_t bucket_count_; - int bcount_log2_; std::size_t size_; float mlf_; std::size_t max_load_; - bucket_pointer buckets_; - - private: - void init_bcount_log2() - { - BOOST_ASSERT( bucket_count_ > 0 ); - bcount_log2_ = static_cast( boost::core::bit_width( bucket_count_ ) ) - 1; - } + bucket_array_type buckets_; public: //////////////////////////////////////////////////////////////////////// // Data access - static node_pointer get_node(c_iterator it) { return it.node_; } + size_type bucket_count() const { return buckets_.bucket_count(); } - static node_pointer next_node(link_pointer n) + template + iterator next_group(Key const& k, c_iterator n) const { - return static_cast(n->next_); + c_iterator last = this->end(); + while (n != last && this->key_eq()(k, extractor::extract(*n))) { + ++n; + } + return iterator(n.p, n.itb); } - static node_pointer next_for_find(link_pointer n) + template std::size_t group_count(Key const& k) const { - node_pointer n2 = static_cast(n); - do { - n2 = next_node(n2); - } while (n2 && !n2->is_first_in_group()); - return n2; + if (size_ == 0) { + return 0; + } + std::size_t c = 0; + std::size_t const key_hash = this->hash(k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + bool found = false; + + for (node_pointer pos = itb->next; pos; pos = pos->next) { + if (this->key_eq()(k, this->get_key(pos))) { + ++c; + found = true; + } else if (found) { + break; + } + } + return c; } - node_pointer next_group(node_pointer n) const + node_allocator_type const& node_alloc() const { - node_pointer n1 = n; - do { - n1 = next_node(n1); - } while (n1 && !n1->is_first_in_group()); - return n1; + return buckets_.get_node_allocator(); } - std::size_t group_count(node_pointer n) const + node_allocator_type& node_alloc() { - std::size_t x = 0; - node_pointer it = n; - do { - ++x; - it = next_node(it); - } while (it && !it->is_first_in_group()); - - return x; + return buckets_.get_node_allocator(); } - std::size_t node_bucket(node_pointer n) const - { - return n->get_bucket(); - } - - bucket_allocator const& bucket_alloc() const - { - return allocators_.first(); - } - - node_allocator const& node_alloc() const - { - return allocators_.second(); - } - - bucket_allocator& bucket_alloc() { return allocators_.first(); } - - node_allocator& node_alloc() { return allocators_.second(); } - std::size_t max_bucket_count() const { - // -1 to account for the start bucket. - return policy::prev_bucket_count( - bucket_allocator_traits::max_size(bucket_alloc()) - 1); + typedef typename bucket_array_type::size_policy size_policy; + return size_policy::size(size_policy::size_index( + boost::allocator_max_size(this->node_alloc()))); } - bucket_pointer get_bucket_pointer(std::size_t bucket_index) const + iterator begin() const { - BOOST_ASSERT(buckets_); - return buckets_ + static_cast(bucket_index); + if (size_ == 0) { + return end(); + } + + bucket_iterator itb = buckets_.begin(); + return iterator(itb->next, itb); } - link_pointer get_previous_start() const + iterator end() const { - return get_bucket_pointer(bucket_count_)->first_from_start(); + return iterator(); } - link_pointer get_previous_start(std::size_t bucket_index) const + l_iterator begin(std::size_t bucket_index) const { - return get_bucket_pointer(bucket_index)->next_; - } - - node_pointer begin() const - { - return size_ ? next_node(get_previous_start()) : node_pointer(); - } - - node_pointer begin(std::size_t bucket_index) const - { - if (!size_) - return node_pointer(); - link_pointer prev = get_previous_start(bucket_index); - return prev ? next_node(prev) : node_pointer(); + return buckets_.begin(bucket_index); } std::size_t hash_to_bucket(std::size_t hash_value) const { - return policy::to_bucket(bucket_count_, hash_value, bcount_log2_); + return buckets_.position(hash_value); } std::size_t bucket_size(std::size_t index) const { - node_pointer n = begin(index); - if (!n) - return 0; - + bucket_iterator itb = buckets_.at(index); + node_pointer n = itb->next; std::size_t count = 0; - while (n && node_bucket(n) == index) { + while (n) { ++count; - n = next_node(n); + n = n->next; } - return count; } @@ -2458,10 +1975,9 @@ namespace boost { // From 6.3.1/13: // Only resize when size >= mlf_ * count - max_load_ = buckets_ ? boost::unordered::detail::double_to_size( - ceil(static_cast(mlf_) * - static_cast(bucket_count_))) - : 0; + max_load_ = boost::unordered::detail::double_to_size( + floor(static_cast(mlf_) * + static_cast(buckets_.bucket_count()))); } void max_load_factor(float z) @@ -2471,130 +1987,38 @@ namespace boost { recalculate_max_load(); } - std::size_t min_buckets_for_size(std::size_t size) const - { - BOOST_ASSERT(mlf_ >= minimum_max_load_factor); - - using namespace std; - - // From insert/emplace requirements: - // - // size <= mlf_ * count - // => count >= size / mlf_ - // - // Or from rehash post-condition: - // - // count >= size / mlf_ - - return policy::new_bucket_count( - boost::unordered::detail::double_to_size( - floor(static_cast(size) / static_cast(mlf_)) + - 1)); - } - //////////////////////////////////////////////////////////////////////// // Constructors table(std::size_t num_buckets, hasher const& hf, key_equal const& eq, - node_allocator const& a) - : functions(hf, eq), allocators_(a, a), - bucket_count_(policy::new_bucket_count(num_buckets)), size_(0), - mlf_(1.0f), max_load_(0), buckets_() + node_allocator_type const& a) + : functions(hf, eq), size_(0), mlf_(1.0f), max_load_(0), + buckets_(num_buckets, a) { - init_bcount_log2(); - this->create_buckets(bucket_count_); + recalculate_max_load(); } - table(table const& x, node_allocator const& a) - : functions(x), allocators_(a, a), - bucket_count_(x.min_buckets_for_size(x.size_)), size_(0), - mlf_(x.mlf_), max_load_(0), buckets_() + table(table const& x, node_allocator_type const& a) + : functions(x), size_(0), mlf_(x.mlf_), max_load_(0), + buckets_(x.size_, a) { - init_bcount_log2(); + recalculate_max_load(); } table(table& x, boost::unordered::detail::move_tag m) - : functions(x, m), allocators_(x.allocators_, m), - bucket_count_(x.bucket_count_), size_(x.size_), mlf_(x.mlf_), - max_load_(x.max_load_), buckets_(x.buckets_) + : functions(x, m), size_(x.size_), mlf_(x.mlf_), + max_load_(x.max_load_), buckets_(boost::move(x.buckets_)) { - init_bcount_log2(); - x.buckets_ = bucket_pointer(); x.size_ = 0; x.max_load_ = 0; } - table(table& x, node_allocator const& a, + table(table& x, node_allocator_type const& a, boost::unordered::detail::move_tag m) - : functions(x, m), allocators_(a, a), - bucket_count_(x.bucket_count_), size_(0), mlf_(x.mlf_), - max_load_(0), buckets_() + : functions(x, m), size_(0), mlf_(x.mlf_), max_load_(0), + buckets_(x.bucket_count(), a) { - init_bcount_log2(); - } - - //////////////////////////////////////////////////////////////////////// - // Clear buckets and Create buckets - // - // IMPORTANT: If the container already contains any elements, the - // buckets will not contain any links to them. This will - // need to be dealt with, for example by: - // - deleting them - // - putting them in a 'node_holder' for future use - // (as in assignment) - // - placing them in buckets (see rehash_impl) - - // Clear the bucket pointers. - void clear_buckets() - { - bucket_pointer end = get_bucket_pointer(bucket_count_); - for (bucket_pointer it = buckets_; it != end; ++it) { - it->next_ = node_pointer(); - } - } - - // Create container buckets. If the container already contains any - // buckets - // the linked list will be transferred to the new buckets, but none - // of the bucket pointers will be set. See above note. - // - // Strong exception safety. - void create_buckets(std::size_t new_count) - { - link_pointer dummy_node; - - // Construct the new buckets and dummy node, and destroy the old - // buckets - if (buckets_) { - dummy_node = - (buckets_ + static_cast(bucket_count_))->next_; - bucket_pointer new_buckets = - bucket_allocator_traits::allocate(bucket_alloc(), new_count + 1); - destroy_buckets(); - buckets_ = new_buckets; - } else if (bucket::extra_node) { - node_constructor a(node_alloc()); - a.create_node(); - buckets_ = - bucket_allocator_traits::allocate(bucket_alloc(), new_count + 1); - dummy_node = a.release(); - } else { - dummy_node = link_pointer(); - buckets_ = - bucket_allocator_traits::allocate(bucket_alloc(), new_count + 1); - } - - // nothrow from here... - bucket_count_ = new_count; - init_bcount_log2(); recalculate_max_load(); - - bucket_pointer end = - buckets_ + static_cast(new_count); - for (bucket_pointer i = buckets_; i != end; ++i) { - new ((void*)boost::to_address(i)) bucket(); - } - new ((void*)boost::to_address(end)) bucket(dummy_node); } //////////////////////////////////////////////////////////////////////// @@ -2610,11 +2034,6 @@ namespace boost { BOOST_ASSERT(node_alloc() == other.node_alloc()); } - void swap_allocators(table& other, true_type) - { - allocators_.swap(other.allocators_); - } - // Not nothrow swappable void swap(table& x, false_type) { @@ -2633,14 +2052,7 @@ namespace boost { this->switch_functions(); x.switch_functions(); - swap_allocators( - x, boost::unordered::detail::integral_constant::propagate_on_container_swap::value>()); - - boost::swap(buckets_, x.buckets_); - boost::swap(bucket_count_, x.bucket_count_); - boost::swap(bcount_log2_, x.bcount_log2_); + buckets_.swap(x.buckets_); boost::swap(size_, x.size_); std::swap(mlf_, x.mlf_); std::swap(max_load_, x.max_load_); @@ -2649,14 +2061,7 @@ namespace boost { // Nothrow swappable void swap(table& x, true_type) { - swap_allocators( - x, boost::unordered::detail::integral_constant::propagate_on_container_swap::value>()); - - boost::swap(buckets_, x.buckets_); - boost::swap(bucket_count_, x.bucket_count_); - boost::swap(bcount_log2_, x.bcount_log2_); + buckets_.swap(x.buckets_); boost::swap(size_, x.size_); std::swap(mlf_, x.mlf_); std::swap(max_load_, x.max_load_); @@ -2668,8 +2073,8 @@ namespace boost { // equal, behaviour is undefined. void swap(table& x) { - BOOST_ASSERT(allocator_traits< - node_allocator>::propagate_on_container_swap::value || + BOOST_ASSERT(boost::allocator_propagate_on_container_swap< + node_allocator_type>::type::value || node_alloc() == x.node_alloc()); swap(x, boost::unordered::detail::integral_constant()); @@ -2680,13 +2085,11 @@ namespace boost { // allocators might have already been moved). void move_buckets_from(table& other) { - BOOST_ASSERT(!buckets_); - buckets_ = other.buckets_; - bucket_count_ = other.bucket_count_; - init_bcount_log2(); + buckets_ = boost::move(other.buckets_); + size_ = other.size_; max_load_ = other.max_load_; - other.buckets_ = bucket_pointer(); + other.size_ = 0; other.max_load_ = 0; } @@ -2696,23 +2099,29 @@ namespace boost { { if (this->node_alloc() == src.node_alloc()) { move_buckets_from(src); - } else { - this->create_buckets(this->bucket_count_); - link_pointer prev = this->get_previous_start(); - std::size_t last_bucket = this->bucket_count_; - for (node_pointer n = src.begin(); n; n = next_node(n)) { - std::size_t n_bucket = n->get_bucket(); - if (n_bucket != last_bucket) { - this->get_bucket_pointer(n_bucket)->next_ = prev; - } - node_pointer n2 = boost::unordered::detail::func::construct_node( - this->node_alloc(), boost::move(n->value())); - n2->bucket_info_ = n->bucket_info_; - prev->next_ = n2; - ++size_; - prev = n2; - last_bucket = n_bucket; - } + return; + } + + if (src.size_ == 0) { + return; + } + + BOOST_ASSERT( + buckets_.bucket_count() == src.buckets_.bucket_count()); + + this->reserve(src.size_); + for (iterator pos = src.begin(); pos != src.end(); ++pos) { + node_tmp b(detail::func::construct_node( + this->node_alloc(), boost::move(pos.p->value())), + this->node_alloc()); + + const_key_type& k = this->get_key(b.node_); + std::size_t key_hash = this->hash(k); + + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + buckets_.insert_node(itb, b.release()); + ++size_; } } @@ -2721,82 +2130,28 @@ namespace boost { ~table() { delete_buckets(); } - void destroy_node(node_pointer n) + void delete_node(node_pointer p) { - BOOST_UNORDERED_CALL_DESTROY( - node_allocator_traits, node_alloc(), n->value_ptr()); - boost::unordered::detail::func::destroy(boost::to_address(n)); - node_allocator_traits::deallocate(node_alloc(), n, 1); + node_allocator_type alloc = this->node_alloc(); + + value_allocator val_alloc(alloc); + boost::allocator_destroy(val_alloc, p->value_ptr()); + boost::allocator_deallocate(alloc, p, 1); } void delete_buckets() { - if (buckets_) { - node_pointer n = static_cast( - get_bucket_pointer(bucket_count_)->next_); - - if (bucket::extra_node) { - node_pointer next = next_node(n); - boost::unordered::detail::func::destroy(boost::to_address(n)); - node_allocator_traits::deallocate(node_alloc(), n, 1); - n = next; - } - - while (n) { - node_pointer next = next_node(n); - destroy_node(n); - n = next; - } - - destroy_buckets(); - buckets_ = bucket_pointer(); - max_load_ = 0; - size_ = 0; - } - } - - void destroy_buckets() - { - bucket_pointer end = get_bucket_pointer(bucket_count_ + 1); - for (bucket_pointer it = buckets_; it != end; ++it) { - boost::unordered::detail::func::destroy(boost::to_address(it)); + iterator pos = begin(), last = this->end(); + for (; pos != last;) { + node_pointer p = pos.p; + bucket_iterator itb = pos.itb; + ++pos; + buckets_.extract_node(itb, p); + delete_node(p); + --size_; } - bucket_allocator_traits::deallocate( - bucket_alloc(), buckets_, bucket_count_ + 1); - } - - //////////////////////////////////////////////////////////////////////// - // Fix buckets after delete/extract - // - // (prev,next) should mark an open range of nodes in a single bucket - // which - // have either been unlinked, or are about to be. - - std::size_t fix_bucket( - std::size_t bucket_index, link_pointer prev, node_pointer next) - { - std::size_t bucket_index2 = bucket_index; - - if (next) { - bucket_index2 = node_bucket(next); - - // If next is in the same bucket, then there's nothing to do. - if (bucket_index == bucket_index2) { - return bucket_index2; - } - - // Update the bucket containing next. - get_bucket_pointer(bucket_index2)->next_ = prev; - } - - // Check if this bucket is now empty. - bucket_pointer this_bucket = get_bucket_pointer(bucket_index); - if (this_bucket->next_ == prev) { - this_bucket->next_ = link_pointer(); - } - - return bucket_index2; + buckets_.clear(); } //////////////////////////////////////////////////////////////////////// @@ -2810,16 +2165,17 @@ namespace boost { template void assign(table const& x, UniqueType is_unique) { + typedef typename boost::allocator_propagate_on_container_copy_assignment::type pocca; + if (this != &x) { assign(x, is_unique, boost::unordered::detail::integral_constant:: - propagate_on_container_copy_assignment::value>()); + pocca::value>()); } } template - void assign(table const& x, UniqueType is_unique, false_type) + void assign(table const &x, UniqueType is_unique, false_type) { // Strong exception safety. this->construct_spare_functions(x.current_functions()); @@ -2828,11 +2184,8 @@ namespace boost { mlf_ = x.mlf_; recalculate_max_load(); - if (x.size_ > max_load_) { - create_buckets(min_buckets_for_size(x.size_)); - } else if (size_) { - clear_buckets(); - } + this->reserve_for_insert(x.size_); + this->clear_impl(); } BOOST_CATCH(...) { @@ -2841,31 +2194,36 @@ namespace boost { } BOOST_CATCH_END this->switch_functions(); - assign_buckets(x, is_unique); + copy_buckets(x, is_unique); } template - void assign(table const& x, UniqueType is_unique, true_type) + void assign(table const &x, UniqueType is_unique, true_type) { - if (node_alloc() == x.node_alloc()) { - allocators_.assign(x.allocators_); + if (node_alloc() == x.node_alloc()) + { + buckets_.reset_allocator(x.node_alloc()); assign(x, is_unique, false_type()); - } else { + } + else + { + bucket_array_type new_buckets(x.size_, x.node_alloc()); this->construct_spare_functions(x.current_functions()); this->switch_functions(); // Delete everything with current allocators before assigning // the new ones. delete_buckets(); - allocators_.assign(x.allocators_); + buckets_.reset_allocator(x.node_alloc()); + buckets_ = boost::move(new_buckets); // Copy over other data, all no throw. mlf_ = x.mlf_; - bucket_count_ = min_buckets_for_size(x.size_); - init_bcount_log2(); + reserve(x.size_); // Finally copy the elements. - if (x.size_) { + if (x.size_) + { copy_buckets(x, is_unique); } } @@ -2877,8 +2235,8 @@ namespace boost { if (this != &x) { move_assign(x, is_unique, boost::unordered::detail::integral_constant:: - propagate_on_container_move_assignment::value>()); + boost::allocator_propagate_on_container_move_assignment< + node_allocator_type>::type::value>()); } } @@ -2893,7 +2251,8 @@ namespace boost { this->current_functions().move_assign(x.current_functions()); } delete_buckets(); - allocators_.move_assign(x.allocators_); + + buckets_.reset_allocator(x.buckets_.get_node_allocator()); mlf_ = x.mlf_; move_buckets_from(x); } @@ -2902,6 +2261,7 @@ namespace boost { template void move_assign(table& x, UniqueType is_unique, false_type) { + reserve(x.size_); if (node_alloc() == x.node_alloc()) { move_assign_equal_alloc(x); } else { @@ -2930,12 +2290,8 @@ namespace boost { { mlf_ = x.mlf_; recalculate_max_load(); - - if (x.size_ > max_load_) { - create_buckets(min_buckets_for_size(x.size_)); - } else if (size_) { - clear_buckets(); - } + this->reserve_for_insert(x.size_); + this->clear_impl(); } BOOST_CATCH(...) { @@ -2954,116 +2310,113 @@ namespace boost { return extractor::extract(n->value()); } - std::size_t hash(const_key_type& k) const + template + std::size_t hash(Key const& k) const { - return policy::apply_hash(this->hash_function(), k); + return this->hash_function()(k); } // Find Node - node_pointer find_node(std::size_t key_hash, const_key_type& k) const - { - return this->find_node_impl(key_hash, k, this->key_eq()); - } - - node_pointer find_node(const_key_type& k) const - { - return this->find_node_impl(hash(k), k, this->key_eq()); - } - - template + template node_pointer find_node_impl( - std::size_t key_hash, Key const& k, Pred const& eq) const + Key const& x, bucket_iterator itb) const { - std::size_t bucket_index = this->hash_to_bucket(key_hash); - node_pointer n = this->begin(bucket_index); - - for (;;) { - if (!n) - return n; - - if (eq(k, this->get_key(n))) { - return n; - } else if (this->node_bucket(n) != bucket_index) { - return node_pointer(); + key_equal const& pred = this->key_eq(); + node_pointer p = itb->next; + for (; p; p = p->next) { + if (pred(x, extractor::extract(p->value()))) { + break; } - - n = next_for_find(n); } + return p; } - template - link_pointer find_previous_node_impl( - KeyEqual const& eq, Key const& k, std::size_t const bucket_index) + template node_pointer find_node(Key const& k) const { - link_pointer prev = this->get_previous_start(bucket_index); - if (!prev) { - return prev; - } - - for (;;) { - node_pointer n = next_node(prev); - if (!n) { - return link_pointer(); - } - // the `first_in_group()` checks are required for the multi-containers - // for the unique containers, this condition seems to be always true - // - else if (n->is_first_in_group()) { - if (node_bucket(n) != bucket_index) { - return link_pointer(); - } else if (eq(k, this->get_key(n))) { - return prev; - } - } - prev = n; - } + std::size_t const key_hash = this->hash(k); + return find_node_impl( + k, buckets_.at(buckets_.position(key_hash))); } - // Find the node before the key, so that it can be erased. - link_pointer find_previous_node( - const_key_type& k, std::size_t bucket_index) + node_pointer find_node( + const_key_type& k, bucket_iterator itb) const { - return find_previous_node_impl(this->key_eq(), k, bucket_index); + return find_node_impl(k, itb); + } + + template iterator find(Key const& k) const + { + return this->transparent_find( + k, this->hash_function(), this->key_eq()); + } + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning( \ + disable : 4714) // function 'function' marked as __forceinline not inlined +#endif + template + BOOST_FORCEINLINE iterator transparent_find( + Key const& k, Hash const& h, Pred const& pred) const + { + std::size_t const key_hash = h(k); + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + for (node_pointer p = itb->next; p; p = p->next) { + if (BOOST_LIKELY(pred(k, extractor::extract(p->value())))) { + return iterator(p, itb); + } + } + + return this->end(); + } + +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + + template + node_pointer* find_prev(Key const& key, bucket_iterator itb) + { + key_equal pred = this->key_eq(); + for (node_pointer* pp = boost::addressof(itb->next); *pp; + pp = boost::addressof((*pp)->next)) { + if (pred(key, extractor::extract((*pp)->value()))) { + return pp; + } + } + typedef node_pointer* node_pointer_pointer; + return node_pointer_pointer(); } // Extract and erase template node_pointer extract_by_key_impl(Key const& k) { - if (!this->size_) { + iterator it = this->find(k); + if (it == this->end()) { return node_pointer(); } - std::size_t key_hash = policy::apply_hash(this->hash_function(), k); - std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = - this->find_previous_node_impl(this->key_eq(), k, bucket_index); - if (!prev) { - return node_pointer(); - } - node_pointer n = next_node(prev); - node_pointer n2 = next_node(n); - if (n2) { - n2->set_first_in_group(); - } - prev->next_ = n2; - --this->size_; - this->fix_bucket(bucket_index, prev, n2); - n->next_ = link_pointer(); - return n; - } + buckets_.extract_node(it.itb, it.p); + --size_; - inline node_pointer extract_by_key(const_key_type& k) - { - return extract_by_key_impl(k); + return it.p; } // Reserve and rehash + void transfer_node( + node_pointer p, bucket_type&, bucket_array_type& new_buckets) + { + const_key_type& key = extractor::extract(p->value()); + std::size_t const h = this->hash(key); + bucket_iterator itnewb = new_buckets.at(new_buckets.position(h)); + new_buckets.insert_node(itnewb, p); + } - void reserve_for_insert(std::size_t); void rehash(std::size_t); void reserve(std::size_t); + void reserve_for_insert(std::size_t); void rehash_impl(std::size_t); //////////////////////////////////////////////////////////////////////// @@ -3076,11 +2429,16 @@ namespace boost { if (this->size_ != other.size_) return false; - for (node_pointer n1 = this->begin(); n1; n1 = next_node(n1)) { - node_pointer n2 = other.find_node(other.get_key(n1)); + c_iterator pos = this->begin(); + c_iterator last = this->end(); - if (!n2 || n1->value() != n2->value()) + while (pos != last) { + node_pointer p = pos.p; + node_pointer p2 = other.find_node(this->get_key(p)); + if (!p2 || !(p->value() == p2->value())) { return false; + } + ++pos; } return true; @@ -3088,49 +2446,12 @@ namespace boost { // Emplace/Insert - inline node_pointer add_node_unique( - node_pointer n, std::size_t key_hash) - { - std::size_t bucket_index = this->hash_to_bucket(key_hash); - bucket_pointer b = this->get_bucket_pointer(bucket_index); - - n->bucket_info_ = bucket_index; - n->set_first_in_group(); - - if (!b->next_) { - link_pointer start_node = this->get_previous_start(); - - if (start_node->next_) { - this->get_bucket_pointer(node_bucket(next_node(start_node))) - ->next_ = n; - } - - b->next_ = start_node; - n->next_ = start_node->next_; - start_node->next_ = n; - } else { - n->next_ = b->next_->next_; - b->next_->next_ = n; - } - - ++this->size_; - return n; - } - - inline node_pointer resize_and_add_node_unique( - node_pointer n, std::size_t key_hash) - { - node_tmp b(n, this->node_alloc()); - this->reserve_for_insert(this->size_ + 1); - return this->add_node_unique(b.release(), key_hash); - } - template iterator emplace_hint_unique( c_iterator hint, const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) { - if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { - return iterator(hint.node_); + if (hint.p && this->key_eq()(k, this->get_key(hint.p))) { + return iterator(hint.p, hint.itb); } else { return emplace_unique(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; } @@ -3141,16 +2462,27 @@ namespace boost { const_key_type& k, BOOST_UNORDERED_EMPLACE_ARGS) { std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + node_pointer pos = this->find_node_impl(k, itb); + if (pos) { - return emplace_return(iterator(pos), false); + return emplace_return(iterator(pos, itb), false); } else { - return emplace_return( - iterator(this->resize_and_add_node_unique( - boost::unordered::detail::func::construct_node_from_args( - this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), - key_hash)), - true); + node_tmp b(boost::unordered::detail::func::construct_node_from_args( + this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), + this->node_alloc()); + + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + node_pointer p = b.release(); + buckets_.insert_node(itb, p); + ++size_; + + return emplace_return(iterator(p, itb), true); } } @@ -3161,18 +2493,30 @@ namespace boost { node_tmp b(boost::unordered::detail::func::construct_node_from_args( this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), this->node_alloc()); + const_key_type& k = this->get_key(b.node_); - if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { - return iterator(hint.node_); + if (hint.p && this->key_eq()(k, this->get_key(hint.p))) { + return iterator(hint.p, hint.itb); } - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (pos) { - return iterator(pos); - } else { - return iterator( - this->resize_and_add_node_unique(b.release(), key_hash)); + + std::size_t const key_hash = this->hash(k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + node_pointer p = this->find_node_impl(k, itb); + if (p) { + return iterator(p, itb); } + + if (size_ + 1 > max_load_) { + this->reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + p = b.release(); + buckets_.insert_node(itb, p); + ++size_; + return iterator(p, itb); } template @@ -3181,15 +2525,27 @@ namespace boost { node_tmp b(boost::unordered::detail::func::construct_node_from_args( this->node_alloc(), BOOST_UNORDERED_EMPLACE_FORWARD), this->node_alloc()); + const_key_type& k = this->get_key(b.node_); std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + node_pointer pos = this->find_node_impl(k, itb); + if (pos) { - return emplace_return(iterator(pos), false); + return emplace_return(iterator(pos, itb), false); } else { - return emplace_return( - iterator(this->resize_and_add_node_unique(b.release(), key_hash)), - true); + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + node_pointer p = b.release(); + buckets_.insert_node(itb, p); + ++size_; + + return emplace_return(iterator(p, itb), true); } } @@ -3197,24 +2553,38 @@ namespace boost { emplace_return try_emplace_unique(BOOST_FWD_REF(Key) k) { std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + node_pointer pos = this->find_node_impl(k, itb); + if (pos) { - return emplace_return(iterator(pos), false); + return emplace_return(iterator(pos, itb), false); } else { - return emplace_return( - iterator(this->resize_and_add_node_unique( - boost::unordered::detail::func::construct_node_pair( - this->node_alloc(), boost::forward(k)), - key_hash)), - true); + node_allocator_type alloc = node_alloc(); + + node_tmp tmp( + detail::func::construct_node_pair(alloc, boost::forward(k)), + alloc); + + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + node_pointer p = tmp.release(); + buckets_.insert_node(itb, p); + + ++size_; + return emplace_return(iterator(p, itb), true); } } template iterator try_emplace_hint_unique(c_iterator hint, BOOST_FWD_REF(Key) k) { - if (hint.node_ && this->key_eq()(hint->first, k)) { - return iterator(hint.node_); + if (hint.p && this->key_eq()(hint->first, k)) { + return iterator(hint.p, hint.itb); } else { return try_emplace_unique(k).first; } @@ -3225,26 +2595,38 @@ namespace boost { BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) { std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + node_pointer pos = this->find_node_impl(k, itb); + if (pos) { - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node_unique( - boost::unordered::detail::func::construct_node_pair_from_args( - this->node_alloc(), boost::forward(k), - BOOST_UNORDERED_EMPLACE_FORWARD), - key_hash)), - true); + return emplace_return(iterator(pos, itb), false); } + + node_tmp b( + boost::unordered::detail::func::construct_node_pair_from_args( + this->node_alloc(), k, BOOST_UNORDERED_EMPLACE_FORWARD), + this->node_alloc()); + + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + pos = b.release(); + + buckets_.insert_node(itb, pos); + ++size_; + return emplace_return(iterator(pos, itb), true); } template iterator try_emplace_hint_unique( c_iterator hint, BOOST_FWD_REF(Key) k, BOOST_UNORDERED_EMPLACE_ARGS) { - if (hint.node_ && this->key_eq()(hint->first, k)) { - return iterator(hint.node_); + if (hint.p && this->key_eq()(hint->first, k)) { + return iterator(hint.p, hint.itb); } else { return try_emplace_unique(k, BOOST_UNORDERED_EMPLACE_FORWARD).first; } @@ -3255,42 +2637,67 @@ namespace boost { BOOST_FWD_REF(Key) k, BOOST_FWD_REF(M) obj) { std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); - if (pos) { - pos->value().second = boost::forward(obj); - return emplace_return(iterator(pos), false); - } else { - return emplace_return( - iterator(this->resize_and_add_node_unique( - boost::unordered::detail::func::construct_node_pair( - this->node_alloc(), boost::forward(k), - boost::forward(obj)), - key_hash)), - true); + node_pointer p = this->find_node_impl(k, itb); + if (p) { + p->value().second = boost::forward(obj); + return emplace_return(iterator(p, itb), false); } + + node_tmp b(boost::unordered::detail::func::construct_node_pair( + this->node_alloc(), boost::forward(k), + boost::forward(obj)), + node_alloc()); + + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + p = b.release(); + + buckets_.insert_node(itb, p); + ++size_; + return emplace_return(iterator(p, itb), true); } template void move_insert_node_type_unique( NodeType& np, InsertReturnType& result) { - if (np) { - const_key_type& k = this->get_key(np.ptr_); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - result.node = boost::move(np); - result.position = iterator(pos); - } else { - this->reserve_for_insert(this->size_ + 1); - result.position = - iterator(this->add_node_unique(np.ptr_, key_hash)); - result.inserted = true; - np.ptr_ = node_pointer(); - } + if (!np) { + result.position = this->end(); + result.inserted = false; + return; } + + const_key_type& k = this->get_key(np.ptr_); + std::size_t const key_hash = this->hash(k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + node_pointer p = this->find_node_impl(k, itb); + + if (p) { + iterator pos(p, itb); + result.node = boost::move(np); + result.position = pos; + result.inserted = false; + return; + } + + this->reserve_for_insert(size_ + 1); + + p = np.ptr_; + itb = buckets_.at(buckets_.position(key_hash)); + + buckets_.insert_node(itb, p); + np.ptr_ = node_pointer(); + ++size_; + + result.position = iterator(p, itb); + result.inserted = true; } template @@ -3298,53 +2705,68 @@ namespace boost { c_iterator hint, NodeType& np) { if (!np) { - return iterator(); + return this->end(); } + const_key_type& k = this->get_key(np.ptr_); - if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { - return iterator(hint.node_); + if (hint.p && this->key_eq()(k, this->get_key(hint.p))) { + return iterator(hint.p, hint.itb); } - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - if (!pos) { - this->reserve_for_insert(this->size_ + 1); - pos = this->add_node_unique(np.ptr_, key_hash); - np.ptr_ = node_pointer(); + + std::size_t const key_hash = this->hash(k); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + node_pointer p = this->find_node_impl(k, itb); + if (p) { + return iterator(p, itb); } - return iterator(pos); + + p = np.ptr_; + + if (size_ + 1 > max_load_) { + this->reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + buckets_.insert_node(itb, p); + ++size_; + np.ptr_ = node_pointer(); + return iterator(p, itb); } template void merge_unique(boost::unordered::detail::table& other) { typedef boost::unordered::detail::table other_table; - BOOST_STATIC_ASSERT( - (boost::is_same::value)); + BOOST_STATIC_ASSERT((boost::is_same::value)); BOOST_ASSERT(this->node_alloc() == other.node_alloc()); - if (other.size_) { - link_pointer prev = other.get_previous_start(); + if (other.size_ == 0) { + return; + } - while (prev->next_) { - node_pointer n = other_table::next_node(prev); - const_key_type& k = this->get_key(n); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); + this->reserve_for_insert(size_ + other.size_); - if (pos) { - prev = n; - } else { - this->reserve_for_insert(this->size_ + 1); - node_pointer n2 = next_node(n); - prev->next_ = n2; - if (n2 && n->is_first_in_group()) { - n2->set_first_in_group(); - } - --other.size_; - other.fix_bucket(other.node_bucket(n), prev, n2); - this->add_node_unique(n, key_hash); - } + iterator last = other.end(); + for (iterator pos = other.begin(); pos != last;) { + const_key_type& key = other.get_key(pos.p); + std::size_t const key_hash = this->hash(key); + + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + if (this->find_node_impl(key, itb)) { + ++pos; + continue; } + + iterator old = pos; + ++pos; + + node_pointer p = other.extract_by_iterator_unique(old); + buckets_.insert_node(itb, p); + ++size_; } } @@ -3354,63 +2776,34 @@ namespace boost { // if hash function throws, or inserting > 1 element, basic exception // safety strong otherwise - template - void insert_range_unique(const_key_type& k, InputIt i, InputIt j) - { - insert_range_unique2(k, i, j); - - while (++i != j) { - // Note: can't use get_key as '*i' might not be value_type - it - // could be a pair with first_types as key_type without const or - // a different second_type. - insert_range_unique2(extractor::extract(*i), i, j); - } - } - - template - void insert_range_unique2(const_key_type& k, InputIt i, InputIt j) - { - // No side effects in this initial code - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (!pos) { - node_tmp b(boost::unordered::detail::func::construct_node( - this->node_alloc(), *i), - this->node_alloc()); - if (this->size_ + 1 > this->max_load_) - this->reserve_for_insert( - this->size_ + boost::unordered::detail::insert_size(i, j)); - this->add_node_unique(b.release(), key_hash); - } - } - template void insert_range_unique(no_key, InputIt i, InputIt j) { - node_constructor a(this->node_alloc()); + hasher const& hf = this->hash_function(); + node_allocator_type alloc = this->node_alloc(); - do { - if (!a.node_) { - a.create_node(); + for (; i != j; ++i) { + node_tmp tmp(detail::func::construct_node(alloc, *i), alloc); + + value_type const& value = tmp.node_->value(); + const_key_type& key = extractor::extract(value); + std::size_t const h = hf(key); + + bucket_iterator itb = buckets_.at(buckets_.position(h)); + node_pointer it = find_node_impl(key, itb); + if (it) { + continue; } - BOOST_UNORDERED_CALL_CONSTRUCT1( - node_allocator_traits, a.alloc_, a.node_->value_ptr(), *i); - node_tmp b(a.release(), a.alloc_); - const_key_type& k = this->get_key(b.node_); - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - - if (pos) { - a.reclaim(b.release()); - } else { - // reserve has basic exception safety if the hash function - // throws, strong otherwise. - this->reserve_for_insert(this->size_ + 1); - this->add_node_unique(b.release(), key_hash); + if (size_ + 1 > max_load_) { + reserve(size_ + 1); + itb = buckets_.at(buckets_.position(h)); } - } while (++i != j); + + node_pointer nptr = tmp.release(); + buckets_.insert_node(itb, nptr); + ++size_; + } } //////////////////////////////////////////////////////////////////////// @@ -3418,69 +2811,72 @@ namespace boost { inline node_pointer extract_by_iterator_unique(c_iterator i) { - node_pointer n = i.node_; - BOOST_ASSERT(n); - std::size_t bucket_index = this->node_bucket(n); - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != n) { - prev = prev->next_; - } - node_pointer n2 = next_node(n); - prev->next_ = n2; - --this->size_; - this->fix_bucket(bucket_index, prev, n2); - n->next_ = link_pointer(); - return n; + node_pointer p = i.p; + bucket_iterator itb = i.itb; + + buckets_.extract_node(itb, p); + --size_; + + return p; } //////////////////////////////////////////////////////////////////////// // Erase // - // no throw - template - std::size_t erase_key_unique_impl(KeyEqual const& eq, Key const& k) + template std::size_t erase_key_unique_impl(Key const& k) { - if (!this->size_) + bucket_iterator itb = buckets_.at(buckets_.position(this->hash(k))); + node_pointer* pp = this->find_prev(k, itb); + if (!pp) { return 0; + } - std::size_t key_hash = policy::apply_hash(this->hash_function(), k); - std::size_t bucket_index = this->hash_to_bucket(key_hash); - - link_pointer prev = - this->find_previous_node_impl(eq, k, bucket_index); - - if (!prev) - return 0; - - node_pointer n = next_node(prev); - node_pointer n2 = next_node(n); - prev->next_ = n2; + node_pointer p = *pp; + buckets_.extract_node_after(itb, pp); + this->delete_node(p); --size_; - this->fix_bucket(bucket_index, prev, n2); - this->destroy_node(n); - return 1; } - void erase_nodes_unique(node_pointer i, node_pointer j) + iterator erase_nodes_range(c_iterator first, c_iterator last) { - std::size_t bucket_index = this->node_bucket(i); + if (first == last) { + return iterator(last.p, last.itb); + } - // Find the node before i. - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) - prev = prev->next_; + // though `first` stores of a copy of a pointer to a node, we wish to + // mutate the pointers stored internally by the singly-linked list in + // each bucket group so we have to retrieve it manually by iterating + // + bucket_iterator itb = first.itb; + node_pointer* pp = boost::addressof(itb->next); + while (*pp != first.p) { + pp = boost::addressof((*pp)->next); + } - // Delete the nodes. - prev->next_ = j; - do { - node_pointer next = next_node(i); - destroy_node(i); + while (*pp != last.p) { + node_pointer p = *pp; + *pp = (*pp)->next; + + this->delete_node(p); --size_; - bucket_index = this->fix_bucket(bucket_index, prev, next); - i = next; - } while (i != j); + + + bool const at_end = !(*pp); + bool const is_empty_bucket = !itb->next; + + if (at_end) { + if (is_empty_bucket) { + buckets_.unlink_bucket(itb++); + } else { + ++itb; + } + pp = boost::addressof(itb->next); + } + } + + return iterator(last.p, last.itb); } //////////////////////////////////////////////////////////////////////// @@ -3488,32 +2884,45 @@ namespace boost { void copy_buckets(table const& src, true_type) { - this->create_buckets(this->bucket_count_); + BOOST_ASSERT(size_ == 0); - for (node_pointer n = src.begin(); n; n = next_node(n)) { - std::size_t key_hash = this->hash(this->get_key(n)); - this->add_node_unique( - boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - key_hash); - } - } + this->reserve_for_insert(src.size_); - void assign_buckets(table const& src, true_type) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n; n = next_node(n)) { - std::size_t key_hash = this->hash(this->get_key(n)); - this->add_node_unique(holder.copy_of(n->value()), key_hash); + for (iterator pos = src.begin(); pos != src.end(); ++pos) { + value_type const& value = *pos; + const_key_type& key = extractor::extract(value); + std::size_t const key_hash = this->hash(key); + + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + + node_allocator_type alloc = this->node_alloc(); + node_tmp tmp(detail::func::construct_node(alloc, value), alloc); + + buckets_.insert_node(itb, tmp.release()); + ++size_; } } void move_assign_buckets(table& src, true_type) { - node_holder holder(*this); - for (node_pointer n = src.begin(); n; n = next_node(n)) { - std::size_t key_hash = this->hash(this->get_key(n)); - this->add_node_unique(holder.move_copy_of(n->value()), key_hash); + BOOST_ASSERT(size_ == 0); + BOOST_ASSERT(max_load_ >= src.size_); + + iterator last = src.end(); + node_allocator_type alloc = this->node_alloc(); + + for (iterator pos = src.begin(); pos != last; ++pos) { + value_type value = boost::move(*pos); + const_key_type& key = extractor::extract(value); + std::size_t const key_hash = this->hash(key); + + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + + node_tmp tmp( + detail::func::construct_node(alloc, boost::move(value)), alloc); + + buckets_.insert_node(itb, tmp.release()); + ++size_; } } @@ -3527,39 +2936,47 @@ namespace boost { if (this->size_ != other.size_) return false; - for (node_pointer n1 = this->begin(); n1;) { - node_pointer n2 = other.find_node(other.get_key(n1)); - if (!n2) + iterator last = this->end(); + for (iterator n1 = this->begin(); n1 != last;) { + const_key_type& k = extractor::extract(*n1); + iterator n2 = other.find(k); + if (n2 == other.end()) { return false; - node_pointer end1 = next_group(n1); - node_pointer end2 = next_group(n2); - if (!group_equals_equiv(n1, end1, n2, end2)) + } + + iterator end1 = this->next_group(k, n1); + iterator end2 = other.next_group(k, n2); + + if (!group_equals_equiv(n1, end1, n2, end2)) { return false; + } + n1 = end1; } return true; } - static bool group_equals_equiv(node_pointer n1, node_pointer end1, - node_pointer n2, node_pointer end2) + static bool group_equals_equiv(iterator n1, iterator end1, + iterator n2, iterator end2) { for (;;) { - if (n1->value() != n2->value()) + if (*n1 != *n2) break; - n1 = next_node(n1); - n2 = next_node(n2); + ++n1; + ++n2; if (n1 == end1) return n2 == end2; + if (n2 == end2) return false; } - for (node_pointer n1a = n1, n2a = n2;;) { - n1a = next_node(n1a); - n2a = next_node(n2a); + for (iterator n1a = n1, n2a = n2;;) { + ++n1a; + ++n2a; if (n1a == end1) { if (n2a == end2) @@ -3572,14 +2989,16 @@ namespace boost { return false; } - node_pointer start = n1; - for (; n1 != end1; n1 = next_node(n1)) { - value_type const& v = n1->value(); + iterator start = n1; + for (; n1 != end1; ++n1) { + value_type const& v = *n1; if (!find_equiv(start, n1, v)) { std::size_t matches = count_equal_equiv(n2, end2, v); if (!matches) return false; - if (matches != 1 + count_equal_equiv(next_node(n1), end1, v)) + + iterator t = n1; + if (matches != 1 + count_equal_equiv(++t, end1, v)) return false; } } @@ -3588,119 +3007,87 @@ namespace boost { } static bool find_equiv( - node_pointer n, node_pointer end, value_type const& v) + iterator n, iterator last, value_type const& v) { - for (; n != end; n = next_node(n)) - if (n->value() == v) + for (; n != last; ++n) + if (*n == v) return true; return false; } static std::size_t count_equal_equiv( - node_pointer n, node_pointer end, value_type const& v) + iterator n, iterator last, value_type const& v) { std::size_t count = 0; - for (; n != end; n = next_node(n)) - if (n->value() == v) + for (; n != last; ++n) + if (*n == v) ++count; return count; } // Emplace/Insert - inline node_pointer add_node_equiv( - node_pointer n, std::size_t key_hash, node_pointer pos) - { - std::size_t bucket_index = this->hash_to_bucket(key_hash); - n->bucket_info_ = bucket_index; - - if (pos) { - n->reset_first_in_group(); - n->next_ = pos->next_; - pos->next_ = n; - if (n->next_) { - std::size_t next_bucket = this->node_bucket(next_node(n)); - if (next_bucket != bucket_index) { - this->get_bucket_pointer(next_bucket)->next_ = n; - } - } - } else { - n->set_first_in_group(); - bucket_pointer b = this->get_bucket_pointer(bucket_index); - - if (!b->next_) { - link_pointer start_node = this->get_previous_start(); - - if (start_node->next_) { - this - ->get_bucket_pointer(this->node_bucket(next_node(start_node))) - ->next_ = n; - } - - b->next_ = start_node; - n->next_ = start_node->next_; - start_node->next_ = n; - } else { - n->next_ = b->next_->next_; - b->next_->next_ = n; - } - } - ++this->size_; - return n; - } - - inline node_pointer add_using_hint_equiv( - node_pointer n, node_pointer hint) - { - n->bucket_info_ = hint->bucket_info_; - n->reset_first_in_group(); - n->next_ = hint->next_; - hint->next_ = n; - if (n->next_) { - std::size_t next_bucket = this->node_bucket(next_node(n)); - if (next_bucket != this->node_bucket(n)) { - this->get_bucket_pointer(next_bucket)->next_ = n; - } - } - ++this->size_; - return n; - } - iterator emplace_equiv(node_pointer n) { node_tmp a(n, this->node_alloc()); const_key_type& k = this->get_key(a.node_); std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - return iterator( - this->add_node_equiv(a.release(), key_hash, position)); + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + node_pointer hint = this->find_node_impl(k, itb); + + if (size_ + 1 > max_load_) { + this->reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + node_pointer p = a.release(); + buckets_.insert_node_hint(itb, p, hint); + ++size_; + return iterator(p, itb); } iterator emplace_hint_equiv(c_iterator hint, node_pointer n) { node_tmp a(n, this->node_alloc()); const_key_type& k = this->get_key(a.node_); - if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { - this->reserve_for_insert(this->size_ + 1); - return iterator( - this->add_using_hint_equiv(a.release(), hint.node_)); - } else { - std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - return iterator( - this->add_node_equiv(a.release(), key_hash, position)); + bucket_iterator itb = hint.itb; + node_pointer p = hint.p; + + std::size_t key_hash = 0u; + + bool const needs_rehash = (size_ + 1 > max_load_); + bool const usable_hint = (p && this->key_eq()(k, this->get_key(p))); + + if (!usable_hint) { + key_hash = this->hash(k); + itb = buckets_.at(buckets_.position(key_hash)); + p = this->find_node_impl(k, itb); + } else if (usable_hint && needs_rehash) { + key_hash = this->hash(k); } + + if (needs_rehash) { + this->reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); + } + + a.release(); + buckets_.insert_node_hint(itb, n, p); + ++size_; + return iterator(n, itb); } void emplace_no_rehash_equiv(node_pointer n) { + BOOST_ASSERT(size_ + 1 <= max_load_); node_tmp a(n, this->node_alloc()); const_key_type& k = this->get_key(a.node_); std::size_t key_hash = this->hash(k); - node_pointer position = this->find_node(key_hash, k); - this->add_node_equiv(a.release(), key_hash, position); + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + node_pointer hint = this->find_node_impl(k, itb); + node_pointer p = a.release(); + buckets_.insert_node_hint(itb, p, hint); + ++size_; } template @@ -3709,11 +3096,19 @@ namespace boost { iterator result; if (np) { + this->reserve_for_insert(size_ + 1); + const_key_type& k = this->get_key(np.ptr_); std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - result = iterator(this->add_node_equiv(np.ptr_, key_hash, pos)); + + bucket_iterator itb = + buckets_.at(buckets_.position(key_hash)); + + node_pointer hint = this->find_node_impl(k, itb); + buckets_.insert_node_hint(itb, np.ptr_, hint); + ++size_; + + result = iterator(np.ptr_, itb); np.ptr_ = node_pointer(); } @@ -3725,20 +3120,25 @@ namespace boost { c_iterator hint, NodeType& np) { iterator result; - if (np) { + bucket_iterator itb = hint.itb; + node_pointer pos = hint.p; const_key_type& k = this->get_key(np.ptr_); - - if (hint.node_ && this->key_eq()(k, this->get_key(hint.node_))) { - this->reserve_for_insert(this->size_ + 1); - result = - iterator(this->add_using_hint_equiv(np.ptr_, hint.node_)); - } else { - std::size_t key_hash = this->hash(k); - node_pointer pos = this->find_node(key_hash, k); - this->reserve_for_insert(this->size_ + 1); - result = iterator(this->add_node_equiv(np.ptr_, key_hash, pos)); + std::size_t key_hash = this->hash(k); + if (size_ + 1 > max_load_) { + this->reserve(size_ + 1); + itb = buckets_.at(buckets_.position(key_hash)); } + + if (hint.p && this->key_eq()(k, this->get_key(hint.p))) { + } else { + itb = buckets_.at(buckets_.position(key_hash)); + pos = this->find_node_impl(k, itb); + } + buckets_.insert_node_hint(itb, np.ptr_, pos); + ++size_; + result = iterator(np.ptr_, itb); + np.ptr_ = node_pointer(); } @@ -3763,7 +3163,7 @@ namespace boost { this->node_alloc(), *i)); } else { // Only require basic exception safety here - this->reserve_for_insert(this->size_ + distance); + this->reserve_for_insert(size_ + distance); for (; i != j; ++i) { emplace_no_rehash_equiv( @@ -3788,25 +3188,11 @@ namespace boost { inline node_pointer extract_by_iterator_equiv(c_iterator n) { - node_pointer i = n.node_; - BOOST_ASSERT(i); - node_pointer j(next_node(i)); - std::size_t bucket_index = this->node_bucket(i); - - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) { - prev = next_node(prev); - } - - prev->next_ = j; - if (j && i->is_first_in_group()) { - j->set_first_in_group(); - } - --this->size_; - this->fix_bucket(bucket_index, prev, j); - i->next_ = link_pointer(); - - return i; + node_pointer p = n.p; + bucket_iterator itb = n.itb; + buckets_.extract_node(itb, p); + --size_; + return p; } //////////////////////////////////////////////////////////////////////// @@ -3814,64 +3200,32 @@ namespace boost { // // no throw - template - std::size_t erase_key_equiv_impl(KeyEqual const& eq, Key const& k) + template std::size_t erase_key_equiv_impl(Key const& k) { - if (!this->size_) - return 0; - - std::size_t key_hash = policy::apply_hash(this->hash_function(), k); - std::size_t bucket_index = this->hash_to_bucket(key_hash); - link_pointer prev = - this->find_previous_node_impl(eq, k, bucket_index); - if (!prev) - return 0; - std::size_t deleted_count = 0; - node_pointer n = next_node(prev); - do { - node_pointer n2 = next_node(n); - destroy_node(n); - ++deleted_count; - n = n2; - } while (n && !n->is_first_in_group()); - size_ -= deleted_count; - prev->next_ = n; - this->fix_bucket(bucket_index, prev, n); + + bucket_iterator itb = buckets_.at(buckets_.position(this->hash(k))); + node_pointer* pp = this->find_prev(k, itb); + if (pp) { + while (*pp && this->key_eq()(this->get_key(*pp), k)) { + node_pointer p = *pp; + *pp = (*pp)->next; + + this->delete_node(p); + --size_; + ++deleted_count; + } + + if (!itb->next) { + buckets_.unlink_bucket(itb); + } + } return deleted_count; } std::size_t erase_key_equiv(const_key_type& k) { - return this->erase_key_equiv_impl(this->key_eq(), k); - } - - link_pointer erase_nodes_equiv(node_pointer i, node_pointer j) - { - std::size_t bucket_index = this->node_bucket(i); - - link_pointer prev = this->get_previous_start(bucket_index); - while (prev->next_ != i) { - prev = next_node(prev); - } - - // Delete the nodes. - // Is it inefficient to call fix_bucket for every node? - bool includes_first = false; - prev->next_ = j; - do { - includes_first = includes_first || i->is_first_in_group(); - node_pointer next = next_node(i); - destroy_node(i); - --size_; - bucket_index = this->fix_bucket(bucket_index, prev, next); - i = next; - } while (i != j); - if (j && includes_first) { - j->set_first_in_group(); - } - - return prev; + return this->erase_key_equiv_impl(k); } //////////////////////////////////////////////////////////////////////// @@ -3879,50 +3233,46 @@ namespace boost { void copy_buckets(table const& src, false_type) { - this->create_buckets(this->bucket_count_); + BOOST_ASSERT(size_ == 0); - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = this->hash(this->get_key(n)); - node_pointer group_end(next_group(n)); - node_pointer pos = this->add_node_equiv( - boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - key_hash, node_pointer()); - for (n = next_node(n); n != group_end; n = next_node(n)) { - this->add_node_equiv( - boost::unordered::detail::func::construct_node( - this->node_alloc(), n->value()), - key_hash, pos); - } - } - } + this->reserve_for_insert(src.size_); - void assign_buckets(table const& src, false_type) - { - node_holder holder(*this); - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = this->hash(this->get_key(n)); - node_pointer group_end(next_group(n)); - node_pointer pos = this->add_node_equiv( - holder.copy_of(n->value()), key_hash, node_pointer()); - for (n = next_node(n); n != group_end; n = next_node(n)) { - this->add_node_equiv(holder.copy_of(n->value()), key_hash, pos); - } + iterator last = src.end(); + for (iterator pos = src.begin(); pos != last; ++pos) { + value_type const& value = *pos; + const_key_type& key = extractor::extract(value); + std::size_t const key_hash = this->hash(key); + + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + node_allocator_type alloc = this->node_alloc(); + node_tmp tmp(detail::func::construct_node(alloc, value), alloc); + node_pointer hint = this->find_node_impl(key, itb); + buckets_.insert_node_hint(itb, tmp.release(), hint); + ++size_; } } void move_assign_buckets(table& src, false_type) { - node_holder holder(*this); - for (node_pointer n = src.begin(); n;) { - std::size_t key_hash = this->hash(this->get_key(n)); - node_pointer group_end(next_group(n)); - node_pointer pos = this->add_node_equiv( - holder.move_copy_of(n->value()), key_hash, node_pointer()); - for (n = next_node(n); n != group_end; n = next_node(n)) { - this->add_node_equiv( - holder.move_copy_of(n->value()), key_hash, pos); - } + BOOST_ASSERT(size_ == 0); + BOOST_ASSERT(max_load_ >= src.size_); + + iterator last = src.end(); + node_allocator_type alloc = this->node_alloc(); + + for (iterator pos = src.begin(); pos != last; ++pos) { + value_type value = boost::move(*pos); + const_key_type& key = extractor::extract(value); + std::size_t const key_hash = this->hash(key); + + bucket_iterator itb = buckets_.at(buckets_.position(key_hash)); + + node_pointer hint = this->find_node_impl(key, itb); + node_tmp tmp( + detail::func::construct_node(alloc, boost::move(value)), alloc); + + buckets_.insert_node_hint(itb, tmp.release(), hint); + ++size_; } } }; @@ -3932,122 +3282,102 @@ namespace boost { template inline void table::clear_impl() { - if (size_) { - bucket_pointer end = get_bucket_pointer(bucket_count_); - for (bucket_pointer it = buckets_; it != end; ++it) { - it->next_ = node_pointer(); - } - - link_pointer prev = end->first_from_start(); - node_pointer n = next_node(prev); - prev->next_ = node_pointer(); - size_ = 0; - - while (n) { - node_pointer next = next_node(n); - destroy_node(n); - n = next; + bucket_iterator itb = buckets_.begin(), last = buckets_.end(); + for (; itb != last;) { + bucket_iterator next_itb = itb; + ++next_itb; + node_pointer* pp = boost::addressof(itb->next); + while (*pp) { + node_pointer p = *pp; + buckets_.extract_node_after(itb, pp); + this->delete_node(p); + --size_; } + itb = next_itb; } } ////////////////////////////////////////////////////////////////////////// // Reserve & Rehash - // basic exception safety - template - inline void table::reserve_for_insert(std::size_t size) - { - if (!buckets_) { - create_buckets((std::max)(bucket_count_, min_buckets_for_size(size))); - } else if (size > max_load_) { - std::size_t num_buckets = - min_buckets_for_size((std::max)(size, size_ + (size_ >> 1))); - - if (num_buckets != bucket_count_) - this->rehash_impl(num_buckets); - } - } - // if hash function throws, basic exception safety // strong otherwise. - template - inline void table::rehash(std::size_t min_buckets) + inline void table::rehash(std::size_t num_buckets) { - using namespace std; + std::size_t bc = (std::max)(num_buckets, + static_cast(1.0f + static_cast(size_) / mlf_)); - if (!size_) { - min_buckets = policy::new_bucket_count(min_buckets); - if (min_buckets != bucket_count_) { - this->create_buckets(min_buckets); - } - } else { - min_buckets = policy::new_bucket_count((std::max)(min_buckets, - boost::unordered::detail::double_to_size( - floor(static_cast(size_) / static_cast(mlf_))) + - 1)); + if (bc <= buckets_.bucket_count()) { + return; + } - if (min_buckets != bucket_count_) - this->rehash_impl(min_buckets); + this->rehash_impl(bc); + } + + template + inline void table::reserve(std::size_t num_elements) + { + std::size_t const num_buckets = static_cast( + std::ceil(static_cast(num_elements) / mlf_)); + + this->rehash(num_buckets); + } + + template + inline void table::reserve_for_insert(std::size_t num_elements) + { + if (num_elements > max_load_) { + std::size_t const num_buckets = static_cast( + 1.0f + std::ceil(static_cast(num_elements) / mlf_)); + + this->rehash_impl(num_buckets); } } - template + template inline void table::rehash_impl(std::size_t num_buckets) { - BOOST_ASSERT(this->buckets_); + bucket_array_type new_buckets( + num_buckets, buckets_.get_node_allocator()); - this->create_buckets(num_buckets); - link_pointer prev = this->get_previous_start(); BOOST_TRY { - while (prev->next_) { - node_pointer n = next_node(prev); - std::size_t key_hash = this->hash(this->get_key(n)); - std::size_t bucket_index = this->hash_to_bucket(key_hash); + boost::unordered::detail::span bspan = buckets_.raw(); - n->bucket_info_ = bucket_index; - n->set_first_in_group(); + bucket_type* pos = bspan.data; + std::size_t size = bspan.size; + bucket_type* last = pos + size; - // Iterator through the rest of the group of equal nodes, - // setting the bucket. - for (;;) { - node_pointer next = next_node(n); - if (!next || next->is_first_in_group()) { - break; - } - n = next; - n->bucket_info_ = bucket_index; - n->reset_first_in_group(); - } - - // n is now the last node in the group - bucket_pointer b = this->get_bucket_pointer(bucket_index); - if (!b->next_) { - b->next_ = prev; - prev = n; - } else { - link_pointer next = n->next_; - n->next_ = b->next_->next_; - b->next_->next_ = prev->next_; - prev->next_ = next; + for (; pos != last; ++pos) { + bucket_type& b = *pos; + for (node_pointer p = b.next; p;) { + node_pointer next_p = p->next; + transfer_node(p, b, new_buckets); + p = next_p; + b.next = p; } } } BOOST_CATCH(...) { - node_pointer n = next_node(prev); - prev->next_ = node_pointer(); - while (n) { - node_pointer next = next_node(n); - destroy_node(n); - --size_; - n = next; + for (bucket_iterator pos = new_buckets.begin(); + pos != new_buckets.end(); ++pos) { + bucket_type& b = *pos; + for (node_pointer p = b.next; p;) { + node_pointer next_p = p->next; + delete_node(p); + --size_; + p = next_p; + } } - BOOST_RETHROW + buckets_.unlink_empty_buckets(); + BOOST_RETHROW; } BOOST_CATCH_END + + buckets_ = boost::move(new_buckets); + recalculate_max_load(); } #if defined(BOOST_MSVC) @@ -4225,142 +3555,6 @@ namespace boost { #undef BOOST_UNORDERED_KEY_FROM_TUPLE }; - //////////////////////////////////////////////////////////////////////// - // Unique nodes - - template - struct node : boost::unordered::detail::value_base - { - typedef - typename ::boost::unordered::detail::rebind_wrap >::type - allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - allocator>::pointer node_pointer; - typedef node_pointer link_pointer; - typedef typename ::boost::unordered::detail::rebind_wrap >::type bucket_allocator; - typedef typename ::boost::unordered::detail::allocator_traits< - bucket_allocator>::pointer bucket_pointer; - - link_pointer next_; - std::size_t bucket_info_; - - node() : next_(), bucket_info_(0) {} - - std::size_t get_bucket() const - { - return bucket_info_ & ((std::size_t)-1 >> 1); - } - - 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); - } - - private: - node& operator=(node const&); - }; - - template - struct ptr_node : boost::unordered::detail::ptr_bucket - { - typedef T value_type; - typedef boost::unordered::detail::ptr_bucket bucket_base; - typedef ptr_node* node_pointer; - typedef ptr_bucket* link_pointer; - typedef ptr_bucket* bucket_pointer; - - std::size_t bucket_info_; - boost::unordered::detail::value_base value_base_; - - ptr_node() : bucket_base(), bucket_info_(0) {} - - void* address() { return value_base_.address(); } - value_type& value() { return value_base_.value(); } - value_type* value_ptr() { return value_base_.value_ptr(); } - - std::size_t get_bucket() const - { - return bucket_info_ & ((std::size_t)-1 >> 1); - } - - 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); - } - - private: - ptr_node& operator=(ptr_node const&); - }; - - // If the allocator uses raw pointers use ptr_node - // Otherwise use node. - - template - struct pick_node2 - { - typedef boost::unordered::detail::node node; - - typedef typename boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type>::pointer node_pointer; - - typedef boost::unordered::detail::bucket bucket; - typedef node_pointer link_pointer; - }; - - template - struct pick_node2*, - boost::unordered::detail::ptr_bucket*> - { - typedef boost::unordered::detail::ptr_node node; - typedef boost::unordered::detail::ptr_bucket bucket; - typedef bucket* link_pointer; - }; - - template struct pick_node - { - typedef typename boost::remove_const::type nonconst; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap >::type> - tentative_node_traits; - - typedef boost::unordered::detail::allocator_traits< - typename boost::unordered::detail::rebind_wrap::type> - tentative_bucket_traits; - - typedef pick_node2 - pick; - - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - }; - template typename Container::size_type erase_if(Container& c, Predicate& pred) { @@ -4369,7 +3563,7 @@ namespace boost { size_type const size = c.size(); - for (iterator pos = c.begin(), end = c.end(); pos != end;) { + for (iterator pos = c.begin(), last = c.end(); pos != last;) { if (pred(*pos)) { pos = c.erase(pos); } else { @@ -4386,7 +3580,5 @@ namespace boost { #undef BOOST_UNORDERED_EMPLACE_TEMPLATE #undef BOOST_UNORDERED_EMPLACE_ARGS #undef BOOST_UNORDERED_EMPLACE_FORWARD -#undef BOOST_UNORDERED_CALL_CONSTRUCT1 -#undef BOOST_UNORDERED_CALL_DESTROY #endif diff --git a/include/boost/unordered/detail/map.hpp b/include/boost/unordered/detail/map.hpp index 8025de80..ccd2508f 100644 --- a/include/boost/unordered/detail/map.hpp +++ b/include/boost/unordered/detail/map.hpp @@ -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_traits; - typedef boost::unordered::detail::pick_node pick; - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::table table; typedef boost::unordered::detail::map_extractor extractor; - typedef typename boost::unordered::detail::pick_policy::type policy; + typedef typename boost::allocator_void_pointer::type + void_pointer; - typedef boost::unordered::iterator_detail::iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::l_iterator l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; + typedef boost::unordered::node_handle_map< + node, K, M, A> + node_type; - typedef boost::unordered::node_handle_map node_type; - typedef boost::unordered::insert_return_type_map - insert_return_type; + typedef typename table::iterator iterator; + typedef boost::unordered::insert_return_type_map insert_return_type; }; template diff --git a/include/boost/unordered/detail/set.hpp b/include/boost/unordered/detail/set.hpp index b7869e3c..6025e40e 100644 --- a/include/boost/unordered/detail/set.hpp +++ b/include/boost/unordered/detail/set.hpp @@ -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_traits; - typedef boost::unordered::detail::pick_node pick; - typedef typename pick::node node; - typedef typename pick::bucket bucket; - typedef typename pick::link_pointer link_pointer; - typedef boost::unordered::detail::table table; typedef boost::unordered::detail::set_extractor extractor; - typedef typename boost::unordered::detail::pick_policy::type policy; + typedef typename boost::allocator_void_pointer::type + void_pointer; - typedef boost::unordered::iterator_detail::c_iterator iterator; - typedef boost::unordered::iterator_detail::c_iterator c_iterator; - typedef boost::unordered::iterator_detail::cl_iterator l_iterator; - typedef boost::unordered::iterator_detail::cl_iterator - cl_iterator; + typedef boost::unordered::node_handle_set< + node, T, A> + node_type; - typedef boost::unordered::node_handle_set node_type; - typedef boost::unordered::insert_return_type_set + typedef typename table::c_iterator iterator; + typedef boost::unordered::insert_return_type_set insert_return_type; }; diff --git a/include/boost/unordered/unordered_map.hpp b/include/boost/unordered/unordered_map.hpp index 525d74ed..02fe1f9f 100644 --- a/include/boost/unordered/unordered_map.hpp +++ b/include/boost/unordered/unordered_map.hpp @@ -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 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(k)); + return table_.erase_key_unique_impl(boost::forward(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 @@ -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 hash_function(), k), k, - this->key_eq()); + return table_.find(k) != this->end(); } template @@ -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 equal_range(const key_type&); @@ -842,12 +825,13 @@ namespace boost { std::pair >::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 @@ -855,12 +839,13 @@ namespace boost { std::pair >::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 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 @@ -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(k)); + return table_.erase_key_equiv_impl(boost::forward(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 @@ -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 hash_function(), k), k, - this->key_eq()); + return table_.find(k) != this->end(); } template @@ -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 equal_range(const key_type&); @@ -1548,12 +1517,8 @@ namespace boost { std::pair >::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 @@ -1561,19 +1526,16 @@ namespace boost { std::pair >::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::iterator unordered_map::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 typename unordered_map::iterator unordered_map::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 typename unordered_map::size_type unordered_map::erase(const key_type& k) { - return table_.erase_key_unique_impl(this->key_eq(), k); + return table_.erase_key_unique_impl(k); } template @@ -1924,11 +1882,7 @@ namespace boost { unordered_map::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 @@ -1998,14 +1952,14 @@ namespace boost { typename unordered_map::iterator unordered_map::find(const key_type& k) { - return iterator(table_.find_node(k)); + return iterator(table_.find(k)); } template typename unordered_map::const_iterator unordered_map::find(const key_type& k) const { - return const_iterator(table_.find_node(k)); + return const_iterator(table_.find(k)); } template @@ -2015,8 +1969,7 @@ namespace boost { unordered_map::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 @@ -2026,8 +1979,7 @@ namespace boost { unordered_map::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 @@ -2042,8 +1994,12 @@ namespace boost { typename unordered_map::iterator> unordered_map::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 @@ -2051,9 +2007,12 @@ namespace boost { typename unordered_map::const_iterator> unordered_map::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 @@ -2074,10 +2033,12 @@ namespace boost { typename unordered_map::mapped_type& unordered_map::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::mapped_type const& unordered_map::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 float unordered_map::load_factor() const BOOST_NOEXCEPT { - BOOST_ASSERT(table_.bucket_count_ != 0); + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / - static_cast(table_.bucket_count_); + static_cast(table_.bucket_count()); } template @@ -2130,8 +2093,7 @@ namespace boost { template void unordered_map::reserve(size_type n) { - table_.rehash(static_cast( - std::ceil(static_cast(n) / table_.mlf_))); + table_.reserve(n); } template @@ -2377,22 +2339,20 @@ namespace boost { typename unordered_multimap::iterator unordered_multimap::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 typename unordered_multimap::iterator unordered_multimap::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 @@ -2407,11 +2367,7 @@ namespace boost { unordered_multimap::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 @@ -2489,14 +2445,14 @@ namespace boost { typename unordered_multimap::iterator unordered_multimap::find(const key_type& k) { - return iterator(table_.find_node(k)); + return iterator(table_.find(k)); } template typename unordered_multimap::const_iterator unordered_multimap::find(const key_type& k) const { - return const_iterator(table_.find_node(k)); + return const_iterator(table_.find(k)); } template @@ -2506,8 +2462,7 @@ namespace boost { unordered_multimap::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 @@ -2525,8 +2480,7 @@ namespace boost { typename unordered_multimap::size_type unordered_multimap::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 @@ -2534,9 +2488,8 @@ namespace boost { typename unordered_multimap::iterator> unordered_multimap::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 @@ -2544,9 +2497,9 @@ namespace boost { typename unordered_multimap::const_iterator> unordered_multimap::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 @@ -2561,9 +2514,9 @@ namespace boost { template float unordered_multimap::load_factor() const BOOST_NOEXCEPT { - BOOST_ASSERT(table_.bucket_count_ != 0); + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / - static_cast(table_.bucket_count_); + static_cast(table_.bucket_count()); } template @@ -2582,8 +2535,7 @@ namespace boost { template void unordered_multimap::reserve(size_type n) { - table_.rehash(static_cast( - std::ceil(static_cast(n) / table_.mlf_))); + table_.reserve(n); } template @@ -2643,16 +2595,14 @@ namespace boost { template friend class boost::unordered::unordered_multimap; - typedef typename boost::unordered::detail::rebind_wrap >::type value_allocator; - typedef boost::unordered::detail::allocator_traits - value_allocator_traits; + typedef typename boost::allocator_rebind >::type + value_allocator; + typedef N node; - typedef typename boost::unordered::detail::rebind_wrap::type - node_allocator; - typedef boost::unordered::detail::allocator_traits - node_allocator_traits; - typedef typename node_allocator_traits::pointer node_pointer; + typedef typename boost::allocator_rebind::type node_allocator; + + typedef + typename boost::allocator_pointer::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::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 struct insert_return_type_map + template struct insert_return_type_map { private: BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_map) - typedef typename boost::unordered::detail::rebind_wrap >::type value_allocator; - typedef N node_; + // typedef typename boost::allocator_rebind >::type value_allocator; + // typedef N node_; public: + Iter position; bool inserted; - boost::unordered::iterator_detail::iterator position; - boost::unordered::node_handle_map 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 - void swap(insert_return_type_map& x, - insert_return_type_map& y) + template + void swap(insert_return_type_map& x, + insert_return_type_map& y) { boost::swap(x.node, y.node); boost::swap(x.inserted, y.inserted); diff --git a/include/boost/unordered/unordered_map_fwd.hpp b/include/boost/unordered/unordered_map_fwd.hpp index 4f45841b..d713cc49 100644 --- a/include/boost/unordered/unordered_map_fwd.hpp +++ b/include/boost/unordered/unordered_map_fwd.hpp @@ -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& c, Predicate pred); template class node_handle_map; - template struct insert_return_type_map; + template struct insert_return_type_map; } using boost::unordered::unordered_map; diff --git a/include/boost/unordered/unordered_set.hpp b/include/boost/unordered/unordered_set.hpp index b997defd..d1ddc347 100644 --- a/include/boost/unordered/unordered_set.hpp +++ b/include/boost/unordered/unordered_set.hpp @@ -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 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 @@ -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(k)); + return table_.erase_key_unique_impl(boost::forward(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 hash_function(), k), k, - this->key_eq()); + return table_.find(k) != this->end(); } template @@ -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 equal_range( @@ -549,19 +537,20 @@ namespace boost { std::pair >::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 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 @@ -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 hash_function(), k), k, - this->key_eq()); + return table_.find(k) != this->end(); } template @@ -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 equal_range( @@ -1170,19 +1147,16 @@ namespace boost { std::pair >::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::iterator unordered_set::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 typename unordered_set::size_type unordered_set::erase(const key_type& k) { - return table_.erase_key_unique_impl(this->key_eq(), k); + return table_.erase_key_unique_impl(k); } template typename unordered_set::iterator unordered_set::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 @@ -1575,7 +1543,7 @@ namespace boost { typename unordered_set::const_iterator unordered_set::find(const key_type& k) const { - return const_iterator(table_.find_node(k)); + return const_iterator(table_.find(k)); } template @@ -1585,8 +1553,7 @@ namespace boost { unordered_set::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 @@ -1601,9 +1568,12 @@ namespace boost { typename unordered_set::const_iterator> unordered_set::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 @@ -1618,9 +1588,9 @@ namespace boost { template float unordered_set::load_factor() const BOOST_NOEXCEPT { - BOOST_ASSERT(table_.bucket_count_ != 0); + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / - static_cast(table_.bucket_count_); + static_cast(table_.bucket_count()); } template @@ -1638,8 +1608,7 @@ namespace boost { template void unordered_set::reserve(size_type n) { - table_.rehash(static_cast( - std::ceil(static_cast(n) / table_.mlf_))); + table_.reserve(n); } template @@ -1883,11 +1852,11 @@ namespace boost { typename unordered_multiset::iterator unordered_multiset::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 @@ -1902,11 +1871,7 @@ namespace boost { unordered_multiset::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 @@ -1984,7 +1949,7 @@ namespace boost { typename unordered_multiset::const_iterator unordered_multiset::find(const key_type& k) const { - return const_iterator(table_.find_node(k)); + return const_iterator(table_.find(k)); } template @@ -1994,16 +1959,14 @@ namespace boost { unordered_multiset::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 typename unordered_multiset::size_type unordered_multiset::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 @@ -2011,9 +1974,9 @@ namespace boost { typename unordered_multiset::const_iterator> unordered_multiset::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 @@ -2028,9 +1991,9 @@ namespace boost { template float unordered_multiset::load_factor() const BOOST_NOEXCEPT { - BOOST_ASSERT(table_.bucket_count_ != 0); + BOOST_ASSERT(table_.bucket_count() != 0); return static_cast(table_.size_) / - static_cast(table_.bucket_count_); + static_cast(table_.bucket_count()); } template @@ -2048,8 +2011,7 @@ namespace boost { template void unordered_multiset::reserve(size_type n) { - table_.rehash(static_cast( - std::ceil(static_cast(n) / table_.mlf_))); + table_.reserve(n); } template @@ -2217,25 +2179,25 @@ namespace boost { x.swap(y); } - template struct insert_return_type_set + template struct insert_return_type_set { private: BOOST_MOVABLE_BUT_NOT_COPYABLE(insert_return_type_set) - typedef typename boost::unordered::detail::rebind_wrap::type - value_allocator; - typedef N node_; + // typedef typename boost::unordered::detail::rebind_wrap::type + // value_allocator; + // typedef N node_; public: + Iter position; bool inserted; - boost::unordered::iterator_detail::c_iterator position; - boost::unordered::node_handle_set 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 + template void swap( - insert_return_type_set& x, insert_return_type_set& y) + insert_return_type_set& x, insert_return_type_set& y) { boost::swap(x.node, y.node); boost::swap(x.inserted, y.inserted); diff --git a/include/boost/unordered/unordered_set_fwd.hpp b/include/boost/unordered/unordered_set_fwd.hpp index 10a29ac6..3cd34bc9 100644 --- a/include/boost/unordered/unordered_set_fwd.hpp +++ b/include/boost/unordered/unordered_set_fwd.hpp @@ -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& c, Predicate pred); template class node_handle_set; - template struct insert_return_type_set; + template struct insert_return_type_set; } using boost::unordered::unordered_set; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 698dff92..58417c90 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -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 : : : msvc-14.0: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 : : : BOOST_UNORDERED_USE_MOVE diff --git a/test/objects/cxx11_allocator.hpp b/test/objects/cxx11_allocator.hpp index 8f3a7711..9e5749ce 100644 --- a/test/objects/cxx11_allocator.hpp +++ b/test/objects/cxx11_allocator.hpp @@ -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 + 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 - void construct(T* p, BOOST_FWD_REF(Args)... args) +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) { - detail::tracker.track_construct((void*)p, sizeof(T), tag_); - new (p) T(boost::forward(args)...); + detail::tracker.track_construct((void*)p, sizeof(U), tag_); + new (p) U(boost::forward(args)...); } #endif - void destroy(T* p) + template + 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 diff --git a/test/objects/exception.hpp b/test/objects/exception.hpp index b684d686..b5570e1d 100644 --- a/test/objects/exception.hpp +++ b/test/objects/exception.hpp @@ -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 + 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 void construct(T* p, BOOST_FWD_REF(Args)... args) +#else + template 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)...); + new (p) U(boost::forward(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 + 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 + 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 void construct(T* p, BOOST_FWD_REF(Args)... args) +#else + template + 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)...); + new (p) U(boost::forward(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 + 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 diff --git a/test/objects/minimal.hpp b/test/objects/minimal.hpp index b89e0af8..f5298265 100644 --- a/test/objects/minimal.hpp +++ b/test/objects/minimal.hpp @@ -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 +#include #include #include #include @@ -309,13 +312,17 @@ namespace test { return tmp; } ptr operator+(std::ptrdiff_t s) const { return ptr(ptr_ + s); } - friend ptr operator+(std::ptrdiff_t s, ptr p) - { - return ptr(s + p.ptr_); - } + friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr(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 void construct(T* p, BOOST_FWD_REF(Args)... args) +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template void construct(U* p, V const& v) { - new ((void*)p) T(boost::forward(args)...); + new ((void*)p) U(v); + } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new ((void*)p) U(boost::forward(args)...); } #endif - void destroy(T* p) { p->~T(); } + template 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 - void construct(T const* p, BOOST_FWD_REF(Args)... args) +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template void construct(U* p, U const& t) { - new ((void*)p) T(boost::forward(args)...); + new (p) U(t); + } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new (p) U(boost::forward(args)...); } #endif - void destroy(T const* p) { p->~T(); } + template 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 void construct(T* p, BOOST_FWD_REF(Args)... args) +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + template void construct(U* p, V const& v) { - new ((void*)p) T(boost::forward(args)...); + new ((void*)p) U(v); + } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new ((void*)p) U(boost::forward(args)...); } #endif - void destroy(T* p) { p->~T(); } + template 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 struct rebind_to + { + typedef ::test::minimal::ptr type; + }; + }; +} + #endif diff --git a/test/objects/test.hpp b/test/objects/test.hpp index 137c01c8..0bcf78e8 100644 --- a/test/objects/test.hpp +++ b/test/objects/test.hpp @@ -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; 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(ptr_ + s); } friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr(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 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 void construct(T* p, BOOST_FWD_REF(Args)... args) +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) { - detail::tracker.track_construct((void*)p, sizeof(T), tag_); - new (p) T(boost::forward(args)...); + detail::tracker.track_construct((void*)p, sizeof(U), tag_); + new (p) U(boost::forward(args)...); } #endif - void destroy(T* p) + template 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 struct rebind_to + { + typedef ::test::ptr type; + }; + }; +} // namespace boost + #endif diff --git a/test/unordered/detail_tests.cpp b/test/unordered/detail_tests.cpp deleted file mode 100644 index 545c8381..00000000 --- a/test/unordered/detail_tests.cpp +++ /dev/null @@ -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 -#include -#include "../helpers/postfix.hpp" -// clang-format on - -#include "../helpers/test.hpp" -#include - -// 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() diff --git a/test/unordered/mix_policy.cpp b/test/unordered/mix_policy.cpp deleted file mode 100644 index 57c0445b..00000000 --- a/test/unordered/mix_policy.cpp +++ /dev/null @@ -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 -#include -#include -#include - -template 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 policy; - - for( SizeT i = 1; i < 200; ++i ) - { - test( i ); - } - - for( int i = 8; i < 64; ++i ) - { - SizeT x = SizeT( 1 ) << i; - - test( x - 1 ); - test( x ); - test( x + 1 ); - } - } - - { - typedef boost::uint32_t SizeT; - typedef boost::unordered::detail::mix32_policy policy; - - for( SizeT i = 1; i < 200; ++i ) - { - test( i ); - } - - for( int i = 8; i < 32; ++i ) - { - SizeT x = SizeT( 1 ) << i; - - test( x - 1 ); - test( x ); - test( x + 1 ); - } - } - - return boost::report_errors(); -} diff --git a/test/unordered/reserve_tests.cpp b/test/unordered/reserve_tests.cpp index bc3936cf..f0d83b91 100644 --- a/test/unordered/reserve_tests.cpp +++ b/test/unordered/reserve_tests.cpp @@ -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 struct A : B +template struct A { typedef T value_type; - A() {} + static int count; + int i; - template A(const A&) BOOST_NOEXCEPT {} + A() : i(++count) {} + + template A(const A& a) BOOST_NOEXCEPT : i(a.i) {} T* allocate(std::size_t n) { @@ -51,8 +43,13 @@ template 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 int A::count = 0; + template void bucket_count_constructor() { BOOST_TEST_EQ(num_allocations, 0u); @@ -152,9 +149,11 @@ template 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 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 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, float>::type alloc_rebound; + alloc_rebound b; + A a(b); + BOOST_ASSERT(alloc_rebound(a) == b); + BOOST_ASSERT(A(b) == a); + } + typedef boost::unordered_set, std::equal_to, A > unordered_set; diff --git a/test/unordered/scary_tests.cpp b/test/unordered/scary_tests.cpp new file mode 100644 index 00000000..f671f68e --- /dev/null +++ b/test/unordered/scary_tests.cpp @@ -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 +#include +#include "../helpers/postfix.hpp" +// clang-format on + +#include "../helpers/test.hpp" +#include "../objects/test.hpp" + +#include + +struct hash1 +{ + template std::size_t operator()(Key const&) const { return 1337; } +}; + +struct hash2 +{ + template std::size_t operator()(Key const&) const { return 7331; } +}; + +struct equal1 +{ + template bool operator==(T const&) const { return true; } +}; + +struct equal2 +{ + template 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 struct allocator1 +{ +#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS +public: +#else + template 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 pointer; + typedef test::const_ptr const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind + { + typedef allocator1 other; + }; + + allocator1() {} + + template allocator1(allocator1 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(::operator new(n * sizeof(T)))); + return p; + } + + pointer allocate(size_type n, void const*) + { + pointer ptr(static_cast(::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 void construct(U* p, V const& v) { new (p) U(v); } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new (p) U(boost::forward(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 + void destroy(U* p) + { + (void)p; + p->~U(); + } + + size_type max_size() const { return (std::numeric_limits::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 struct allocator2 +{ +#ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS +public: +#else + template 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 pointer; + typedef test::const_ptr const_pointer; + typedef T& reference; + typedef T const& const_reference; + typedef T value_type; + + template struct rebind + { + typedef allocator2 other; + }; + + allocator2() {} + + template allocator2(allocator2 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(::operator new(n * sizeof(T)))); + return p; + } + + pointer allocate(size_type n, void const*) + { + pointer ptr(static_cast(::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 void construct(U* p, V const& v) { new (p) U(v); } +#else + template + void construct(U* p, BOOST_FWD_REF(Args)... args) + { + new (p) U(boost::forward(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 + void destroy(U* p) + { + (void)p; + p->~U(); + } + + size_type max_size() const { return (std::numeric_limits::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 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 Map> +void map_scary_test() +{ + typedef std::pair value_type; + typedef std::allocator std_allocator_type; + + typedef Map, std_allocator_type> + hash1_unordered_map; + typedef Map, std_allocator_type> + hash2_unordered_map; + + typedef Map, equal1, std_allocator_type> + equal1_unordered_map; + typedef Map, equal2, std_allocator_type> + equal2_unordered_map; + + // test allocators with a raw pointer as their `::pointer` type + // + typedef Map, std::equal_to, + test::allocator1 > + alloc1_unordered_map; + typedef Map, std::equal_to, + std_allocator_type> + std_alloc_unordered_map; + + // test allocators with a fancy pointer as their `::pointer` type + // + typedef Map, std::equal_to, + allocator1 > + fancy1_unordered_map; + typedef Map, std::equal_to, + allocator2 > + fancy2_unordered_map; + + scary_test(); + scary_test(); + scary_test(); + scary_test(); +} + +template