fca.hpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. // Copyright (C) 2022 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/core/addressof.hpp>
  94. #include <boost/core/allocator_access.hpp>
  95. #include <boost/core/bit.hpp>
  96. #include <boost/core/empty_value.hpp>
  97. #include <boost/core/no_exceptions_support.hpp>
  98. #include <boost/cstdint.hpp>
  99. #include <boost/move/core.hpp>
  100. #include <boost/move/utility_core.hpp>
  101. #include <boost/swap.hpp>
  102. #include <boost/type_traits/aligned_storage.hpp>
  103. #include <boost/type_traits/alignment_of.hpp>
  104. #include <boost/config.hpp>
  105. #include <iterator>
  106. namespace boost {
  107. namespace unordered {
  108. namespace detail {
  109. template <class ValueType, class VoidPtr> struct node
  110. {
  111. typedef ValueType value_type;
  112. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  113. node>::type node_pointer;
  114. node_pointer next;
  115. typename boost::aligned_storage<sizeof(value_type),
  116. boost::alignment_of<value_type>::value>::type buf;
  117. node() BOOST_NOEXCEPT : next(), buf() {}
  118. value_type* value_ptr() BOOST_NOEXCEPT
  119. {
  120. return reinterpret_cast<value_type*>(buf.address());
  121. }
  122. value_type& value() BOOST_NOEXCEPT
  123. {
  124. return *reinterpret_cast<value_type*>(buf.address());
  125. }
  126. };
  127. template <class Node, class VoidPtr> struct bucket
  128. {
  129. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  130. Node>::type node_pointer;
  131. typedef typename boost::pointer_traits<VoidPtr>::template rebind_to<
  132. bucket>::type bucket_pointer;
  133. node_pointer next;
  134. bucket() BOOST_NOEXCEPT : next() {}
  135. };
  136. template <class Bucket> struct bucket_group
  137. {
  138. typedef typename Bucket::bucket_pointer bucket_pointer;
  139. typedef
  140. typename boost::pointer_traits<bucket_pointer>::template rebind_to<
  141. bucket_group>::type bucket_group_pointer;
  142. BOOST_STATIC_CONSTANT(std::size_t, N = sizeof(std::size_t) * CHAR_BIT);
  143. bucket_pointer buckets;
  144. std::size_t bitmask;
  145. bucket_group_pointer next, prev;
  146. bucket_group() BOOST_NOEXCEPT : buckets(), bitmask(0), next(), prev() {}
  147. ~bucket_group() {}
  148. };
  149. inline std::size_t set_bit(std::size_t n) { return std::size_t(1) << n; }
  150. inline std::size_t reset_bit(std::size_t n)
  151. {
  152. return ~(std::size_t(1) << n);
  153. }
  154. inline std::size_t reset_first_bits(std::size_t n) // n>0
  155. {
  156. return ~(~(std::size_t(0)) >> (sizeof(std::size_t) * 8 - n));
  157. }
  158. template <class Bucket> struct grouped_bucket_iterator
  159. {
  160. public:
  161. typedef typename Bucket::bucket_pointer bucket_pointer;
  162. typedef
  163. typename boost::pointer_traits<bucket_pointer>::template rebind_to<
  164. bucket_group<Bucket> >::type bucket_group_pointer;
  165. typedef Bucket value_type;
  166. typedef typename boost::pointer_traits<bucket_pointer>::difference_type
  167. difference_type;
  168. typedef Bucket& reference;
  169. typedef Bucket* pointer;
  170. typedef std::forward_iterator_tag iterator_category;
  171. private:
  172. bucket_pointer p;
  173. bucket_group_pointer pbg;
  174. public:
  175. grouped_bucket_iterator() : p(), pbg() {}
  176. reference operator*() const BOOST_NOEXCEPT { return dereference(); }
  177. pointer operator->() const BOOST_NOEXCEPT
  178. {
  179. return boost::to_address(p);
  180. }
  181. grouped_bucket_iterator& operator++() BOOST_NOEXCEPT
  182. {
  183. increment();
  184. return *this;
  185. }
  186. grouped_bucket_iterator operator++(int) BOOST_NOEXCEPT
  187. {
  188. grouped_bucket_iterator old = *this;
  189. increment();
  190. return old;
  191. }
  192. bool operator==(
  193. grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
  194. {
  195. return equal(other);
  196. }
  197. bool operator!=(
  198. grouped_bucket_iterator const& other) const BOOST_NOEXCEPT
  199. {
  200. return !equal(other);
  201. }
  202. private:
  203. template <typename, typename, typename>
  204. friend class grouped_bucket_array;
  205. BOOST_STATIC_CONSTANT(std::size_t, N = bucket_group<Bucket>::N);
  206. grouped_bucket_iterator(bucket_pointer p_, bucket_group_pointer pbg_)
  207. : p(p_), pbg(pbg_)
  208. {
  209. }
  210. Bucket& dereference() const BOOST_NOEXCEPT { return *p; }
  211. bool equal(const grouped_bucket_iterator& x) const BOOST_NOEXCEPT
  212. {
  213. return p == x.p;
  214. }
  215. void increment() BOOST_NOEXCEPT
  216. {
  217. std::size_t const offset = static_cast<std::size_t>(p - pbg->buckets);
  218. std::size_t n = std::size_t(boost::core::countr_zero(
  219. pbg->bitmask & reset_first_bits(offset + 1)));
  220. if (n < N) {
  221. p = pbg->buckets + static_cast<difference_type>(n);
  222. } else {
  223. pbg = pbg->next;
  224. std::ptrdiff_t x = boost::core::countr_zero(pbg->bitmask);
  225. p = pbg->buckets + x;
  226. }
  227. }
  228. };
  229. template <class Node> struct const_grouped_local_bucket_iterator;
  230. template <class Node> struct grouped_local_bucket_iterator
  231. {
  232. typedef typename Node::node_pointer node_pointer;
  233. public:
  234. typedef typename Node::value_type value_type;
  235. typedef value_type element_type;
  236. typedef value_type* pointer;
  237. typedef value_type& reference;
  238. typedef std::ptrdiff_t difference_type;
  239. typedef std::forward_iterator_tag iterator_category;
  240. grouped_local_bucket_iterator() : p() {}
  241. reference operator*() const BOOST_NOEXCEPT { return dereference(); }
  242. pointer operator->() const BOOST_NOEXCEPT
  243. {
  244. return boost::to_address(p);
  245. }
  246. grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
  247. {
  248. increment();
  249. return *this;
  250. }
  251. grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
  252. {
  253. grouped_local_bucket_iterator old = *this;
  254. increment();
  255. return old;
  256. }
  257. bool operator==(
  258. grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
  259. {
  260. return equal(other);
  261. }
  262. bool operator!=(
  263. grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
  264. {
  265. return !equal(other);
  266. }
  267. private:
  268. template <typename, typename, typename>
  269. friend class grouped_bucket_array;
  270. template <class> friend struct const_grouped_local_bucket_iterator;
  271. grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
  272. value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
  273. bool equal(const grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
  274. {
  275. return p == x.p;
  276. }
  277. void increment() BOOST_NOEXCEPT { p = p->next; }
  278. node_pointer p;
  279. };
  280. template <class Node> struct const_grouped_local_bucket_iterator
  281. {
  282. typedef typename Node::node_pointer node_pointer;
  283. public:
  284. typedef typename Node::value_type const value_type;
  285. typedef value_type const element_type;
  286. typedef value_type const* pointer;
  287. typedef value_type const& reference;
  288. typedef std::ptrdiff_t difference_type;
  289. typedef std::forward_iterator_tag iterator_category;
  290. const_grouped_local_bucket_iterator() : p() {}
  291. const_grouped_local_bucket_iterator(
  292. grouped_local_bucket_iterator<Node> it)
  293. : p(it.p)
  294. {
  295. }
  296. reference operator*() const BOOST_NOEXCEPT { return dereference(); }
  297. pointer operator->() const BOOST_NOEXCEPT
  298. {
  299. return boost::to_address(p);
  300. }
  301. const_grouped_local_bucket_iterator& operator++() BOOST_NOEXCEPT
  302. {
  303. increment();
  304. return *this;
  305. }
  306. const_grouped_local_bucket_iterator operator++(int) BOOST_NOEXCEPT
  307. {
  308. const_grouped_local_bucket_iterator old = *this;
  309. increment();
  310. return old;
  311. }
  312. bool operator==(
  313. const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
  314. {
  315. return equal(other);
  316. }
  317. bool operator!=(
  318. const_grouped_local_bucket_iterator const& other) const BOOST_NOEXCEPT
  319. {
  320. return !equal(other);
  321. }
  322. private:
  323. template <typename, typename, typename>
  324. friend class grouped_bucket_array;
  325. const_grouped_local_bucket_iterator(node_pointer p_) : p(p_) {}
  326. value_type& dereference() const BOOST_NOEXCEPT { return p->value(); }
  327. bool equal(
  328. const const_grouped_local_bucket_iterator& x) const BOOST_NOEXCEPT
  329. {
  330. return p == x.p;
  331. }
  332. void increment() BOOST_NOEXCEPT { p = p->next; }
  333. node_pointer p;
  334. };
  335. template <class T> struct span
  336. {
  337. T* begin() const BOOST_NOEXCEPT { return data; }
  338. T* end() const BOOST_NOEXCEPT { return data + size; }
  339. T* data;
  340. std::size_t size;
  341. span(T* data_, std::size_t size_) : data(data_), size(size_) {}
  342. };
  343. template <class Bucket, class Allocator, class SizePolicy>
  344. class grouped_bucket_array
  345. : boost::empty_value<typename boost::allocator_rebind<Allocator,
  346. node<typename boost::allocator_value_type<Allocator>::type,
  347. typename boost::allocator_void_pointer<Allocator>::type> >::
  348. type>
  349. {
  350. BOOST_MOVABLE_BUT_NOT_COPYABLE(grouped_bucket_array)
  351. typedef typename boost::allocator_value_type<Allocator>::type
  352. allocator_value_type;
  353. typedef
  354. typename boost::allocator_void_pointer<Allocator>::type void_pointer;
  355. typedef typename boost::allocator_difference_type<Allocator>::type
  356. difference_type;
  357. public:
  358. typedef typename boost::allocator_rebind<Allocator,
  359. node<allocator_value_type, void_pointer> >::type node_allocator_type;
  360. typedef node<allocator_value_type, void_pointer> node_type;
  361. typedef typename boost::allocator_pointer<node_allocator_type>::type
  362. node_pointer;
  363. typedef SizePolicy size_policy;
  364. private:
  365. typedef typename boost::allocator_rebind<Allocator, Bucket>::type
  366. bucket_allocator_type;
  367. typedef typename boost::allocator_pointer<bucket_allocator_type>::type
  368. bucket_pointer;
  369. typedef boost::pointer_traits<bucket_pointer> bucket_pointer_traits;
  370. typedef bucket_group<Bucket> group;
  371. typedef typename boost::allocator_rebind<Allocator, group>::type
  372. group_allocator_type;
  373. typedef typename boost::allocator_pointer<group_allocator_type>::type
  374. group_pointer;
  375. typedef typename boost::pointer_traits<group_pointer>
  376. group_pointer_traits;
  377. public:
  378. typedef Bucket value_type;
  379. typedef Bucket bucket_type;
  380. typedef std::size_t size_type;
  381. typedef Allocator allocator_type;
  382. typedef grouped_bucket_iterator<Bucket> iterator;
  383. typedef grouped_local_bucket_iterator<node_type> local_iterator;
  384. typedef const_grouped_local_bucket_iterator<node_type>
  385. const_local_iterator;
  386. private:
  387. std::size_t size_index_, size_;
  388. bucket_pointer buckets;
  389. group_pointer groups;
  390. public:
  391. static std::size_t bucket_count_for(std::size_t num_buckets)
  392. {
  393. if (num_buckets == 0) {
  394. return 0;
  395. }
  396. return size_policy::size(size_policy::size_index(num_buckets));
  397. }
  398. grouped_bucket_array()
  399. : empty_value<node_allocator_type>(
  400. empty_init_t(), node_allocator_type()),
  401. size_index_(0), size_(0), buckets(), groups()
  402. {
  403. }
  404. grouped_bucket_array(size_type n, const Allocator& al)
  405. : empty_value<node_allocator_type>(empty_init_t(), al),
  406. size_index_(0),
  407. size_(0), buckets(), groups()
  408. {
  409. if (n == 0) {
  410. return;
  411. }
  412. size_index_ = size_policy::size_index(n);
  413. size_ = size_policy::size(size_index_);
  414. bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
  415. group_allocator_type group_alloc = this->get_group_allocator();
  416. size_type const num_buckets = buckets_len();
  417. size_type const num_groups = groups_len();
  418. buckets = boost::allocator_allocate(bucket_alloc, num_buckets);
  419. BOOST_TRY
  420. {
  421. groups = boost::allocator_allocate(group_alloc, num_groups);
  422. bucket_type* pb = boost::to_address(buckets);
  423. for (size_type i = 0; i < num_buckets; ++i) {
  424. new (pb + i) bucket_type();
  425. }
  426. group* pg = boost::to_address(groups);
  427. for (size_type i = 0; i < num_groups; ++i) {
  428. new (pg + i) group();
  429. }
  430. }
  431. BOOST_CATCH(...)
  432. {
  433. boost::allocator_deallocate(bucket_alloc, buckets, num_buckets);
  434. BOOST_RETHROW
  435. }
  436. BOOST_CATCH_END
  437. size_type const N = group::N;
  438. group_pointer pbg =
  439. groups + static_cast<difference_type>(num_groups - 1);
  440. pbg->buckets =
  441. buckets + static_cast<difference_type>(N * (size_ / N));
  442. pbg->bitmask = set_bit(size_ % N);
  443. pbg->next = pbg->prev = pbg;
  444. }
  445. ~grouped_bucket_array() { this->deallocate(); }
  446. grouped_bucket_array(
  447. BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
  448. : empty_value<node_allocator_type>(
  449. empty_init_t(), other.get_node_allocator()),
  450. size_index_(other.size_index_),
  451. size_(other.size_),
  452. buckets(other.buckets),
  453. groups(other.groups)
  454. {
  455. other.size_ = 0;
  456. other.size_index_ = 0;
  457. other.buckets = bucket_pointer();
  458. other.groups = group_pointer();
  459. }
  460. grouped_bucket_array& operator=(
  461. BOOST_RV_REF(grouped_bucket_array) other) BOOST_NOEXCEPT
  462. {
  463. BOOST_ASSERT(
  464. this->get_node_allocator() == other.get_node_allocator());
  465. if (this == boost::addressof(other)) {
  466. return *this;
  467. }
  468. this->deallocate();
  469. size_index_ = other.size_index_;
  470. size_ = other.size_;
  471. buckets = other.buckets;
  472. groups = other.groups;
  473. other.size_index_ = 0;
  474. other.size_ = 0;
  475. other.buckets = bucket_pointer();
  476. other.groups = group_pointer();
  477. return *this;
  478. }
  479. void deallocate() BOOST_NOEXCEPT
  480. {
  481. if (buckets) {
  482. bucket_allocator_type bucket_alloc = this->get_bucket_allocator();
  483. boost::allocator_deallocate(
  484. bucket_alloc, buckets, this->buckets_len());
  485. buckets = bucket_pointer();
  486. }
  487. if (groups) {
  488. group_allocator_type group_alloc = this->get_group_allocator();
  489. boost::allocator_deallocate(
  490. group_alloc, groups, this->groups_len());
  491. groups = group_pointer();
  492. }
  493. }
  494. void swap(grouped_bucket_array& other)
  495. {
  496. std::swap(size_index_, other.size_index_);
  497. std::swap(size_, other.size_);
  498. std::swap(buckets, other.buckets);
  499. std::swap(groups, other.groups);
  500. bool b = boost::allocator_propagate_on_container_swap<
  501. allocator_type>::type::value;
  502. if (b) {
  503. boost::swap(get_node_allocator(), other.get_node_allocator());
  504. }
  505. }
  506. node_allocator_type const& get_node_allocator() const
  507. {
  508. return empty_value<node_allocator_type>::get();
  509. }
  510. node_allocator_type& get_node_allocator()
  511. {
  512. return empty_value<node_allocator_type>::get();
  513. }
  514. bucket_allocator_type get_bucket_allocator() const
  515. {
  516. return this->get_node_allocator();
  517. }
  518. group_allocator_type get_group_allocator() const
  519. {
  520. return this->get_node_allocator();
  521. }
  522. size_type buckets_len() const BOOST_NOEXCEPT { return size_ + 1; }
  523. size_type groups_len() const BOOST_NOEXCEPT
  524. {
  525. return size_ / group::N + 1;
  526. }
  527. void reset_allocator(Allocator const& allocator_)
  528. {
  529. this->get_node_allocator() = node_allocator_type(allocator_);
  530. }
  531. size_type bucket_count() const { return size_; }
  532. iterator begin() const { return size_ == 0 ? end() : ++at(size_); }
  533. iterator end() const
  534. {
  535. // micro optimization: no need to return the bucket group
  536. // as end() is not incrementable
  537. iterator pbg;
  538. pbg.p =
  539. buckets + static_cast<difference_type>(this->buckets_len() - 1);
  540. return pbg;
  541. }
  542. local_iterator begin(size_type n) const
  543. {
  544. if (size_ == 0) {
  545. return this->end(n);
  546. }
  547. return local_iterator(
  548. (buckets + static_cast<difference_type>(n))->next);
  549. }
  550. local_iterator end(size_type) const { return local_iterator(); }
  551. size_type capacity() const BOOST_NOEXCEPT { return size_; }
  552. iterator at(size_type n) const
  553. {
  554. if (size_ > 0) {
  555. std::size_t const N = group::N;
  556. iterator pbg(buckets + static_cast<difference_type>(n),
  557. groups + static_cast<difference_type>(n / N));
  558. return pbg;
  559. } else {
  560. return this->end();
  561. }
  562. }
  563. span<Bucket> raw()
  564. {
  565. BOOST_ASSERT(size_ == 0 || size_ < this->buckets_len());
  566. return span<Bucket>(boost::to_address(buckets), size_);
  567. }
  568. size_type position(std::size_t hash) const
  569. {
  570. return size_policy::position(hash, size_index_);
  571. }
  572. void clear()
  573. {
  574. this->deallocate();
  575. size_index_ = 0;
  576. size_ = 0;
  577. }
  578. void append_bucket_group(iterator itb) BOOST_NOEXCEPT
  579. {
  580. std::size_t const N = group::N;
  581. bool const is_empty_bucket = (!itb->next);
  582. if (is_empty_bucket) {
  583. bucket_pointer pb = itb.p;
  584. group_pointer pbg = itb.pbg;
  585. std::size_t n =
  586. static_cast<std::size_t>(boost::to_address(pb) - &buckets[0]);
  587. bool const is_empty_group = (!pbg->bitmask);
  588. if (is_empty_group) {
  589. size_type const num_groups = this->groups_len();
  590. group_pointer last_group =
  591. groups + static_cast<difference_type>(num_groups - 1);
  592. pbg->buckets =
  593. buckets + static_cast<difference_type>(N * (n / N));
  594. pbg->next = last_group->next;
  595. pbg->next->prev = pbg;
  596. pbg->prev = last_group;
  597. pbg->prev->next = pbg;
  598. }
  599. pbg->bitmask |= set_bit(n % N);
  600. }
  601. }
  602. void insert_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
  603. {
  604. this->append_bucket_group(itb);
  605. p->next = itb->next;
  606. itb->next = p;
  607. }
  608. void insert_node_hint(
  609. iterator itb, node_pointer p, node_pointer hint) BOOST_NOEXCEPT
  610. {
  611. this->append_bucket_group(itb);
  612. if (hint) {
  613. p->next = hint->next;
  614. hint->next = p;
  615. } else {
  616. p->next = itb->next;
  617. itb->next = p;
  618. }
  619. }
  620. void extract_node(iterator itb, node_pointer p) BOOST_NOEXCEPT
  621. {
  622. node_pointer* pp = boost::addressof(itb->next);
  623. while ((*pp) != p)
  624. pp = boost::addressof((*pp)->next);
  625. *pp = p->next;
  626. if (!itb->next)
  627. unlink_bucket(itb);
  628. }
  629. void extract_node_after(iterator itb, node_pointer* pp) BOOST_NOEXCEPT
  630. {
  631. *pp = (*pp)->next;
  632. if (!itb->next)
  633. unlink_bucket(itb);
  634. }
  635. void unlink_empty_buckets() BOOST_NOEXCEPT
  636. {
  637. std::size_t const N = group::N;
  638. group_pointer pbg = groups,
  639. last = groups + static_cast<difference_type>(
  640. this->groups_len() - 1);
  641. for (; pbg != last; ++pbg) {
  642. if (!pbg->buckets) {
  643. continue;
  644. }
  645. for (std::size_t n = 0; n < N; ++n) {
  646. bucket_pointer bs = pbg->buckets;
  647. bucket_type& b = bs[static_cast<std::ptrdiff_t>(n)];
  648. if (!b.next)
  649. pbg->bitmask &= reset_bit(n);
  650. }
  651. if (!pbg->bitmask && pbg->next)
  652. unlink_group(pbg);
  653. }
  654. // do not check end bucket
  655. for (std::size_t n = 0; n < size_ % N; ++n) {
  656. if (!pbg->buckets[static_cast<std::ptrdiff_t>(n)].next)
  657. pbg->bitmask &= reset_bit(n);
  658. }
  659. }
  660. void unlink_bucket(iterator itb)
  661. {
  662. typename iterator::bucket_pointer p = itb.p;
  663. typename iterator::bucket_group_pointer pbg = itb.pbg;
  664. if (!(pbg->bitmask &=
  665. reset_bit(static_cast<std::size_t>(p - pbg->buckets))))
  666. unlink_group(pbg);
  667. }
  668. private:
  669. void unlink_group(group_pointer pbg)
  670. {
  671. pbg->next->prev = pbg->prev;
  672. pbg->prev->next = pbg->next;
  673. pbg->prev = pbg->next = group_pointer();
  674. }
  675. };
  676. } // namespace detail
  677. } // namespace unordered
  678. } // namespace boost
  679. #endif // BOOST_UNORDERED_DETAIL_FCA_HPP