fca.hpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. // Copyright (C) 2022-2024 Joaquin M Lopez Munoz.
  2. // Copyright (C) 2022 Christian Mazakas
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_UNORDERED_DETAIL_FCA_HPP
  7. #define BOOST_UNORDERED_DETAIL_FCA_HPP
  8. /*
  9. The general structure of the fast closed addressing implementation is that we
  10. use straight-forward separate chaining (i.e. each bucket contains its own linked
  11. list) and then improve iteration time by adding an array of "bucket groups".
  12. A bucket group is a constant-width view into a subsection of the buckets array,
  13. containing a bitmask that indicates which one of the buckets in the subsection
  14. contains a list of nodes. This allows the code to test N buckets for occupancy
  15. in a single operation. Additional speed can be found by inter-linking occupied
  16. bucket groups with one another in a doubly-linked list. To this end, large
  17. swathes of the bucket groups array no longer need to be iterated and have their
  18. bitmasks examined for occupancy.
  19. A bucket group iterator contains a pointer to a bucket group along with a
  20. pointer into the buckets array. The iterator's bucket pointer is guaranteed to
  21. point to a bucket within the bucket group's view of the array. To advance the
  22. iterator, we need to determine if we need to skip to the next bucket group or
  23. simply move to the next occupied bucket as denoted by the bitmask.
  24. To accomplish this, we perform something roughly equivalent to this:
  25. ```
  26. bucket_iterator itb = ...
  27. bucket_pointer p = itb.p
  28. bucket_group_pointer pbg = itb.pbg
  29. offset = p - pbg->buckets
  30. // because we wish to see if the _next_ bit in the mask is occupied, we'll
  31. // generate a testing mask from the current offset + 1
  32. //
  33. testing_mask = reset_first_bits(offset + 1)
  34. n = ctz(pbg->bitmask & testing_mask)
  35. if (n < N) {
  36. p = pbg->buckets + n
  37. } else {
  38. pbg = pbg->next
  39. p = pbg->buckets + ctz(pbg->bitmask)
  40. }
  41. ```
  42. `reset_first_bits` yields an unsigned integral with the first n bits set to 0
  43. and then by counting the number of trailing zeroes when AND'd against the bucket
  44. group's bitmask, we can derive the offset into the buckets array. When the
  45. calculated offset is equal to N, we know we've reached the end of a bucket group
  46. and we can advance to the next one.
  47. This is a rough explanation for how iterator incrementation should work for a
  48. fixed width size of N as 3 for the bucket groups
  49. ```
  50. N = 3
  51. p = buckets
  52. pbg->bitmask = 0b101
  53. pbg->buckets = buckets
  54. offset = p - pbg->buckets // => 0
  55. testing_mask = reset_first_bits(offset + 1) // reset_first_bits(1) => 0b110
  56. x = bitmask & testing_mask // => 0b101 & 0b110 => 0b100
  57. ctz(x) // ctz(0b100) => 2
  58. // 2 < 3
  59. => p = pbg->buckets + 2
  60. // increment again...
  61. offset = p - pbg->buckets // => 2
  62. testing_mask = reset_first_bits(offset + 1) // reset_first_bits(3) => 0b000
  63. bitmask & testing_mask // 0b101 & 0b000 => 0b000
  64. ctz(0b000) => 3
  65. // 3 < 3 is false now
  66. pbg = pbg->next
  67. initial_offset = ctz(pbg->bitmask)
  68. p = pbg->buckets + initial_offset
  69. ```
  70. For `size_` number of buckets, there are `1 + (size_ / N)` bucket groups where
  71. `N` is the width of a bucket group, determined at compile-time.
  72. We allocate space for `size_ + 1` buckets, using the last one as a dummy bucket
  73. which is kept permanently empty so it can act as a sentinel value in the
  74. implementation of `iterator end();`. We set the last bucket group to act as a
  75. sentinel.
  76. ```
  77. num_groups = size_ / N + 1
  78. groups = allocate(num_groups)
  79. pbg = groups + (num_groups - 1)
  80. // not guaranteed to point to exactly N buckets
  81. pbg->buckets = buckets + N * (size_ / N)
  82. // this marks the true end of the bucket array
  83. buckets pbg->bitmask = set_bit(size_ % N)
  84. // links in on itself
  85. pbg->next = pbg->prev = pbg
  86. ```
  87. To this end, we can devise a safe iteration scheme while also creating a useful
  88. sentinel to use as the end iterator.
  89. Otherwise, usage of the data structure is relatively straight-forward compared
  90. to normal separate chaining implementations.
  91. */
  92. #include <boost/unordered/detail/prime_fmod.hpp>
  93. #include <boost/unordered/detail/serialize_tracked_address.hpp>
  94. #include <boost/unordered/detail/opt_storage.hpp>
  95. #include <boost/assert.hpp>
  96. #include <boost/core/allocator_access.hpp>
  97. #include <boost/core/bit.hpp>
  98. #include <boost/core/empty_value.hpp>
  99. #include <boost/core/invoke_swap.hpp>
  100. #include <boost/core/no_exceptions_support.hpp>
  101. #include <boost/core/serialization.hpp>
  102. #include <boost/cstdint.hpp>
  103. #include <boost/config.hpp>
  104. #include <iterator>
  105. namespace boost {
  106. namespace unordered {
  107. namespace detail {
  108. template <class ValueType, class VoidPtr> struct node
  109. {
  110. typedef ValueType value_type;
  111. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  112. node>::type node_pointer;
  113. node_pointer next;
  114. opt_storage<value_type> buf;
  115. node() noexcept : next(), buf() {}
  116. value_type* value_ptr() noexcept
  117. {
  118. return buf.address();
  119. }
  120. value_type& value() noexcept
  121. {
  122. return *buf.address();
  123. }
  124. };
  125. template <class Node, class VoidPtr> struct bucket
  126. {
  127. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  128. Node>::type node_pointer;
  129. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  130. bucket>::type bucket_pointer;
  131. node_pointer next;
  132. bucket() noexcept : next() {}
  133. };
  134. template <class Bucket> struct bucket_group
  135. {
  136. typedef typename Bucket::bucket_pointer bucket_pointer;
  137. typedef
  138. typename boost::pointer_traits<bucket_pointer>::template rebind_to<
  139. bucket_group>::type bucket_group_pointer;
  140. BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
  141. bucket_pointer buckets;
  142. std::size_t bitmask;
  143. bucket_group_pointer next, prev;
  144. bucket_group() noexcept : buckets(), bitmask(0), next(), prev() {}
  145. ~bucket_group() {}
  146. };
  147. inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
  148. inline std::size_t reset_bit(std::size_t n)
  149. {
  150. return ~(std::size_t(1) << n);
  151. }
  152. inline std::size_t reset_first_bits(std::size_t n) // n>0
  153. {
  154. return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
  155. }
  156. template <class Bucket> struct grouped_bucket_iterator
  157. {
  158. public:
  159. typedef typename Bucket::bucket_pointer bucket_pointer;
  160. typedef
  161. typename boost::pointer_traits<bucket_pointer>::template rebind_to<
  162. bucket_group<Bucket> >::type bucket_group_pointer;
  163. typedef Bucket value_type;
  164. typedef typename boost::pointer_traits<bucket_pointer>::difference_type
  165. difference_type;
  166. typedef Bucket& reference;
  167. typedef Bucket* pointer;
  168. typedef std::forward_iterator_tag iterator_category;
  169. private:
  170. bucket_pointer p;
  171. bucket_group_pointer pbg;
  172. public:
  173. grouped_bucket_iterator() : p(), pbg() {}
  174. reference operator*() const noexcept { return dereference(); }
  175. pointer operator->() const noexcept { return boost::to_address(p); }
  176. grouped_bucket_iterator& operator++() noexcept
  177. {
  178. increment();
  179. return *this;
  180. }
  181. grouped_bucket_iterator operator++(int) noexcept
  182. {
  183. grouped_bucket_iterator old = *this;
  184. increment();
  185. return old;
  186. }
  187. bool operator==(grouped_bucket_iterator const& other) const noexcept
  188. {
  189. return equal(other);
  190. }
  191. bool operator!=(grouped_bucket_iterator const& other) const noexcept
  192. {
  193. return !equal(other);
  194. }
  195. private:
  196. template <typename, typename, typename>
  197. friend class grouped_bucket_array;
  198. BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
  199. grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
  200. : p(p_), pbg(pbg_)
  201. {
  202. }
  203. Bucket& dereference() const noexcept { return *p; }
  204. bool equal(const grouped_bucket_iterator& x) const noexcept
  205. {
  206. return p == x.p;
  207. }
  208. void increment() noexcept
  209. {
  210. std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
  211. std::size_t n = std::size_t(boost::core::countr_zero(
  212. pbg->bitmask & reset_first_bits(offset + 1)));
  213. if (n < N) {
  214. p = pbg->buckets + static_cast<difference_type>(n);
  215. } else {
  216. pbg = pbg->next;
  217. std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
  218. p = pbg->buckets + x;
  219. }
  220. }
  221. template <typename Archive>
  222. friend void serialization_track(
  223. Archive& ar, grouped_bucket_iterator const& x)
  224. {
  225. // requires: not at end() position
  226. track_address(ar, x.p);
  227. track_address(ar, x.pbg);
  228. }
  229. friend class boost::serialization::access;
  230. template <typename Archive> void serialize(Archive& ar, unsigned int)
  231. {
  232. // requires: not at end() position
  233. serialize_tracked_address(ar, p);
  234. serialize_tracked_address(ar, pbg);
  235. }
  236. };
  237. template <class Node> struct const_grouped_local_bucket_iterator;
  238. template <class Node> struct grouped_local_bucket_iterator
  239. {
  240. typedef typename Node::node_pointer node_pointer;
  241. public:
  242. typedef typename Node::value_type value_type;
  243. typedef value_type element_type;
  244. typedef value_type* pointer;
  245. typedef value_type& reference;
  246. typedef std::ptrdiff_t difference_type;
  247. typedef std::forward_iterator_tag iterator_category;
  248. grouped_local_bucket_iterator() : p() {}
  249. reference operator*() const noexcept { return dereference(); }
  250. pointer operator->() const noexcept
  251. {
  252. return std::addressof(dereference());
  253. }
  254. grouped_local_bucket_iterator& operator++() noexcept
  255. {
  256. increment();
  257. return *this;
  258. }
  259. grouped_local_bucket_iterator operator++(int) noexcept
  260. {
  261. grouped_local_bucket_iterator old = *this;
  262. increment();
  263. return old;
  264. }
  265. bool operator==(
  266. grouped_local_bucket_iterator const& other) const noexcept
  267. {
  268. return equal(other);
  269. }
  270. bool operator!=(
  271. grouped_local_bucket_iterator const& other) const noexcept
  272. {
  273. return !equal(other);
  274. }
  275. private:
  276. template <typename, typename, typename>
  277. friend class grouped_bucket_array;
  278. template <class> friend struct const_grouped_local_bucket_iterator;
  279. grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
  280. value_type& dereference() const noexcept { return p->value(); }
  281. bool equal(const grouped_local_bucket_iterator& x) const noexcept
  282. {
  283. return p == x.p;
  284. }
  285. void increment() noexcept { p = p->next; }
  286. node_pointer p;
  287. };
  288. template <class Node> struct const_grouped_local_bucket_iterator
  289. {
  290. typedef typename Node::node_pointer node_pointer;
  291. public:
  292. typedef typename Node::value_type const value_type;
  293. typedef value_type const element_type;
  294. typedef value_type const* pointer;
  295. typedef value_type const& reference;
  296. typedef std::ptrdiff_t difference_type;
  297. typedef std::forward_iterator_tag iterator_category;
  298. const_grouped_local_bucket_iterator() : p() {}
  299. const_grouped_local_bucket_iterator(
  300. grouped_local_bucket_iterator<Node> it)
  301. : p(it.p)
  302. {
  303. }
  304. reference operator*() const noexcept { return dereference(); }
  305. pointer operator->() const noexcept
  306. {
  307. return std::addressof(dereference());
  308. }
  309. const_grouped_local_bucket_iterator& operator++() noexcept
  310. {
  311. increment();
  312. return *this;
  313. }
  314. const_grouped_local_bucket_iterator operator++(int) noexcept
  315. {
  316. const_grouped_local_bucket_iterator old = *this;
  317. increment();
  318. return old;
  319. }
  320. bool operator==(
  321. const_grouped_local_bucket_iterator const& other) const noexcept
  322. {
  323. return equal(other);
  324. }
  325. bool operator!=(
  326. const_grouped_local_bucket_iterator const& other) const noexcept
  327. {
  328. return !equal(other);
  329. }
  330. private:
  331. template <typename, typename, typename>
  332. friend class grouped_bucket_array;
  333. const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
  334. value_type& dereference() const noexcept { return p->value(); }
  335. bool equal(const const_grouped_local_bucket_iterator& x) const noexcept
  336. {
  337. return p == x.p;
  338. }
  339. void increment() noexcept { p = p->next; }
  340. node_pointer p;
  341. };
  342. template <class T> struct span
  343. {
  344. T* begin() const noexcept { return data; }
  345. T* end() const noexcept { return data + size; }
  346. T* data;
  347. std::size_t size;
  348. span(T* data_, std::size_t size_) : data(data_), size(size_) {}
  349. };
  350. template <class Bucket, class Allocator, class SizePolicy>
  351. class grouped_bucket_array
  352. : boost::empty_value<typename boost::allocator_rebind<Allocator,
  353. node<typename boost::allocator_value_type<Allocator>::type,
  354. typename boost::allocator_void_pointer<Allocator>::type> >::
  355. type>
  356. {
  357. typedef typename boost::allocator_value_type<Allocator>::type
  358. allocator_value_type;
  359. typedef
  360. typename boost::allocator_void_pointer<Allocator>::type void_pointer;
  361. typedef typename boost::allocator_difference_type<Allocator>::type
  362. difference_type;
  363. public:
  364. typedef typename boost::allocator_rebind<Allocator,
  365. node<allocator_value_type, void_pointer> >::type node_allocator_type;
  366. typedef node<allocator_value_type, void_pointer> node_type;
  367. typedef typename boost::allocator_pointer<node_allocator_type>::type
  368. node_pointer;
  369. typedef SizePolicy size_policy;
  370. private:
  371. typedef typename boost::allocator_rebind<Allocator, Bucket>::type
  372. bucket_allocator_type;
  373. typedef typename boost::allocator_pointer<bucket_allocator_type>::type
  374. bucket_pointer;
  375. typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
  376. typedef bucket_group<Bucket> group;
  377. typedef typename boost::allocator_rebind<Allocator, group>::type
  378. group_allocator_type;
  379. typedef typename boost::allocator_pointer<group_allocator_type>::type
  380. group_pointer;
  381. typedef typename boost::pointer_traits<group_pointer>
  382. group_pointer_traits;
  383. public:
  384. typedef Bucket value_type;
  385. typedef Bucket bucket_type;
  386. typedef std::size_t size_type;
  387. typedef Allocator allocator_type;
  388. typedef grouped_bucket_iterator<Bucket> iterator;
  389. typedef grouped_local_bucket_iterator<node_type> local_iterator;
  390. typedef const_grouped_local_bucket_iterator<node_type>
  391. const_local_iterator;
  392. private:
  393. std::size_t size_index_, size_;
  394. bucket_pointer buckets;
  395. group_pointer groups;
  396. public:
  397. static std::size_t bucket_count_for(std::size_t num_buckets)
  398. {
  399. if (num_buckets == 0) {
  400. return 0;
  401. }
  402. return size_policy::size(size_policy::size_index(num_buckets));
  403. }
  404. grouped_bucket_array()
  405. : empty_value<node_allocator_type>(
  406. empty_init_t(), node_allocator_type()),
  407. size_index_(0), size_(0), buckets(), groups()
  408. {
  409. }
  410. grouped_bucket_array(size_type n, const Allocator& al)
  411. : empty_value<node_allocator_type>(
  412. empty_init_t(), node_allocator_type(al)),
  413. size_index_(0), size_(0), buckets(), groups()
  414. {
  415. if (n == 0) {
  416. return;
  417. }
  418. size_index_ = size_policy::size_index(n);
  419. size_ = size_policy::size(size_index_);
  420. bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
  421. group_allocator_type group_alloc = this->get_group_allocator();
  422. size_type const num_buckets = buckets_len();
  423. size_type const num_groups = groups_len();
  424. buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
  425. BOOST_TRY
  426. {
  427. groups = boost::allocator_allocate(group_alloc, num_groups);
  428. bucket_type* pb = boost::to_address(buckets);
  429. for (size_type i = 0; i < num_buckets; ++i) {
  430. new (pb + i) bucket_type();
  431. }
  432. group* pg = boost::to_address(groups);
  433. for (size_type i = 0; i < num_groups; ++i) {
  434. new (pg + i) group();
  435. }
  436. }
  437. BOOST_CATCH(...)
  438. {
  439. boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
  440. BOOST_RETHROW
  441. }
  442. BOOST_CATCH_END
  443. size_type const N = group::N;
  444. group_pointer pbg =
  445. groups + static_cast<difference_type>(num_groups - 1);
  446. pbg->buckets =
  447. buckets + static_cast<difference_type>(N * (size_ / N));
  448. pbg->bitmask = set_bit(size_ % N);
  449. pbg->next = pbg->prev = pbg;
  450. }
  451. ~grouped_bucket_array() { this->deallocate(); }
  452. grouped_bucket_array(grouped_bucket_array const&) = delete;
  453. grouped_bucket_array& operator=(grouped_bucket_array const&) = delete;
  454. grouped_bucket_array(grouped_bucket_array&& other) noexcept
  455. : empty_value<node_allocator_type>(
  456. empty_init_t(), other.get_node_allocator()),
  457. size_index_(other.size_index_),
  458. size_(other.size_),
  459. buckets(other.buckets),
  460. groups(other.groups)
  461. {
  462. other.size_ = 0;
  463. other.size_index_ = 0;
  464. other.buckets = bucket_pointer();
  465. other.groups = group_pointer();
  466. }
  467. grouped_bucket_array& operator=(grouped_bucket_array&& other) noexcept
  468. {
  469. BOOST_ASSERT(
  470. this->get_node_allocator() == other.get_node_allocator());
  471. if (this == std::addressof(other)) {
  472. return *this;
  473. }
  474. this->deallocate();
  475. size_index_ = other.size_index_;
  476. size_ = other.size_;
  477. buckets = other.buckets;
  478. groups = other.groups;
  479. other.size_index_ = 0;
  480. other.size_ = 0;
  481. other.buckets = bucket_pointer();
  482. other.groups = group_pointer();
  483. return *this;
  484. }
  485. #if defined(BOOST_MSVC)
  486. #pragma warning(push)
  487. #pragma warning(disable : 4100) // unreferenced formal parameter (dtor calls)
  488. #endif
  489. void deallocate() noexcept
  490. {
  491. if (buckets) {
  492. size_type const num_buckets = buckets_len();
  493. bucket_type* pb = boost::to_address(buckets);
  494. (void)pb; // VS complains when dtor is trivial
  495. for (size_type i = 0; i < num_buckets; ++i) {
  496. (pb + i)->~bucket_type();
  497. }
  498. bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
  499. boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
  500. buckets = bucket_pointer();
  501. }
  502. if (groups) {
  503. size_type const num_groups = groups_len();
  504. group* pg = boost::to_address(groups);
  505. (void)pg; // VS complains when dtor is trivial
  506. for (size_type i = 0; i < num_groups; ++i) {
  507. (pg + i)->~group();
  508. }
  509. group_allocator_type group_alloc = this->get_group_allocator();
  510. boost::allocator_deallocate(group_alloc, groups, num_groups);
  511. groups = group_pointer();
  512. }
  513. }
  514. #if defined(BOOST_MSVC)
  515. #pragma warning(pop)
  516. #endif
  517. void swap(grouped_bucket_array& other)
  518. {
  519. std::swap(size_index_, other.size_index_);
  520. std::swap(size_, other.size_);
  521. std::swap(buckets, other.buckets);
  522. std::swap(groups, other.groups);
  523. swap_allocator_if_pocs(other);
  524. }
  525. node_allocator_type const& get_node_allocator() const
  526. {
  527. return empty_value<node_allocator_type>::get();
  528. }
  529. node_allocator_type& get_node_allocator()
  530. {
  531. return empty_value<node_allocator_type>::get();
  532. }
  533. bucket_allocator_type get_bucket_allocator() const
  534. {
  535. return bucket_allocator_type(this->get_node_allocator());
  536. }
  537. group_allocator_type get_group_allocator() const
  538. {
  539. return group_allocator_type(this->get_node_allocator());
  540. }
  541. Allocator get_allocator() const
  542. {
  543. return Allocator(this->get_node_allocator());
  544. }
  545. size_type buckets_len() const noexcept { return size_ + 1; }
  546. size_type groups_len() const noexcept { return size_ / group::N + 1; }
  547. void reset_allocator(Allocator const& allocator_)
  548. {
  549. this->get_node_allocator() = node_allocator_type(allocator_);
  550. }
  551. size_type bucket_count() const { return size_; }
  552. iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
  553. iterator end() const
  554. {
  555. // micro optimization: no need to return the bucket group
  556. // as end() is not incrementable
  557. iterator pbg;
  558. pbg.p =
  559. buckets + static_cast<difference_type>(this->buckets_len() - 1);
  560. return pbg;
  561. }
  562. local_iterator begin(size_type n) const
  563. {
  564. if (size_ == 0) {
  565. return this->end(n);
  566. }
  567. return local_iterator(
  568. (buckets + static_cast<difference_type>(n))->next);
  569. }
  570. local_iterator end(size_type) const { return local_iterator(); }
  571. size_type capacity() const noexcept { return size_; }
  572. iterator at(size_type n) const
  573. {
  574. if (size_ > 0) {
  575. std::size_t const N = group::N;
  576. iterator pbg(buckets + static_cast<difference_type>(n),
  577. groups + static_cast<difference_type>(n / N));
  578. return pbg;
  579. } else {
  580. return this->end();
  581. }
  582. }
  583. span<Bucket> raw()
  584. {
  585. BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
  586. return span<Bucket>(boost::to_address(buckets), size_);
  587. }
  588. size_type position(std::size_t hash) const
  589. {
  590. return size_policy::position(hash, size_index_);
  591. }
  592. void clear()
  593. {
  594. this->deallocate();
  595. size_index_ = 0;
  596. size_ = 0;
  597. }
  598. void append_bucket_group(iterator itb) noexcept
  599. {
  600. std::size_t const N = group::N;
  601. bool const is_empty_bucket = (!itb->next);
  602. if (is_empty_bucket) {
  603. bucket_pointer pb = itb.p;
  604. group_pointer pbg = itb.pbg;
  605. std::size_t n =
  606. static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
  607. bool const is_empty_group = (!pbg->bitmask);
  608. if (is_empty_group) {
  609. size_type const num_groups = this->groups_len();
  610. group_pointer last_group =
  611. groups + static_cast<difference_type>(num_groups - 1);
  612. pbg->buckets =
  613. buckets + static_cast<difference_type>(N * (n / N));
  614. pbg->next = last_group->next;
  615. pbg->next->prev = pbg;
  616. pbg->prev = last_group;
  617. pbg->prev->next = pbg;
  618. }
  619. pbg->bitmask |= set_bit(n % N);
  620. }
  621. }
  622. void insert_node(iterator itb, node_pointer p) noexcept
  623. {
  624. this->append_bucket_group(itb);
  625. p->next = itb->next;
  626. itb->next = p;
  627. }
  628. void insert_node_hint(
  629. iterator itb, node_pointer p, node_pointer hint) noexcept
  630. {
  631. this->append_bucket_group(itb);
  632. if (hint) {
  633. p->next = hint->next;
  634. hint->next = p;
  635. } else {
  636. p->next = itb->next;
  637. itb->next = p;
  638. }
  639. }
  640. void extract_node(iterator itb, node_pointer p) noexcept
  641. {
  642. node_pointer* pp = std::addressof(itb->next);
  643. while ((*pp) != p)
  644. pp = std::addressof((*pp)->next);
  645. *pp = p->next;
  646. if (!itb->next)
  647. unlink_bucket(itb);
  648. }
  649. void extract_node_after(iterator itb, node_pointer* pp) noexcept
  650. {
  651. *pp = (*pp)->next;
  652. if (!itb->next)
  653. unlink_bucket(itb);
  654. }
  655. void unlink_empty_buckets() noexcept
  656. {
  657. std::size_t const N = group::N;
  658. group_pointer pbg = groups,
  659. last = groups + static_cast<difference_type>(
  660. this->groups_len() - 1);
  661. for (; pbg != last; ++pbg) {
  662. if (!pbg->buckets) {
  663. continue;
  664. }
  665. for (std::size_t n = 0; n < N; ++n) {
  666. bucket_pointer bs = pbg->buckets;
  667. bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
  668. if (!b.next)
  669. pbg->bitmask &= reset_bit(n);
  670. }
  671. if (!pbg->bitmask && pbg->next)
  672. unlink_group(pbg);
  673. }
  674. // do not check end bucket
  675. for (std::size_t n = 0; n < size_ % N; ++n) {
  676. if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
  677. pbg->bitmask &= reset_bit(n);
  678. }
  679. }
  680. void unlink_bucket(iterator itb)
  681. {
  682. typename iterator::bucket_pointer p = itb.p;
  683. typename iterator::bucket_group_pointer pbg = itb.pbg;
  684. if (!(pbg->bitmask &=
  685. reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
  686. unlink_group(pbg);
  687. }
  688. private:
  689. void unlink_group(group_pointer pbg)
  690. {
  691. pbg->next->prev = pbg->prev;
  692. pbg->prev->next = pbg->next;
  693. pbg->prev = pbg->next = group_pointer();
  694. }
  695. void swap_allocator_if_pocs(grouped_bucket_array& other)
  696. {
  697. using allocator_pocs =
  698. typename boost::allocator_propagate_on_container_swap<
  699. allocator_type>::type;
  700. swap_allocator_if_pocs(
  701. other, std::integral_constant<bool, allocator_pocs::value>());
  702. }
  703. void swap_allocator_if_pocs(
  704. grouped_bucket_array& other, std::true_type /* propagate */)
  705. {
  706. boost::core::invoke_swap(
  707. get_node_allocator(), other.get_node_allocator());
  708. }
  709. void swap_allocator_if_pocs(
  710. grouped_bucket_array&, std::false_type /* don't propagate */)
  711. {
  712. }
  713. };
  714. } // namespace detail
  715. } // namespace unordered
  716. } // namespace boost
  717. #endif // BOOST_UNORDERED_DETAIL_FCA_HPP