Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-09 08:28:05

0001 // Copyright (C) 2022-2024 Joaquin M Lopez Munoz.
0002 // Copyright (C) 2022 Christian Mazakas
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 
0007 #ifndef BOOST_UNORDERED_DETAIL_FCA_HPP
0008 #define BOOST_UNORDERED_DETAIL_FCA_HPP
0009 
0010 /*
0011 
0012 The general structure of the fast closed addressing implementation is that we
0013 use straight-forward separate chaining (i.e. each bucket contains its own linked
0014 list) and then improve iteration time by adding an array of "bucket groups".
0015 
0016 A bucket group is a constant-width view into a subsection of the buckets array,
0017 containing a bitmask that indicates which one of the buckets in the subsection
0018 contains a list of nodes. This allows the code to test N buckets for occupancy
0019 in a single operation. Additional speed can be found by inter-linking occupied
0020 bucket groups with one another in a doubly-linked list. To this end, large
0021 swathes of the bucket groups array no longer need to be iterated and have their
0022 bitmasks examined for occupancy.
0023 
0024 A bucket group iterator contains a pointer to a bucket group along with a
0025 pointer into the buckets array. The iterator's bucket pointer is guaranteed to
0026 point to a bucket within the bucket group's view of the array. To advance the
0027 iterator, we need to determine if we need to skip to the next bucket group or
0028 simply move to the next occupied bucket as denoted by the bitmask.
0029 
0030 To accomplish this, we perform something roughly equivalent to this:
0031 ```
0032 bucket_iterator itb = ...
0033 bucket_pointer p = itb.p
0034 bucket_group_pointer pbg = itb.pbg
0035 
0036 offset = p - pbg->buckets
0037 // because we wish to see if the _next_ bit in the mask is occupied, we'll
0038 // generate a testing mask from the current offset + 1
0039 //
0040 testing_mask = reset_first_bits(offset + 1)
0041 n = ctz(pbg->bitmask & testing_mask)
0042 
0043 if (n < N) {
0044   p = pbg->buckets + n
0045 } else {
0046   pbg = pbg->next
0047   p = pbg->buckets + ctz(pbg->bitmask)
0048 }
0049 ```
0050 
0051 `reset_first_bits` yields an unsigned integral with the first n bits set to 0
0052 and then by counting the number of trailing zeroes when AND'd against the bucket
0053 group's bitmask, we can derive the offset into the buckets array. When the
0054 calculated offset is equal to N, we know we've reached the end of a bucket group
0055 and we can advance to the next one.
0056 
0057 This is a rough explanation for how iterator incrementation should work for a
0058 fixed width size of N as 3 for the bucket groups
0059 ```
0060 N = 3
0061 p = buckets
0062 pbg->bitmask = 0b101
0063 pbg->buckets = buckets
0064 
0065 offset = p - pbg->buckets // => 0
0066 testing_mask = reset_first_bits(offset + 1) // reset_first_bits(1) => 0b110
0067 
0068 x = bitmask & testing_mask // => 0b101 & 0b110 => 0b100
0069 ctz(x) // ctz(0b100) => 2
0070 // 2 < 3
0071 => p = pbg->buckets + 2
0072 
0073 // increment again...
0074 offset = p - pbg->buckets // => 2
0075 testing_mask = reset_first_bits(offset + 1) // reset_first_bits(3) => 0b000
0076 
0077 bitmask & testing_mask // 0b101 & 0b000 => 0b000
0078 ctz(0b000) => 3
0079 // 3 < 3 is false now
0080 pbg = pbg->next
0081 initial_offset = ctz(pbg->bitmask)
0082 p = pbg->buckets + initial_offset
0083 ```
0084 
0085 For `size_` number of buckets, there are `1 + (size_ / N)` bucket groups where
0086 `N` is the width of a bucket group, determined at compile-time.
0087 
0088 We allocate space for `size_ + 1` buckets, using the last one as a dummy bucket
0089 which is kept permanently empty so it can act as a sentinel value in the
0090 implementation of `iterator end();`. We set the last bucket group to act as a
0091 sentinel.
0092 
0093 ```
0094 num_groups = size_ / N + 1
0095 groups = allocate(num_groups)
0096 pbg = groups + (num_groups - 1)
0097 
0098 // not guaranteed to point to exactly N buckets
0099 pbg->buckets = buckets + N * (size_ / N)
0100 
0101 // this marks the true end of the bucket array
0102 buckets pbg->bitmask = set_bit(size_ % N)
0103 
0104 // links in on itself
0105 pbg->next = pbg->prev = pbg
0106 ```
0107 
0108 To this end, we can devise a safe iteration scheme while also creating a useful
0109 sentinel to use as the end iterator.
0110 
0111 Otherwise, usage of the data structure is relatively straight-forward compared
0112 to normal separate chaining implementations.
0113 
0114 */
0115 
0116 #include <boost/unordered/detail/prime_fmod.hpp>
0117 #include <boost/unordered/detail/serialize_tracked_address.hpp>
0118 #include <boost/unordered/detail/opt_storage.hpp>
0119 
0120 #include <boost/assert.hpp>
0121 #include <boost/core/allocator_access.hpp>
0122 #include <boost/core/bit.hpp>
0123 #include <boost/core/empty_value.hpp>
0124 #include <boost/core/invoke_swap.hpp>
0125 #include <boost/core/no_exceptions_support.hpp>
0126 #include <boost/core/serialization.hpp>
0127 #include <boost/cstdint.hpp>
0128 
0129 #include <boost/config.hpp>
0130 
0131 #include <iterator>
0132 
0133 namespace boost {
0134   namespace unordered {
0135     namespace detail {
0136 
0137       template <class ValueType, class VoidPtr> struct node
0138       {
0139         typedef ValueType value_type;
0140         typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0141           node>::type node_pointer;
0142 
0143         node_pointer next;
0144         opt_storage<value_type> buf;
0145 
0146         node() noexcept : next(), buf() {}
0147 
0148         value_type* value_ptr() noexcept
0149         {
0150           return buf.address();
0151         }
0152 
0153         value_type& value() noexcept
0154         {
0155           return *buf.address();
0156         }
0157       };
0158 
0159       template <class Node, class VoidPtr> struct bucket
0160       {
0161         typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0162           Node>::type node_pointer;
0163 
0164         typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
0165           bucket>::type bucket_pointer;
0166 
0167         node_pointer next;
0168 
0169         bucket() noexcept : next() {}
0170       };
0171 
0172       template <class Bucket> struct bucket_group
0173       {
0174         typedef typename Bucket::bucket_pointer bucket_pointer;
0175         typedef
0176           typename boost::pointer_traits<bucket_pointer>::template rebind_to<
0177             bucket_group>::type bucket_group_pointer;
0178 
0179         BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
0180 
0181         bucket_pointer buckets;
0182         std::size_t bitmask;
0183         bucket_group_pointer next, prev;
0184 
0185         bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
0186         ~bucket_group() {}
0187       };
0188 
0189       inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
0190 
0191       inline std::size_t reset_bit(std::size_t n)
0192       {
0193         return ~(std::size_t(1) << n);
0194       }
0195 
0196       inline std::size_t reset_first_bits(std::size_t n) // n>0
0197       {
0198         return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
0199       }
0200 
0201       template <class Bucket> struct grouped_bucket_iterator
0202       {
0203       public:
0204         typedef typename Bucket::bucket_pointer bucket_pointer;
0205         typedef
0206           typename boost::pointer_traits<bucket_pointer>::template rebind_to<
0207             bucket_group<Bucket> >::type bucket_group_pointer;
0208 
0209         typedef Bucket value_type;
0210         typedef typename boost::pointer_traits<bucket_pointer>::difference_type
0211           difference_type;
0212         typedef Bucket& reference;
0213         typedef Bucket* pointer;
0214         typedef std::forward_iterator_tag iterator_category;
0215 
0216       private:
0217         bucket_pointer p;
0218         bucket_group_pointer pbg;
0219 
0220       public:
0221         grouped_bucket_iterator() : p(), pbg() {}
0222 
0223         reference operator*() const noexcept { return dereference(); }
0224         pointer operator->() const noexcept { return boost::to_address(p); }
0225 
0226         grouped_bucket_iterator& operator++() noexcept
0227         {
0228           increment();
0229           return *this;
0230         }
0231 
0232         grouped_bucket_iterator operator++(int) noexcept
0233         {
0234           grouped_bucket_iterator old = *this;
0235           increment();
0236           return old;
0237         }
0238 
0239         bool operator==(grouped_bucket_iterator const& other) const noexcept
0240         {
0241           return equal(other);
0242         }
0243 
0244         bool operator!=(grouped_bucket_iterator const& other) const noexcept
0245         {
0246           return !equal(other);
0247         }
0248 
0249       private:
0250         template <typename, typename, typename>
0251         friend class grouped_bucket_array;
0252 
0253         BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
0254 
0255         grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
0256             : p(p_), pbg(pbg_)
0257         {
0258         }
0259 
0260         Bucket& dereference() const noexcept { return *p; }
0261 
0262         bool equal(const grouped_bucket_iterator& x) const noexcept
0263         {
0264           return p == x.p;
0265         }
0266 
0267         void increment() noexcept
0268         {
0269           std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
0270 
0271           std::size_t n = std::size_t(boost::core::countr_zero(
0272             pbg->bitmask & reset_first_bits(offset + 1)));
0273 
0274           if (n < N) {
0275             p = pbg->buckets + static_cast<difference_type>(n);
0276           } else {
0277             pbg = pbg->next;
0278 
0279             std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
0280             p = pbg->buckets + x;
0281           }
0282         }
0283 
0284         template <typename Archive>
0285         friend void serialization_track(
0286           Archive& ar, grouped_bucket_iterator const& x)
0287         {
0288           // requires: not at end() position
0289           track_address(ar, x.p);
0290           track_address(ar, x.pbg);
0291         }
0292 
0293         friend class boost::serialization::access;
0294 
0295         template <typename Archive> void serialize(Archive& ar, unsigned int)
0296         {
0297           // requires: not at end() position
0298           serialize_tracked_address(ar, p);
0299           serialize_tracked_address(ar, pbg);
0300         }
0301       };
0302 
0303       template <class Node> struct const_grouped_local_bucket_iterator;
0304 
0305       template <class Node> struct grouped_local_bucket_iterator
0306       {
0307         typedef typename Node::node_pointer node_pointer;
0308 
0309       public:
0310         typedef typename Node::value_type value_type;
0311         typedef value_type element_type;
0312         typedef value_type* pointer;
0313         typedef value_type& reference;
0314         typedef std::ptrdiff_t difference_type;
0315         typedef std::forward_iterator_tag iterator_category;
0316 
0317         grouped_local_bucket_iterator() : p() {}
0318 
0319         reference operator*() const noexcept { return dereference(); }
0320 
0321         pointer operator->() const noexcept
0322         {
0323           return std::addressof(dereference());
0324         }
0325 
0326         grouped_local_bucket_iterator& operator++() noexcept
0327         {
0328           increment();
0329           return *this;
0330         }
0331 
0332         grouped_local_bucket_iterator operator++(int) noexcept
0333         {
0334           grouped_local_bucket_iterator old = *this;
0335           increment();
0336           return old;
0337         }
0338 
0339         bool operator==(
0340           grouped_local_bucket_iterator const& other) const noexcept
0341         {
0342           return equal(other);
0343         }
0344 
0345         bool operator!=(
0346           grouped_local_bucket_iterator const& other) const noexcept
0347         {
0348           return !equal(other);
0349         }
0350 
0351       private:
0352         template <typename, typename, typename>
0353         friend class grouped_bucket_array;
0354 
0355         template <class> friend struct const_grouped_local_bucket_iterator;
0356 
0357         grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
0358 
0359         value_type& dereference() const noexcept { return p->value(); }
0360 
0361         bool equal(const grouped_local_bucket_iterator& x) const noexcept
0362         {
0363           return p == x.p;
0364         }
0365 
0366         void increment() noexcept { p = p->next; }
0367 
0368         node_pointer p;
0369       };
0370 
0371       template <class Node> struct const_grouped_local_bucket_iterator
0372       {
0373         typedef typename Node::node_pointer node_pointer;
0374 
0375       public:
0376         typedef typename Node::value_type const value_type;
0377         typedef value_type const element_type;
0378         typedef value_type const* pointer;
0379         typedef value_type const& reference;
0380         typedef std::ptrdiff_t difference_type;
0381         typedef std::forward_iterator_tag iterator_category;
0382 
0383         const_grouped_local_bucket_iterator() : p() {}
0384         const_grouped_local_bucket_iterator(
0385           grouped_local_bucket_iterator<Node> it)
0386             : p(it.p)
0387         {
0388         }
0389 
0390         reference operator*() const noexcept { return dereference(); }
0391 
0392         pointer operator->() const noexcept
0393         {
0394           return std::addressof(dereference());
0395         }
0396 
0397         const_grouped_local_bucket_iterator& operator++() noexcept
0398         {
0399           increment();
0400           return *this;
0401         }
0402 
0403         const_grouped_local_bucket_iterator operator++(int) noexcept
0404         {
0405           const_grouped_local_bucket_iterator old = *this;
0406           increment();
0407           return old;
0408         }
0409 
0410         bool operator==(
0411           const_grouped_local_bucket_iterator const& other) const noexcept
0412         {
0413           return equal(other);
0414         }
0415 
0416         bool operator!=(
0417           const_grouped_local_bucket_iterator const& other) const noexcept
0418         {
0419           return !equal(other);
0420         }
0421 
0422       private:
0423         template <typename, typename, typename>
0424         friend class grouped_bucket_array;
0425 
0426         const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
0427 
0428         value_type& dereference() const noexcept { return p->value(); }
0429 
0430         bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
0431         {
0432           return p == x.p;
0433         }
0434 
0435         void increment() noexcept { p = p->next; }
0436 
0437         node_pointer p;
0438       };
0439 
0440       template <class T> struct span
0441       {
0442         T* begin() const noexcept { return data; }
0443         T* end() const noexcept { return data + size; }
0444 
0445         T* data;
0446         std::size_t size;
0447 
0448         span(T* data_, std::size_t size_) : data(data_), size(size_) {}
0449       };
0450 
0451       template <class Bucket, class Allocator, class SizePolicy>
0452       class grouped_bucket_array
0453           : boost::empty_value<typename boost::allocator_rebind<Allocator,
0454               node<typename boost::allocator_value_type<Allocator>::type,
0455                 typename boost::allocator_void_pointer<Allocator>::type> >::
0456                 type>
0457       {
0458         typedef typename boost::allocator_value_type<Allocator>::type
0459           allocator_value_type;
0460         typedef
0461           typename boost::allocator_void_pointer<Allocator>::type void_pointer;
0462         typedef typename boost::allocator_difference_type<Allocator>::type
0463           difference_type;
0464 
0465       public:
0466         typedef typename boost::allocator_rebind<Allocator,
0467           node<allocator_value_type, void_pointer> >::type node_allocator_type;
0468 
0469         typedef node<allocator_value_type, void_pointer> node_type;
0470         typedef typename boost::allocator_pointer<node_allocator_type>::type
0471           node_pointer;
0472         typedef SizePolicy size_policy;
0473 
0474       private:
0475         typedef typename boost::allocator_rebind<Allocator, Bucket>::type
0476           bucket_allocator_type;
0477         typedef typename boost::allocator_pointer<bucket_allocator_type>::type
0478           bucket_pointer;
0479         typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
0480 
0481         typedef bucket_group<Bucket> group;
0482         typedef typename boost::allocator_rebind<Allocator, group>::type
0483           group_allocator_type;
0484         typedef typename boost::allocator_pointer<group_allocator_type>::type
0485           group_pointer;
0486         typedef typename boost::pointer_traits<group_pointer>
0487           group_pointer_traits;
0488 
0489       public:
0490         typedef Bucket value_type;
0491         typedef Bucket bucket_type;
0492         typedef std::size_t size_type;
0493         typedef Allocator allocator_type;
0494         typedef grouped_bucket_iterator<Bucket> iterator;
0495         typedef grouped_local_bucket_iterator<node_type> local_iterator;
0496         typedef const_grouped_local_bucket_iterator<node_type>
0497           const_local_iterator;
0498 
0499       private:
0500         std::size_t size_index_, size_;
0501         bucket_pointer buckets;
0502         group_pointer groups;
0503 
0504       public:
0505         static std::size_t bucket_count_for(std::size_t num_buckets)
0506         {
0507           if (num_buckets == 0) {
0508             return 0;
0509           }
0510           return size_policy::size(size_policy::size_index(num_buckets));
0511         }
0512 
0513         grouped_bucket_array()
0514             : empty_value<node_allocator_type>(
0515                 empty_init_t(), node_allocator_type()),
0516               size_index_(0), size_(0), buckets(), groups()
0517         {
0518         }
0519 
0520         grouped_bucket_array(size_type n, const Allocator& al)
0521             : empty_value<node_allocator_type>(
0522                 empty_init_t(), node_allocator_type(al)),
0523               size_index_(0), size_(0), buckets(), groups()
0524         {
0525           if (n == 0) {
0526             return;
0527           }
0528 
0529           size_index_ = size_policy::size_index(n);
0530           size_ = size_policy::size(size_index_);
0531 
0532           bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0533           group_allocator_type group_alloc = this->get_group_allocator();
0534 
0535           size_type const num_buckets = buckets_len();
0536           size_type const num_groups = groups_len();
0537 
0538           buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
0539           BOOST_TRY
0540           {
0541             groups = boost::allocator_allocate(group_alloc, num_groups);
0542 
0543             bucket_type* pb = boost::to_address(buckets);
0544             for (size_type i = 0; i < num_buckets; ++i) {
0545               new (pb + i) bucket_type();
0546             }
0547 
0548             group* pg = boost::to_address(groups);
0549             for (size_type i = 0; i < num_groups; ++i) {
0550               new (pg + i) group();
0551             }
0552           }
0553           BOOST_CATCH(...)
0554           {
0555             boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0556             BOOST_RETHROW
0557           }
0558           BOOST_CATCH_END
0559 
0560           size_type const N = group::N;
0561           group_pointer pbg =
0562             groups + static_cast<difference_type>(num_groups - 1);
0563 
0564           pbg->buckets =
0565             buckets + static_cast<difference_type>(N * (size_ / N));
0566           pbg->bitmask = set_bit(size_ % N);
0567           pbg->next = pbg->prev = pbg;
0568         }
0569 
0570         ~grouped_bucket_array() { this->deallocate(); }
0571 
0572         grouped_bucket_array(grouped_bucket_array const&) = delete;
0573         grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
0574 
0575         grouped_bucket_array(grouped_bucket_array&& other) noexcept
0576             : empty_value<node_allocator_type>(
0577                 empty_init_t(), other.get_node_allocator()),
0578               size_index_(other.size_index_),
0579               size_(other.size_),
0580               buckets(other.buckets),
0581               groups(other.groups)
0582         {
0583           other.size_ = 0;
0584           other.size_index_ = 0;
0585           other.buckets = bucket_pointer();
0586           other.groups = group_pointer();
0587         }
0588 
0589         grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
0590         {
0591           BOOST_ASSERT(
0592             this->get_node_allocator() == other.get_node_allocator());
0593 
0594           if (this == std::addressof(other)) {
0595             return *this;
0596           }
0597 
0598           this->deallocate();
0599           size_index_ = other.size_index_;
0600           size_ = other.size_;
0601 
0602           buckets = other.buckets;
0603           groups = other.groups;
0604 
0605           other.size_index_ = 0;
0606           other.size_ = 0;
0607           other.buckets = bucket_pointer();
0608           other.groups = group_pointer();
0609 
0610           return *this;
0611         }
0612 
0613 #if defined(BOOST_MSVC)
0614 #pragma warning(push)
0615 #pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
0616 #endif
0617 
0618         void deallocate() noexcept
0619         {
0620           if (buckets) {
0621             size_type const num_buckets = buckets_len();
0622             bucket_type* pb = boost::to_address(buckets);
0623             (void)pb; // VS complains when dtor is trivial
0624 
0625             for (size_type i = 0; i < num_buckets; ++i) {
0626               (pb + i)->~bucket_type();
0627             }
0628 
0629             bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0630             boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0631 
0632             buckets = bucket_pointer();
0633           }
0634 
0635           if (groups) {
0636             size_type const num_groups = groups_len();
0637             group* pg = boost::to_address(groups);
0638             (void)pg; // VS complains when dtor is trivial
0639 
0640             for (size_type i = 0; i < num_groups; ++i) {
0641               (pg + i)->~group();
0642             }
0643 
0644             group_allocator_type group_alloc = this->get_group_allocator();
0645             boost::allocator_deallocate(group_alloc, groups, num_groups);
0646 
0647             groups = group_pointer();
0648           }
0649         }
0650 
0651 #if defined(BOOST_MSVC)
0652 #pragma warning(pop)
0653 #endif
0654 
0655         void swap(grouped_bucket_array& other)
0656         {
0657           std::swap(size_index_, other.size_index_);
0658           std::swap(size_, other.size_);
0659           std::swap(buckets, other.buckets);
0660           std::swap(groups, other.groups);
0661 
0662           swap_allocator_if_pocs(other);
0663         }
0664 
0665         node_allocator_type const& get_node_allocator() const
0666         {
0667           return empty_value<node_allocator_type>::get();
0668         }
0669 
0670         node_allocator_type& get_node_allocator()
0671         {
0672           return empty_value<node_allocator_type>::get();
0673         }
0674 
0675         bucket_allocator_type get_bucket_allocator() const
0676         {
0677           return bucket_allocator_type(this->get_node_allocator());
0678         }
0679 
0680         group_allocator_type get_group_allocator() const
0681         {
0682           return group_allocator_type(this->get_node_allocator());
0683         }
0684 
0685         Allocator get_allocator() const
0686         {
0687           return Allocator(this->get_node_allocator());
0688         }
0689 
0690         size_type buckets_len() const noexcept { return size_ + 1; }
0691 
0692         size_type groups_len() const noexcept { return size_ / group::N + 1; }
0693 
0694         void reset_allocator(Allocator const& allocator_)
0695         {
0696           this->get_node_allocator() = node_allocator_type(allocator_);
0697         }
0698 
0699         size_type bucket_count() const { return size_; }
0700 
0701         iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
0702 
0703         iterator end() const
0704         {
0705           // micro optimization: no need to return the bucket group
0706           // as end() is not incrementable
0707           iterator pbg;
0708           pbg.p =
0709             buckets + static_cast<difference_type>(this->buckets_len() - 1);
0710           return pbg;
0711         }
0712 
0713         local_iterator begin(size_type n) const
0714         {
0715           if (size_ == 0) {
0716             return this->end(n);
0717           }
0718 
0719           return local_iterator(
0720             (buckets + static_cast<difference_type>(n))->next);
0721         }
0722 
0723         local_iterator end(size_type) const { return local_iterator(); }
0724 
0725         size_type capacity() const noexcept { return size_; }
0726 
0727         iterator at(size_type n) const
0728         {
0729           if (size_ > 0) {
0730             std::size_t const N = group::N;
0731 
0732             iterator pbg(buckets + static_cast<difference_type>(n),
0733               groups + static_cast<difference_type>(n / N));
0734 
0735             return pbg;
0736           } else {
0737             return this->end();
0738           }
0739         }
0740 
0741         span<Bucket> raw()
0742         {
0743           BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
0744           return span<Bucket>(boost::to_address(buckets), size_);
0745         }
0746 
0747         size_type position(std::size_t hash) const
0748         {
0749           return size_policy::position(hash, size_index_);
0750         }
0751 
0752         void clear()
0753         {
0754           this->deallocate();
0755           size_index_ = 0;
0756           size_ = 0;
0757         }
0758 
0759         void append_bucket_group(iterator itb) noexcept
0760         {
0761           std::size_t const N = group::N;
0762 
0763           bool const is_empty_bucket = (!itb->next);
0764           if (is_empty_bucket) {
0765             bucket_pointer pb = itb.p;
0766             group_pointer pbg = itb.pbg;
0767 
0768             std::size_t n =
0769               static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
0770 
0771             bool const is_empty_group = (!pbg->bitmask);
0772             if (is_empty_group) {
0773               size_type const num_groups = this->groups_len();
0774               group_pointer last_group =
0775                 groups + static_cast<difference_type>(num_groups - 1);
0776 
0777               pbg->buckets =
0778                 buckets + static_cast<difference_type>(N * (n / N));
0779               pbg->next = last_group->next;
0780               pbg->next->prev = pbg;
0781               pbg->prev = last_group;
0782               pbg->prev->next = pbg;
0783             }
0784 
0785             pbg->bitmask |= set_bit(n % N);
0786           }
0787         }
0788 
0789         void insert_node(iterator itb, node_pointer p) noexcept
0790         {
0791           this->append_bucket_group(itb);
0792 
0793           p->next = itb->next;
0794           itb->next = p;
0795         }
0796 
0797         void insert_node_hint(
0798           iterator itb, node_pointer p, node_pointer hint) noexcept
0799         {
0800           this->append_bucket_group(itb);
0801 
0802           if (hint) {
0803             p->next = hint->next;
0804             hint->next = p;
0805           } else {
0806             p->next = itb->next;
0807             itb->next = p;
0808           }
0809         }
0810 
0811         void extract_node(iterator itb, node_pointer p) noexcept
0812         {
0813           node_pointer* pp = std::addressof(itb->next);
0814           while ((*pp) != p)
0815             pp = std::addressof((*pp)->next);
0816           *pp = p->next;
0817           if (!itb->next)
0818             unlink_bucket(itb);
0819         }
0820 
0821         void extract_node_after(iterator itb, node_pointer* pp) noexcept
0822         {
0823           *pp = (*pp)->next;
0824           if (!itb->next)
0825             unlink_bucket(itb);
0826         }
0827 
0828         void unlink_empty_buckets() noexcept
0829         {
0830           std::size_t const N = group::N;
0831 
0832           group_pointer pbg = groups,
0833                         last = groups + static_cast<difference_type>(
0834                                           this->groups_len() - 1);
0835 
0836           for (; pbg != last; ++pbg) {
0837             if (!pbg->buckets) {
0838               continue;
0839             }
0840 
0841             for (std::size_t n = 0; n < N; ++n) {
0842               bucket_pointer bs = pbg->buckets;
0843               bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
0844               if (!b.next)
0845                 pbg->bitmask &= reset_bit(n);
0846             }
0847             if (!pbg->bitmask && pbg->next)
0848               unlink_group(pbg);
0849           }
0850 
0851           // do not check end bucket
0852           for (std::size_t n = 0; n < size_ % N; ++n) {
0853             if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
0854               pbg->bitmask &= reset_bit(n);
0855           }
0856         }
0857 
0858         void unlink_bucket(iterator itb)
0859         {
0860           typename iterator::bucket_pointer p = itb.p;
0861           typename iterator::bucket_group_pointer pbg = itb.pbg;
0862           if (!(pbg->bitmask &=
0863                 reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
0864             unlink_group(pbg);
0865         }
0866 
0867       private:
0868         void unlink_group(group_pointer pbg)
0869         {
0870           pbg->next->prev = pbg->prev;
0871           pbg->prev->next = pbg->next;
0872           pbg->prev = pbg->next = group_pointer();
0873         }
0874 
0875         void swap_allocator_if_pocs(grouped_bucket_array& other)
0876         {
0877           using allocator_pocs =
0878             typename boost::allocator_propagate_on_container_swap<
0879               allocator_type>::type;
0880           swap_allocator_if_pocs(
0881             other, std::integral_constant<bool, allocator_pocs::value>());
0882         }
0883 
0884         void swap_allocator_if_pocs(
0885           grouped_bucket_array& other, std::true_type /* propagate */)
0886         {
0887           boost::core::invoke_swap(
0888             get_node_allocator(), other.get_node_allocator());
0889         }
0890 
0891         void swap_allocator_if_pocs(
0892           grouped_bucket_array&, std::false_type /* don't propagate */)
0893         {
0894         }
0895       };
0896     } // namespace detail
0897   } // namespace unordered
0898 } // namespace boost
0899 
0900 #endif // BOOST_UNORDERED_DETAIL_FCA_HPP