Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-31 10:03:44

0001 // Copyright (C) 2022-2023 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>(empty_init_t(), al),
0522               size_index_(0), size_(0), buckets(), groups()
0523         {
0524           if (n == 0) {
0525             return;
0526           }
0527 
0528           size_index_ = size_policy::size_index(n);
0529           size_ = size_policy::size(size_index_);
0530 
0531           bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0532           group_allocator_type group_alloc = this->get_group_allocator();
0533 
0534           size_type const num_buckets = buckets_len();
0535           size_type const num_groups = groups_len();
0536 
0537           buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
0538           BOOST_TRY
0539           {
0540             groups = boost::allocator_allocate(group_alloc, num_groups);
0541 
0542             bucket_type* pb = boost::to_address(buckets);
0543             for (size_type i = 0; i < num_buckets; ++i) {
0544               new (pb + i) bucket_type();
0545             }
0546 
0547             group* pg = boost::to_address(groups);
0548             for (size_type i = 0; i < num_groups; ++i) {
0549               new (pg + i) group();
0550             }
0551           }
0552           BOOST_CATCH(...)
0553           {
0554             boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0555             BOOST_RETHROW
0556           }
0557           BOOST_CATCH_END
0558 
0559           size_type const N = group::N;
0560           group_pointer pbg =
0561             groups + static_cast<difference_type>(num_groups - 1);
0562 
0563           pbg->buckets =
0564             buckets + static_cast<difference_type>(N * (size_ / N));
0565           pbg->bitmask = set_bit(size_ % N);
0566           pbg->next = pbg->prev = pbg;
0567         }
0568 
0569         ~grouped_bucket_array() { this->deallocate(); }
0570 
0571         grouped_bucket_array(grouped_bucket_array const&) = delete;
0572         grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
0573 
0574         grouped_bucket_array(grouped_bucket_array&& other) noexcept
0575             : empty_value<node_allocator_type>(
0576                 empty_init_t(), other.get_node_allocator()),
0577               size_index_(other.size_index_),
0578               size_(other.size_),
0579               buckets(other.buckets),
0580               groups(other.groups)
0581         {
0582           other.size_ = 0;
0583           other.size_index_ = 0;
0584           other.buckets = bucket_pointer();
0585           other.groups = group_pointer();
0586         }
0587 
0588         grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
0589         {
0590           BOOST_ASSERT(
0591             this->get_node_allocator() == other.get_node_allocator());
0592 
0593           if (this == std::addressof(other)) {
0594             return *this;
0595           }
0596 
0597           this->deallocate();
0598           size_index_ = other.size_index_;
0599           size_ = other.size_;
0600 
0601           buckets = other.buckets;
0602           groups = other.groups;
0603 
0604           other.size_index_ = 0;
0605           other.size_ = 0;
0606           other.buckets = bucket_pointer();
0607           other.groups = group_pointer();
0608 
0609           return *this;
0610         }
0611 
0612 #if defined(BOOST_MSVC)
0613 #pragma warning(push)
0614 #pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
0615 #endif
0616 
0617         void deallocate() noexcept
0618         {
0619           if (buckets) {
0620             size_type const num_buckets = buckets_len();
0621             bucket_type* pb = boost::to_address(buckets);
0622             (void)pb; // VS complains when dtor is trivial
0623 
0624             for (size_type i = 0; i < num_buckets; ++i) {
0625               (pb + i)->~bucket_type();
0626             }
0627 
0628             bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
0629             boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
0630 
0631             buckets = bucket_pointer();
0632           }
0633 
0634           if (groups) {
0635             size_type const num_groups = groups_len();
0636             group* pg = boost::to_address(groups);
0637             (void)pg; // VS complains when dtor is trivial
0638 
0639             for (size_type i = 0; i < num_groups; ++i) {
0640               (pg + i)->~group();
0641             }
0642 
0643             group_allocator_type group_alloc = this->get_group_allocator();
0644             boost::allocator_deallocate(group_alloc, groups, num_groups);
0645 
0646             groups = group_pointer();
0647           }
0648         }
0649 
0650 #if defined(BOOST_MSVC)
0651 #pragma warning(pop)
0652 #endif
0653 
0654         void swap(grouped_bucket_array& other)
0655         {
0656           std::swap(size_index_, other.size_index_);
0657           std::swap(size_, other.size_);
0658           std::swap(buckets, other.buckets);
0659           std::swap(groups, other.groups);
0660 
0661           bool b = boost::allocator_propagate_on_container_swap<
0662             allocator_type>::type::value;
0663           if (b) {
0664             boost::core::invoke_swap(
0665               get_node_allocator(), other.get_node_allocator());
0666           }
0667         }
0668 
0669         node_allocator_type const& get_node_allocator() const
0670         {
0671           return empty_value<node_allocator_type>::get();
0672         }
0673 
0674         node_allocator_type& get_node_allocator()
0675         {
0676           return empty_value<node_allocator_type>::get();
0677         }
0678 
0679         bucket_allocator_type get_bucket_allocator() const
0680         {
0681           return this->get_node_allocator();
0682         }
0683 
0684         group_allocator_type get_group_allocator() const
0685         {
0686           return this->get_node_allocator();
0687         }
0688 
0689         size_type buckets_len() const noexcept { return size_ + 1; }
0690 
0691         size_type groups_len() const noexcept { return size_ / group::N + 1; }
0692 
0693         void reset_allocator(Allocator const& allocator_)
0694         {
0695           this->get_node_allocator() = node_allocator_type(allocator_);
0696         }
0697 
0698         size_type bucket_count() const { return size_; }
0699 
0700         iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
0701 
0702         iterator end() const
0703         {
0704           // micro optimization: no need to return the bucket group
0705           // as end() is not incrementable
0706           iterator pbg;
0707           pbg.p =
0708             buckets + static_cast<difference_type>(this->buckets_len() - 1);
0709           return pbg;
0710         }
0711 
0712         local_iterator begin(size_type n) const
0713         {
0714           if (size_ == 0) {
0715             return this->end(n);
0716           }
0717 
0718           return local_iterator(
0719             (buckets + static_cast<difference_type>(n))->next);
0720         }
0721 
0722         local_iterator end(size_type) const { return local_iterator(); }
0723 
0724         size_type capacity() const noexcept { return size_; }
0725 
0726         iterator at(size_type n) const
0727         {
0728           if (size_ > 0) {
0729             std::size_t const N = group::N;
0730 
0731             iterator pbg(buckets + static_cast<difference_type>(n),
0732               groups + static_cast<difference_type>(n / N));
0733 
0734             return pbg;
0735           } else {
0736             return this->end();
0737           }
0738         }
0739 
0740         span<Bucket> raw()
0741         {
0742           BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
0743           return span<Bucket>(boost::to_address(buckets), size_);
0744         }
0745 
0746         size_type position(std::size_t hash) const
0747         {
0748           return size_policy::position(hash, size_index_);
0749         }
0750 
0751         void clear()
0752         {
0753           this->deallocate();
0754           size_index_ = 0;
0755           size_ = 0;
0756         }
0757 
0758         void append_bucket_group(iterator itb) noexcept
0759         {
0760           std::size_t const N = group::N;
0761 
0762           bool const is_empty_bucket = (!itb->next);
0763           if (is_empty_bucket) {
0764             bucket_pointer pb = itb.p;
0765             group_pointer pbg = itb.pbg;
0766 
0767             std::size_t n =
0768               static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
0769 
0770             bool const is_empty_group = (!pbg->bitmask);
0771             if (is_empty_group) {
0772               size_type const num_groups = this->groups_len();
0773               group_pointer last_group =
0774                 groups + static_cast<difference_type>(num_groups - 1);
0775 
0776               pbg->buckets =
0777                 buckets + static_cast<difference_type>(N * (n / N));
0778               pbg->next = last_group->next;
0779               pbg->next->prev = pbg;
0780               pbg->prev = last_group;
0781               pbg->prev->next = pbg;
0782             }
0783 
0784             pbg->bitmask |= set_bit(n % N);
0785           }
0786         }
0787 
0788         void insert_node(iterator itb, node_pointer p) noexcept
0789         {
0790           this->append_bucket_group(itb);
0791 
0792           p->next = itb->next;
0793           itb->next = p;
0794         }
0795 
0796         void insert_node_hint(
0797           iterator itb, node_pointer p, node_pointer hint) noexcept
0798         {
0799           this->append_bucket_group(itb);
0800 
0801           if (hint) {
0802             p->next = hint->next;
0803             hint->next = p;
0804           } else {
0805             p->next = itb->next;
0806             itb->next = p;
0807           }
0808         }
0809 
0810         void extract_node(iterator itb, node_pointer p) noexcept
0811         {
0812           node_pointer* pp = std::addressof(itb->next);
0813           while ((*pp) != p)
0814             pp = std::addressof((*pp)->next);
0815           *pp = p->next;
0816           if (!itb->next)
0817             unlink_bucket(itb);
0818         }
0819 
0820         void extract_node_after(iterator itb, node_pointer* pp) noexcept
0821         {
0822           *pp = (*pp)->next;
0823           if (!itb->next)
0824             unlink_bucket(itb);
0825         }
0826 
0827         void unlink_empty_buckets() noexcept
0828         {
0829           std::size_t const N = group::N;
0830 
0831           group_pointer pbg = groups,
0832                         last = groups + static_cast<difference_type>(
0833                                           this->groups_len() - 1);
0834 
0835           for (; pbg != last; ++pbg) {
0836             if (!pbg->buckets) {
0837               continue;
0838             }
0839 
0840             for (std::size_t n = 0; n < N; ++n) {
0841               bucket_pointer bs = pbg->buckets;
0842               bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
0843               if (!b.next)
0844                 pbg->bitmask &= reset_bit(n);
0845             }
0846             if (!pbg->bitmask && pbg->next)
0847               unlink_group(pbg);
0848           }
0849 
0850           // do not check end bucket
0851           for (std::size_t n = 0; n < size_ % N; ++n) {
0852             if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
0853               pbg->bitmask &= reset_bit(n);
0854           }
0855         }
0856 
0857         void unlink_bucket(iterator itb)
0858         {
0859           typename iterator::bucket_pointer p = itb.p;
0860           typename iterator::bucket_group_pointer pbg = itb.pbg;
0861           if (!(pbg->bitmask &=
0862                 reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
0863             unlink_group(pbg);
0864         }
0865 
0866       private:
0867         void unlink_group(group_pointer pbg)
0868         {
0869           pbg->next->prev = pbg->prev;
0870           pbg->prev->next = pbg->next;
0871           pbg->prev = pbg->next = group_pointer();
0872         }
0873       };
0874     } // namespace detail
0875   } // namespace unordered
0876 } // namespace boost
0877 
0878 #endif // BOOST_UNORDERED_DETAIL_FCA_HPP