libs/hub/include/boost/container/hub.hpp

99.0% Lines (676/683) 91.3% Functions (1192/1306)
libs/hub/include/boost/container/hub.hpp
Line Hits Source Code
1 /* Hub container.
2 *
3 * Copyright 2025-2026 Joaquin M Lopez Munoz.
4 * Distributed under the Boost Software License, Version 1.0.
5 * (See accompanying file LICENSE_1_0.txt or copy at
6 * http://www.boost.org/LICENSE_1_0.txt)
7 */
8
9 #ifndef BOOST_CONTAINER_HUB_HPP
10 #define BOOST_CONTAINER_HUB_HPP
11
12 #include <algorithm>
13 #include <boost/assert.hpp>
14 #include <boost/config.hpp>
15 #include <boost/config/workaround.hpp>
16 #include <boost/core/allocator_access.hpp>
17 #include <boost/core/bit.hpp>
18 #include <boost/core/empty_value.hpp>
19 #include <boost/core/no_exceptions_support.hpp>
20 #include <boost/core/pointer_traits.hpp>
21 #include <boost/throw_exception.hpp>
22 #include <cstddef>
23 #include <cstdint>
24 #include <functional>
25 #include <initializer_list>
26 #include <iterator>
27 #include <memory>
28 #include <new>
29 #include <stdexcept>
30 #include <type_traits>
31 #include <utility>
32
33 #ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
34 #include <memory_resource>
35 #endif
36
37 #if defined(BOOST_NO_CXX20_HDR_CONCEPTS) || defined(BOOST_NO_CXX20_HDR_RANGES)
38 #define BOOST_CONTAINER_HUB_NO_RANGES
39 #elif BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 170100) && \
40 defined(BOOST_LIBSTDCXX_VERSION)
41 /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109647
42 * https://github.com/llvm/llvm-project/issues/49620
43 */
44 #define BOOST_CONTAINER_HUB_NO_RANGES
45 #endif
46
47 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
48 #include <concepts>
49 #include <ranges>
50 #endif
51
52 #if !defined(BOOST_CONTAINER_HUB_DISABLE_SSE2)
53 #if defined(BOOST_CONTAINER_HUB_ENABLE_SSE2)|| \
54 defined(__SSE2__) || \
55 defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
56 #define BOOST_CONTAINER_HUB_SSE2
57 #endif
58 #endif
59
60 #if defined(BOOST_CONTAINER_HUB_SSE2)
61 #include <emmintrin.h>
62 #endif
63
64 #ifdef __has_builtin
65 #define BOOST_CONTAINER_HUB_HAS_BUILTIN(x) __has_builtin(x)
66 #else
67 #define BOOST_CONTAINER_HUB_HAS_BUILTIN(x) 0
68 #endif
69
70 #if !defined(NDEBUG)
71 #define BOOST_CONTAINER_HUB_ASSUME(cond) BOOST_ASSERT(cond)
72 #elif BOOST_CONTAINER_HUB_HAS_BUILTIN(__builtin_assume)
73 #define BOOST_CONTAINER_HUB_ASSUME(cond) __builtin_assume(cond)
74 #elif defined(__GNUC__) || \
75 BOOST_CONTAINER_HUB_HAS_BUILTIN(__builtin_unreachable)
76 #define BOOST_CONTAINER_HUB_ASSUME(cond) \
77 do{ \
78 if(!(cond)) __builtin_unreachable(); \
79 } while(0)
80 #elif defined(_MSC_VER)
81 #define BOOST_CONTAINER_HUB_ASSUME(cond) __assume(cond)
82 #else
83 #define BOOST_CONTAINER_HUB_ASSUME(cond) \
84 do{ \
85 static_cast<void>(false && (cond)); \
86 } while(0)
87 #endif
88
89 /* We use BOOST_CONTAINER_HUB_PREFETCH[_BLOCK] macros rather than proper
90 * functions because of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109985
91 */
92
93 #if defined(BOOST_GCC) || defined(BOOST_CLANG)
94 #define BOOST_CONTAINER_HUB_PREFETCH(p) \
95 __builtin_prefetch((const char*)boost::to_address(p))
96 #elif defined(BOOST_CONTAINER_HUB_SSE2)
97 #define BOOST_CONTAINER_HUB_PREFETCH(p) \
98 _mm_prefetch((const char*)boost::to_address(p), _MM_HINT_T0)
99 #else
100 #define BOOST_CONTAINER_HUB_PREFETCH(p) ((void)(p))
101 #endif
102
103 #define BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb, Block) \
104 do{ \
105 auto p0 = &static_cast<Block&>(*(pbb)); \
106 BOOST_CONTAINER_HUB_PREFETCH(p0->data()); \
107 } while(0)
108
109 #if defined(BOOST_MSVC)
110 #pragma warning(push)
111 #pragma warning(disable:4714) /* marked as __forceinline not inlined */
112 #endif
113
114 namespace boost {
115
116 namespace container {
117
118 template<typename T, typename Allocator = std::allocator<T>>
119 class hub;
120
121 template<typename T, typename Allocator, typename Predicate>
122 typename hub<T, Allocator>::size_type erase_if(hub<T, Allocator>&, Predicate);
123
124 #ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
125 namespace pmr {
126
127 template<typename T>
128 using hub = boost::container::hub<T, std::pmr::polymorphic_allocator<T>>;
129
130 }
131 #endif
132
133 namespace hub_detail {
134
135 30574994 inline int unchecked_countr_zero(std::uint64_t x)
136 {
137 #if defined(BOOST_MSVC) && (defined(_M_X64) || defined(_M_ARM64))
138 unsigned long r;
139 _BitScanForward64(&r, x);
140 return (int)r;
141 #elif defined(BOOST_GCC) || defined(BOOST_CLANG)
142 30574994 return (int)__builtin_ctzll(x);
143 #else
144 BOOST_CONTAINER_HUB_ASSUME(x != 0);
145 return (int)core::countr_zero(x);
146 #endif
147 }
148
149 10413115 inline int unchecked_countr_one(std::uint64_t x)
150 {
151 10413115 return unchecked_countr_zero(~x);
152 }
153
154 626934 inline int unchecked_countl_zero(std::uint64_t x)
155 {
156 #if defined(BOOST_MSVC) && (defined(_M_X64) || defined(_M_ARM64))
157 unsigned long r;
158 _BitScanReverse64(&r, x);
159 return (int)(63 - r);
160 #elif defined(BOOST_GCC) || defined(BOOST_CLANG)
161 626934 return (int)__builtin_clzll(x);
162 #else
163 BOOST_CONTAINER_HUB_ASSUME(x != 0);
164 return (int)core::countl_zero(x);
165 #endif
166 }
167
168 template<typename Pointer, typename T>
169 using pointer_rebind_t =
170 typename pointer_traits<Pointer>::template rebind<T>;
171
172 template<typename VoidPointer>
173 struct block_base
174 {
175 using pointer = pointer_rebind_t<VoidPointer, block_base>;
176 using const_pointer = pointer_rebind_t<VoidPointer, const block_base>;
177 using mask_type = std::uint64_t;
178
179 static constexpr int N = 64;
180 static constexpr mask_type full = (mask_type)(-1);
181
182 21616095 static pointer pointer_to(block_base& x) noexcept
183 {
184 21616095 return pointer_traits<pointer>::pointer_to(x);
185 }
186
187 3427 static const_pointer pointer_to(const block_base& x) noexcept
188 {
189 3427 return pointer_traits<const_pointer>::pointer_to(x);
190 }
191
192 BOOST_FORCEINLINE void link_available_before(pointer p) noexcept
193 {
194 155686 next_available = p;
195 155686 prev_available = p->prev_available;
196 156492 next_available->prev_available = pointer_to(*this);
197 156492 prev_available->next_available = pointer_to(*this);
198 156300 }
199
200 BOOST_FORCEINLINE void link_available_after(pointer p) noexcept
201 {
202 117687 prev_available = p;
203 117687 next_available = p->next_available;
204 117807 next_available->prev_available = pointer_to(*this);
205 117807 prev_available->next_available = pointer_to(*this);
206 117777 }
207
208 BOOST_FORCEINLINE void unlink_available() noexcept
209 {
210 174337 prev_available->next_available = next_available;
211 174483 next_available->prev_available = prev_available;
212 174337 }
213
214 BOOST_FORCEINLINE void link_before(pointer p) noexcept
215 {
216 155662 next = p;
217 155662 prev = p->prev;
218 156398 next->prev = pointer_to(*this);
219 156412 prev->next = pointer_to(*this);
220 156234 }
221
222 BOOST_FORCEINLINE void unlink() noexcept
223 {
224 99074 prev->next = next;
225 99164 next->prev = prev;
226 99074 }
227
228 pointer prev_available,
229 next_available,
230 prev,
231 next;
232 mask_type mask;
233 };
234
235 template<typename ValuePointer>
236 struct block: block_base<pointer_rebind_t<ValuePointer, void>>
237 {
238 using super = block_base<pointer_rebind_t<ValuePointer, void>>;
239
240 41019428 ValuePointer data() noexcept { return data_; }
241 ValuePointer data_;
242 };
243
244 template<typename ValuePointer>
245 17940 void swap_payload(block<ValuePointer>& x, block<ValuePointer>& y) noexcept
246 {
247 17940 std::swap(x.mask, y.mask);
248 17940 std::swap(x.data_, y.data_);
249 17940 }
250
251 template<typename ValuePointer>
252 struct block_list: block<ValuePointer>
253 {
254 using block = hub_detail::block<ValuePointer>;
255 using block_base = typename block::super;
256 using block_base_pointer = typename block_base::pointer;
257 using const_block_base_pointer = typename block_base::const_pointer;
258 using block_pointer = pointer_rebind_t<ValuePointer, block>;
259 using block_base::full;
260 using block_base::pointer_to;
261 using block_base::prev_available;
262 using block_base::next_available;
263 using block_base::prev;
264 using block_base::next;
265 using block_base::mask;
266 using block::data_;
267
268 static block_pointer
269 15624323 static_cast_block_pointer(block_base_pointer pbb) noexcept
270 {
271 15621282 return pointer_traits<block_pointer>::pointer_to(
272 15624323 static_cast<block&>(*pbb));
273 }
274
275 347 block_list()
276 59 {
277 347 reset();
278 347 mask = 1; /* sentinel */
279 347 data_ = nullptr;
280 347 }
281
282 29 block_list(block_list&& x) noexcept: block_list{}
283 {
284 34 if(x.next_available != x.header()) {
285 29 prev_available = x.prev_available;
286 29 next_available = x.next_available;
287 34 next_available->prev_available = header();
288 39 prev_available->next_available = header();
289 }
290 34 if(x.prev != x.header()) {
291 29 prev = x.prev;
292 29 next = x.next;
293 34 next->prev = header();
294 39 prev->next = header();
295 }
296 29 x.reset();
297 29 }
298
299 53 block_list& operator=(block_list&& x) noexcept
300 {
301 53 reset();
302 63 if(x.next_available != x.header()) {
303 45 prev_available = x.prev_available;
304 45 next_available = x.next_available;
305 53 next_available->prev_available = header();
306 61 prev_available->next_available = header();
307 }
308 63 if(x.prev != x.header()) {
309 45 prev = x.prev;
310 45 next = x.next;
311 53 next->prev = header();
312 61 prev->next = header();
313 }
314 53 x.reset();
315 53 return *this;
316 }
317
318 823 void reset() noexcept
319 {
320 823 prev_available = header();
321 823 next_available = header();
322 823 prev = header();
323 823 next = header();
324 823 }
325
326 10789623 block_base_pointer header() noexcept
327 {
328 10789623 return pointer_to(static_cast<block_base&>(*this));
329 }
330
331 3427 const_block_base_pointer header() const noexcept
332 {
333 3427 return pointer_to(static_cast<const block_base&>(*this));
334 }
335
336 BOOST_FORCEINLINE void link_at_back(block_pointer pb) noexcept
337 {
338 155648 pb->link_before(header());
339 156220 }
340
341 BOOST_FORCEINLINE void link_before(
342 block_pointer pbx, block_pointer pby) noexcept
343 {
344 14 pbx->link_before(pby);
345 14 }
346
347 BOOST_FORCEINLINE static void unlink(block_pointer pb) noexcept
348 {
349 59496 pb->unlink();
350 99358 }
351
352 BOOST_FORCEINLINE void link_available_at_back(block_pointer pb) noexcept
353 {
354 155686 pb->link_available_before(header());
355 156300 }
356
357 BOOST_FORCEINLINE void link_available_at_front(block_pointer pb) noexcept
358 {
359 117687 pb->link_available_after(header());
360 117777 }
361
362 BOOST_FORCEINLINE void unlink_available(block_pointer pb) noexcept
363 {
364 174337 pb->unlink_available();
365 174799 }
366 };
367
368 template<typename ValuePointer>
369 class iterator
370 {
371 using element_type = typename pointer_traits<ValuePointer>::element_type;
372 template<typename Value2Pointer>
373 using enable_if_consts_to_element_type_t =typename std::enable_if<
374 std::is_same<
375 const typename pointer_traits<Value2Pointer>::element_type,
376 element_type>::value
377 >::type;
378
379 public:
380 using value_type = typename std::remove_const<element_type>::type;
381 using difference_type =
382 typename pointer_traits<ValuePointer>::difference_type;
383 using pointer = ValuePointer;
384 using reference = element_type&;
385 using iterator_category = std::bidirectional_iterator_tag;
386
387 iterator() = default;
388 24417 iterator(const iterator&) = default;
389
390 template<
391 typename Value2Pointer,
392 typename = enable_if_consts_to_element_type_t<Value2Pointer>
393 >
394 3480 iterator(const iterator<Value2Pointer>& x) noexcept: pbb{x.pbb}, n{x.n} {}
395
396 2802 iterator& operator=(const iterator& x) = default;
397
398 template<
399 typename Value2Pointer,
400 typename = enable_if_consts_to_element_type_t<Value2Pointer>
401 >
402 3078 iterator& operator=(const iterator<Value2Pointer>& x) noexcept
403 {
404 3078 pbb = x.pbb;
405 3078 n = x.n;
406 3078 return *this;
407 }
408
409 9841711 pointer operator->() const noexcept
410 {
411 9930031 return static_cast<block&>(*pbb).data() + n;
412 }
413
414 9841637 reference operator*() const noexcept
415 {
416 9885779 return *operator->();
417 }
418
419 BOOST_FORCEINLINE iterator& operator++() noexcept
420 {
421 4955404 auto mask = pbb->mask & (full << 1 << n);
422 5323087 if(BOOST_UNLIKELY(mask == 0)) {
423 122688 pbb = pbb->next;
424 136496 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb->next, block);
425 133057 mask = pbb->mask;
426 }
427 5333499 n = hub_detail::unchecked_countr_zero(mask);
428 5333499 return *this;
429 }
430
431 BOOST_FORCEINLINE iterator operator++(int) noexcept
432 {
433 9768 iterator tmp(*this);
434 this->operator++();
435 9768 return tmp;
436 }
437
438 BOOST_FORCEINLINE iterator& operator--() noexcept
439 {
440 9025 auto mask = pbb->mask & (full >> 1 >> (N - 1 - n));
441 35074 if(BOOST_UNLIKELY(mask == 0)) {
442 810 pbb = pbb->prev;
443 2430 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb->prev, block);
444 2025 mask = pbb->mask;
445 }
446 36100 n = N - 1 - hub_detail::unchecked_countl_zero(mask);
447 36100 return *this;
448 }
449
450 BOOST_FORCEINLINE iterator operator--(int) noexcept
451 {
452 1368 iterator tmp(*this);
453 this->operator--();
454 1368 return tmp;
455 }
456
457 5258155 friend bool operator==(const iterator& x, const iterator& y) noexcept
458 {
459 5348159 return x.pbb == y.pbb && x.n == y.n;
460 }
461
462 5172678 friend bool operator!=(const iterator& x, const iterator& y) noexcept
463 {
464 5172678 return !(x == y);
465 }
466
467 private:
468 template<typename> friend class iterator;
469 template<typename, typename> friend class container::hub;
470
471 template<typename T>
472 using pointer_rebind_t = hub_detail::pointer_rebind_t<ValuePointer, T>;
473 using block_base = hub_detail::block_base<pointer_rebind_t<void>>;
474 using block_base_pointer = pointer_rebind_t<block_base>;
475 using const_block_base_pointer = pointer_rebind_t<const block_base>;
476 using nonconst_pointer = pointer_rebind_t<value_type>; /* used by Natvis */
477 using block = hub_detail::block<nonconst_pointer>;
478 using mask_type = typename block_base::mask_type;
479
480 static constexpr int N = block_base::N;
481 static constexpr mask_type full = block_base::full;
482
483 9965327 iterator(const_block_base_pointer pbb_, int n_) noexcept:
484 9965327 pbb{const_cast_block_base_pointer(pbb_)}, n{n_} {}
485
486 523 iterator(const_block_base_pointer pbb_) noexcept:
487 523 pbb{const_cast_block_base_pointer(pbb_)},
488 642 n{hub_detail::unchecked_countr_zero(pbb->mask)}
489 523 {}
490
491 static block_base_pointer
492 9965850 const_cast_block_base_pointer(const_block_base_pointer pbb_) noexcept
493 {
494 9965850 return block_base::pointer_to(const_cast<block_base&>(*pbb_));
495 }
496
497 block_base_pointer pbb = nullptr;
498 int n = 0;
499 };
500
501 template<typename T, std::size_t N>
502 struct sort_iterator
503 {
504 using value_type = T;
505 using difference_type = std::ptrdiff_t;
506 using pointer = T*;
507 using reference = T&;
508 using iterator_category = std::random_access_iterator_tag;
509
510 998663 sort_iterator(T** pp_, std::size_t index_): pp{pp_}, index{index_} {}
511
512 131446458 pointer operator->() const noexcept
513 {
514 131446458 return pp[index / N] + (index % N);
515 }
516
517 131446458 reference operator*() const noexcept
518 {
519 131446458 return *operator->();
520 }
521
522 24379312 sort_iterator& operator++() noexcept
523 {
524 24379312 ++index;
525 24379312 return *this;
526 }
527
528 sort_iterator operator++(int) noexcept
529 {
530 sort_iterator tmp(*this);
531 ++index;
532 return tmp;
533 }
534
535 29888125 sort_iterator& operator--() noexcept
536 {
537 29888125 --index;
538 29888125 return *this;
539 }
540
541 sort_iterator operator--(int) noexcept
542 {
543 sort_iterator tmp(*this);
544 --index;
545 return tmp;
546 }
547
548 friend difference_type
549 748998 operator-(const sort_iterator& x, const sort_iterator& y) noexcept
550 {
551 748998 return (difference_type)(x.index - y.index);
552 }
553
554 sort_iterator& operator+=(difference_type n) noexcept
555 {
556 index += n;
557 return *this;
558 }
559
560 friend sort_iterator
561 748998 operator+(const sort_iterator& x, difference_type n) noexcept
562 {
563 748998 return {x.pp, x.index + n};
564 }
565
566 friend sort_iterator
567 operator+(difference_type n, const sort_iterator& x) noexcept
568 {
569 return {x.pp, n + x.index};
570 }
571
572 sort_iterator& operator-=(difference_type n) noexcept
573 {
574 index -= n;
575 return *this;
576 }
577
578 friend sort_iterator
579 249649 operator-(const sort_iterator& x, difference_type n) noexcept
580 {
581 249649 return {x.pp, x.index - n};
582 }
583
584 reference operator[](difference_type n) const noexcept
585 {
586 return operator*(*this + n);
587 }
588
589 friend bool
590 8 operator==(const sort_iterator& x, const sort_iterator& y) noexcept
591 {
592 8 return x.index == y.index;
593 }
594
595 friend bool
596 2400528 operator!=(const sort_iterator& x, const sort_iterator& y) noexcept
597 {
598 2400528 return x.index != y.index;
599 }
600
601 friend bool
602 8543092 operator<(const sort_iterator& x, const sort_iterator& y) noexcept
603 {
604 8543092 return x.index < y.index;
605 }
606
607 friend bool
608 operator>(const sort_iterator& x, const sort_iterator& y) noexcept
609 {
610 return x.index > y.index;
611 }
612
613 friend bool
614 operator<=(const sort_iterator& x, const sort_iterator& y) noexcept
615 {
616 return x.index <= y.index;
617 }
618
619 friend bool
620 operator>=(const sort_iterator& x, const sort_iterator& y) noexcept
621 {
622 return x.index >= y.index;
623 }
624
625 T** pp;
626 std::size_t index;
627 };
628
629 template<typename T, typename Allocator>
630 struct buffer
631 {
632 29 buffer(std::size_t n, Allocator al_): al{al_}
633 {
634 29 data = static_cast<T*>(::operator new[](n * sizeof(T), std::nothrow));
635 29 if(data) capacity = n;
636 29 }
637
638 29 ~buffer()
639 {
640 29 if(data) {
641 29 for(; begin_ != end_; ++begin_) allocator_destroy(al, begin());
642 29 ::operator delete[](data);
643 }
644 29 }
645
646 4824835 T* begin() const noexcept { return data + begin_; }
647 2412432 T* end() const noexcept { return data + end_; }
648
649 template<typename... Args>
650 2412403 void emplace_back(Args&&... args)
651 {
652 2412403 BOOST_ASSERT(data && end_ != capacity);
653 2412403 allocator_construct(al, end(), std::forward<Args>(args)...);
654 2412403 ++end_;
655 2412403 }
656
657 2412403 void erase_front() noexcept
658 {
659 2412403 BOOST_ASSERT(data && begin_ != end_);
660 2412403 allocator_destroy(al, begin());
661 2412403 ++begin_;
662 2412403 }
663
664 Allocator al;
665 std::size_t begin_ = 0, end_ = 0;
666 std::size_t capacity = 0;
667 T* data = nullptr;
668 };
669
670 template<typename T>
671 struct nodtor_deleter
672 {
673 using pointer = T*;
674 void operator()(pointer p) noexcept { ::operator delete(p); }
675 };
676
677 template<typename T>
678 struct nodtor_deleter<T[]>
679 {
680 using pointer = T*;
681 16 void operator()(pointer p) noexcept { ::operator delete[](p); }
682 };
683
684 template<typename T>
685 using nodtor_unique_ptr = std::unique_ptr<T, nodtor_deleter<T>>;
686
687 template<typename T>
688 struct type_identity { using type = T; };
689
690 template<typename T>
691 using type_identity_t = typename type_identity<T>::type;
692
693 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
694 template<class R, class T>
695 concept container_compatible_range =
696 std::ranges::input_range<R> &&
697 std::convertible_to<std::ranges::range_reference_t<R>, T>;
698
699 /* Use own from_range_t only if std::from_range_t does not exist.
700 * Technique explained at
701 https://bannalia.blogspot.com/2016/09/compile-time-checking-existence-of.html
702 */
703
704 struct from_range_t{ explicit from_range_t() = default; };
705 struct from_range_t_hook{};
706
707 } /* namespace hub_detail */
708 } /* namespace container */
709 } /* namespace boost */
710
711 namespace std {
712
713 template<> struct hash< ::boost::container::hub_detail::from_range_t_hook>
714 {
715 using from_range_t_type = decltype([] {
716 using namespace ::boost::container::hub_detail;
717 return from_range_t{};
718 }());
719
720 /* make standard happy */
721 std::size_t operator()(
722 const ::boost::container::hub_detail::from_range_t_hook&) const;
723 };
724
725 }
726
727 namespace boost {
728 namespace container {
729
730 /* TODO: this may collide with other same-named entities in different
731 * parts of Boost.Container.
732 */
733 using from_range_t =
734 typename std::hash<hub_detail::from_range_t_hook>::from_range_t_type;
735 inline constexpr from_range_t from_range {};
736
737 namespace hub_detail {
738 #endif
739
740 template<typename InputIterator>
741 using enable_if_is_input_iterator_t =
742 typename std::enable_if<
743 std::is_convertible<
744 typename std::iterator_traits<InputIterator>::iterator_category,
745 std::input_iterator_tag
746 >::value
747 >::type;
748
749 /* std::pmr::polymorphic_allocator::destroy may be marked as deprecated.
750 * C&P from boost/core/allocator_access.hpp.
751 */
752 #if defined(_LIBCPP_SUPPRESS_DEPRECATED_PUSH)
753 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
754 #endif
755 #if defined(_STL_DISABLE_DEPRECATED_WARNING)
756 _STL_DISABLE_DEPRECATED_WARNING
757 #endif
758 #if defined(__clang__) && defined(__has_warning)
759 # if __has_warning("-Wdeprecated-declarations")
760 # pragma clang diagnostic push
761 # pragma clang diagnostic ignored "-Wdeprecated-declarations"
762 # endif
763 #elif defined(_MSC_VER)
764 # pragma warning(push)
765 # pragma warning(disable: 4996)
766 #elif defined(BOOST_GCC) && BOOST_GCC >= 40600
767 # pragma GCC diagnostic push
768 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
769 #endif
770
771 template<typename Allocator, typename Ptr, typename = void>
772 struct allocator_has_destroy: std::false_type {};
773
774 template<typename Allocator, typename Ptr>
775 struct allocator_has_destroy<
776 Allocator, Ptr,
777 decltype((void)std::declval<Allocator&>().destroy(std::declval<Ptr>()))
778 >: std::true_type {};
779
780 #if defined(__clang__) && defined(__has_warning)
781 # if __has_warning("-Wdeprecated-declarations")
782 # pragma clang diagnostic pop
783 # endif
784 #elif defined(_MSC_VER)
785 # pragma warning(pop)
786 #elif defined(BOOST_GCC) && BOOST_GCC >= 40600
787 # pragma GCC diagnostic pop
788 #endif
789 #if defined(_STL_RESTORE_DEPRECATED_WARNING)
790 _STL_RESTORE_DEPRECATED_WARNING
791 #endif
792 #if defined(_LIBCPP_SUPPRESS_DEPRECATED_POP)
793 _LIBCPP_SUPPRESS_DEPRECATED_POP
794 #endif
795
796 template<typename Allocator>
797 struct is_std_allocator: std::false_type {};
798
799 template<typename T>
800 struct is_std_allocator<std::allocator<T>>: std::true_type {};
801
802 template<typename Allocator>
803 struct is_std_pmr_polymorphic_allocator: std::false_type {};
804
805 #ifndef BOOST_NO_CXX17_HDR_MEMORY_RESOURCE
806 template<typename T>
807 struct is_std_pmr_polymorphic_allocator<std::pmr::polymorphic_allocator<T>>:
808 std::true_type {};
809 #endif
810
811 struct if_constexpr_void_else{ void operator()() const {} };
812
813 template<typename F, typename G = if_constexpr_void_else>
814 2 void if_constexpr(std::true_type, F f, G = G{}) { f(); }
815
816 template<typename F, typename G>
817 19 void if_constexpr(std::false_type, F, G g) { g(); }
818
819 template<typename T>
820 2 void copy_assign_if(std::true_type, T& x, const T& y) { x = y; }
821
822 template<typename T>
823 6 void copy_assign_if(std::false_type, T&, const T&) {}
824
825 template<typename T>
826 6 void move_assign_if(std::true_type, T& x, T& y) { x = std::move(y); }
827
828 template<typename T>
829 3 void move_assign_if(std::false_type, T&, T&) {}
830
831 template<typename T>
832 2 void swap_if(std::true_type, T& x, T& y) { using std::swap; swap(x, y); }
833
834 template<typename T>
835 void swap_if(std::false_type, T&, T&) {}
836
837 template<typename Allocator>
838 struct block_typedefs
839 {
840 using pointer = allocator_pointer_t<Allocator>;
841 template<typename Q>
842 using pointer_rebind_t = hub_detail::pointer_rebind_t<pointer, Q>;
843
844 using block_base = hub_detail::block_base<pointer_rebind_t<void>>;
845 using block_base_pointer = pointer_rebind_t<block_base>;
846 using const_block_base_pointer = pointer_rebind_t<const block_base>;
847 using block = hub_detail::block<pointer>;
848 using block_pointer = pointer_rebind_t<block>;
849 using block_allocator = allocator_rebind_t<Allocator,block>;
850 using block_list = hub_detail::block_list<pointer>;
851 };
852
853 } /* namespace container::hub_detail */
854
855 template<typename T, typename Allocator>
856 class hub: empty_value<
857 typename hub_detail::block_typedefs<Allocator>::block_allocator, 0>
858 {
859 static_assert(
860 !std::is_const<T>::value && !std::is_volatile<T>::value &&
861 !std::is_function<T>::value && !std::is_reference<T>::value &&
862 !std::is_void<T>::value,
863 "T must be a cv-unqualified object type");
864 static_assert(
865 std::is_same<T, allocator_value_type_t<Allocator>>::value,
866 "Allocator's value_type must be the same type as T");
867
868 public:
869 using value_type = T;
870 using allocator_type = Allocator;
871 using pointer = allocator_pointer_t<Allocator>;
872 using const_pointer = allocator_const_pointer_t<Allocator>;
873 using reference = T&;
874 using const_reference = const T&;
875 using size_type = allocator_size_type_t<Allocator>;
876 using difference_type = allocator_difference_type_t<Allocator>;
877 using iterator = hub_detail::iterator<pointer>;
878 using const_iterator = hub_detail::iterator<const_pointer>;
879 using reverse_iterator = std::reverse_iterator<iterator>;
880 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
881
882 95 hub() noexcept(noexcept(Allocator())): hub{Allocator()} {}
883
884 318 explicit hub(const Allocator& al_) noexcept:
885 318 allocator_base{empty_init, al_} {}
886
887 20 explicit hub(size_type n, const Allocator& al_ = Allocator()): hub{al_}
888 {
889 20 range_insert_impl(size_type(0), n, [&, this] (T* p, size_type) {
890 allocator_construct(al(), p);
891 });
892 20 }
893
894 21 hub(size_type n, const T& x, const Allocator& al_ = Allocator()): hub{al_}
895 {
896 21 insert(n, x);
897 21 }
898
899 template<
900 typename InputIterator,
901 typename = hub_detail::enable_if_is_input_iterator_t<InputIterator>
902 >
903 156 hub(
904 InputIterator first, InputIterator last,
905 156 const Allocator& al_ = Allocator()): hub{al_}
906 {
907 156 insert(first, last);
908 156 }
909
910 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
911 template<hub_detail::container_compatible_range<T> R>
912 10 hub(from_range_t, R&& rg, const Allocator& al_ = Allocator()): hub{al_}
913 {
914 10 insert_range(std::forward<R>(rg));
915 10 }
916 #endif
917
918 24 hub(const hub& x):
919 48 hub{x, allocator_select_on_container_copy_construction(x.al())} {}
920
921 28 hub(const hub& x, const hub_detail::type_identity_t<Allocator>& al_):
922 28 hub(x.begin(), x.end(), al_) {}
923
924 6 hub(hub&& x) noexcept:
925 10 hub{std::move(x), Allocator(std::move(x.al())), std::true_type{}} {}
926
927 8 hub(hub&& x, const hub_detail::type_identity_t<Allocator>& al_):
928 8 hub{std::move(x), al_, allocator_is_always_equal_t<Allocator>{}} {}
929
930 10 hub(std::initializer_list<T> il, const Allocator& al_ = Allocator()):
931 10 hub{il.begin(), il.end(), al_} {}
932
933 326 ~hub() { reset(); }
934
935 8 hub& operator=(const hub& x)
936 {
937 using pocca =
938 allocator_propagate_on_container_copy_assignment_t<Allocator>;
939
940 8 if(this != &x) {
941 10 if(al() != x.al() && pocca::value) {
942 1 reset();
943 1 hub_detail::copy_assign_if(pocca{}, al(), x.al());
944 1 insert(x.begin(), x.end());
945 }
946 else{
947 7 hub_detail::copy_assign_if(pocca{}, al(), x.al());
948 7 assign(x.begin(), x.end());
949 }
950 }
951 8 return *this;
952 }
953
954 10 hub& operator=(hub&& x)
955 noexcept(
956 allocator_propagate_on_container_move_assignment_t<Allocator>::value ||
957 allocator_is_always_equal_t<Allocator>::value)
958 {
959 10 if(this != &x) {
960 10 move_assign(
961 x,
962 std::integral_constant<
963 bool,
964 allocator_propagate_on_container_move_assignment_t<Allocator>::
965 value ||
966 allocator_is_always_equal_t<Allocator>::value>{});
967 }
968 10 return *this;
969 }
970
971 4 hub& operator=(std::initializer_list<T> il)
972 {
973 4 assign(il);
974 4 return *this;
975 }
976
977 template<
978 typename InputIterator,
979 typename = hub_detail::enable_if_is_input_iterator_t<InputIterator>
980 >
981 19 void assign(InputIterator first, InputIterator last)
982 {
983 19 range_assign_impl(
984 first, last,
985 [this] (T* p, InputIterator it) { allocator_construct(al(), p, *it); },
986 [] (T* p, InputIterator it) { *p = *it; });
987 19 }
988
989 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
990 template<hub_detail::container_compatible_range<T> R>
991 4 void assign_range(R&& rg)
992 {
993 4 range_assign_impl(
994 std::ranges::begin(rg), std::ranges::end(rg),
995 [this] (T* p, auto it) { allocator_construct(al(), p, *it); },
996 [] (T* p, auto it) { *p = *it; });
997 4 }
998 #endif
999
1000 20 void assign(size_type n, const T& x)
1001 {
1002 20 range_assign_impl(
1003 size_type(0), n,
1004 [&, this] (T* p, size_type) { allocator_construct(al(), p, x); },
1005 [&] (T* p, size_type) { *p = x; });
1006 20 }
1007
1008 8 void assign(std::initializer_list<T> il) { assign(il.begin(), il.end()); }
1009
1010 64 allocator_type get_allocator() const noexcept { return al(); }
1011
1012 2560 iterator begin() noexcept { return ++end(); }
1013 2656 const_iterator begin() const noexcept { return ++end(); }
1014 19443 iterator end() noexcept { return {blist.header(), 0}; }
1015 3427 const_iterator end() const noexcept { return {blist.header(), 0}; }
1016 8 reverse_iterator rbegin() noexcept { return reverse_iterator{end()}; }
1017 12 const_reverse_iterator rbegin() const noexcept
1018 12 { return const_reverse_iterator{end()}; }
1019 8 reverse_iterator rend() noexcept { return reverse_iterator{begin()}; }
1020 12 const_reverse_iterator rend() const noexcept
1021 12 { return const_reverse_iterator{begin()}; }
1022 1074 const_iterator cbegin() const noexcept { return begin(); }
1023 1201 const_iterator cend() const noexcept { return end(); }
1024 4 const_reverse_iterator crbegin() const noexcept { return rbegin(); }
1025 4 const_reverse_iterator crend() const noexcept { return rend(); }
1026
1027 48 bool empty() const noexcept { return size_ == 0; }
1028 1322 size_type size() const noexcept { return size_; }
1029
1030 32 size_type max_size() const noexcept
1031 {
1032 std::size_t
1033 32 bs = (std::size_t)allocator_max_size(al()) * sizeof(block),
1034 50 vs = (std::size_t)allocator_max_size(Allocator(al())) * sizeof(T);
1035 return
1036 32 (size_type)((std::min)(bs, vs) / (sizeof(block) + sizeof(T) * N) * N);
1037 }
1038
1039 418 size_type capacity() const noexcept { return num_blocks * N; }
1040
1041 size_type memory() const noexcept // TODO: remove
1042 {
1043 return num_blocks * (sizeof(block) + sizeof(T) * N);
1044 }
1045
1046 20 void reserve(size_type n)
1047 {
1048 20 if(n > max_size()) {
1049 8 BOOST_THROW_EXCEPTION(
1050 std::length_error("Requested capacity greater than max_size()"));
1051 }
1052 188 while(capacity() < n) (void)create_new_available_block();
1053 16 }
1054
1055 8 void shrink_to_fit()
1056 {
1057 8 compact();
1058 8 trim_capacity();
1059 8 }
1060
1061 14 void trim_capacity() noexcept { trim_capacity(0); }
1062
1063 20 void trim_capacity(size_type n) noexcept
1064 {
1065 /* Linear on # available blocks, std::hive is linear on # _reserved_
1066 * blocks.
1067 */
1068 26 for(auto pbb = blist.header()->next_available;
1069 231 capacity() > n && pbb != blist.header(); ) {
1070 178 auto pb = static_cast_block_pointer(pbb);
1071 178 pbb = pbb-> next_available;
1072 178 if(pb->mask == 0) {
1073 174 blist.unlink_available(pb);
1074 146 delete_block(pb);
1075 146 --num_blocks;
1076 }
1077 }
1078 20 }
1079
1080 template<typename... Args>
1081 BOOST_FORCEINLINE iterator emplace(Args&&... args)
1082 {
1083 int n;
1084 9934242 auto pb = retrieve_available_block(n);
1085 19870120 allocator_construct(
1086 9934651 al(), boost::to_address(pb->data() + n), std::forward<Args>(args)...);
1087 9933424 auto mask_plus_one = (pb->mask |= pb->mask + 1) + 1;
1088 9934651 if(BOOST_UNLIKELY(mask_plus_one <= 2)) {
1089 /* pb->mask == 0 (impossible), 1 or full */
1090 310477 if(mask_plus_one == 0) blist.unlink_available(pb);
1091 155268 else /* pb->mask == 1 */ blist.link_at_back(pb);
1092 }
1093 9933424 ++size_;
1094 9934651 return {pb, n};
1095 }
1096
1097 template<typename... Args>
1098 BOOST_FORCEINLINE iterator emplace_hint(const_iterator, Args&&... args)
1099 {
1100 14 return emplace(std::forward<Args>(args)...);
1101 }
1102
1103 1604 BOOST_FORCEINLINE iterator insert(const T& x) { return emplace(x); }
1104 BOOST_FORCEINLINE iterator insert(const_iterator, const T& x)
1105 4 { return emplace(x); }
1106 19866036 BOOST_FORCEINLINE iterator insert(T&& x) { return emplace(std::move(x)); }
1107 BOOST_FORCEINLINE iterator insert(const_iterator, T&& x)
1108 8 { return emplace(std::move(x)); }
1109
1110 4 void insert(std::initializer_list<T> il) { insert(il.begin(), il.end()); }
1111
1112 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
1113 template<hub_detail::container_compatible_range<T> R>
1114 18 void insert_range(R&& rg)
1115 {
1116 18 range_insert_impl(
1117 std::ranges::begin(rg), std::ranges::end(rg),
1118 [this] (T* p, auto it) { allocator_construct(al(), p, *it); });
1119 18 }
1120 #endif
1121
1122 template<
1123 typename InputIterator,
1124 typename = hub_detail::enable_if_is_input_iterator_t<InputIterator>
1125 >
1126 191 void insert(InputIterator first, InputIterator last)
1127 {
1128 191 range_insert_impl(first, last, [this] (T* p, InputIterator it) {
1129 allocator_construct(al(), p, *it);
1130 });
1131 191 }
1132
1133 21 void insert(size_type n, const T& x)
1134 {
1135 21 range_insert_impl(size_type(0), n, [&, this] (T* p, size_type) {
1136 10 allocator_construct(al(), p, x);
1137 });
1138 21 }
1139
1140 BOOST_FORCEINLINE iterator erase(const_iterator pos)
1141 {
1142 1414 auto pbb = pos.pbb;
1143 3022 auto n = pos.n;
1144 ++pos;
1145 536 erase_impl(pbb, n);
1146 3558 return {pos.pbb, pos.n};
1147 }
1148
1149 BOOST_FORCEINLINE void erase_void(const_iterator pos)
1150 {
1151 erase_impl(pos.pbb, pos.n);
1152 }
1153
1154 1233 iterator erase(const_iterator first, const_iterator last)
1155 {
1156 3245 for(auto pbb = first.pbb; first != last; ) {
1157 2157 first = erase(first);
1158 2157 if(first.pbb != pbb) break;
1159 }
1160 1233 auto pbb = first.pbb;
1161 1440 if(pbb != last.pbb){
1162 do {
1163 29 auto pb = static_cast_block_pointer(pbb);
1164 29 pbb = pb->next;
1165 29 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb, block);
1166 29 size_ -= destroy_all_in_nonempty_block(pb);
1167 5 blist.unlink(pb);
1168 32 if(BOOST_UNLIKELY(pb->mask == full)) blist.link_available_at_front(pb);
1169 29 pb->mask = 0;
1170 34 } while(pbb != last.pbb);
1171 11 first = {pbb};
1172 }
1173 1243 while(first != last) first = erase(first);
1174 1440 return {last.pbb, last.n};
1175 }
1176
1177 21 void swap(hub& x)
1178 noexcept(
1179 allocator_propagate_on_container_swap_t<Allocator>::value ||
1180 allocator_is_always_equal_t<Allocator>::value)
1181 {
1182 using pocs = allocator_propagate_on_container_swap_t<Allocator>;
1183
1184 21 hub_detail::if_constexpr(pocs{}, [&, this]{
1185 hub_detail::swap_if(pocs{}, al(), x.al());
1186 },
1187 [&, this]{ /* else */
1188 BOOST_ASSERT(al() == x.al());
1189 (void)this;
1190 });
1191 21 std::swap(blist, x.blist);
1192 21 std::swap(num_blocks, x.num_blocks);
1193 21 std::swap(size_, x.size_);
1194 21 }
1195
1196 8 void clear() noexcept { erase(begin(), end()); }
1197
1198 12 void splice(hub& x)
1199 {
1200 12 BOOST_ASSERT(this != &x);
1201 20 BOOST_ASSERT(al() == x.al());
1202 90 for(auto pbb = x.blist.header()->next; pbb != x.blist.header(); ) {
1203 60 auto pb = static_cast_block_pointer(pbb);
1204 60 pbb = pbb->next;
1205 60 if(pb->mask != full) {
1206 18 x.blist.unlink_available(pb);
1207 21 blist.link_available_at_front(pb);
1208 }
1209 12 x.blist.unlink(pb);
1210 60 blist.link_at_back(pb);
1211 60 --x.num_blocks;
1212 60 ++num_blocks;
1213 60 auto s = core::popcount(pb->mask);
1214 60 x.size_ -= s;
1215 60 size_ += s;
1216 }
1217 12 }
1218
1219 6 void splice(hub&& x) { splice(x); }
1220
1221 template<typename BinaryPredicate = std::equal_to<T>>
1222 6 size_type unique(BinaryPredicate pred = BinaryPredicate())
1223 {
1224 6 auto s = size_;
1225 1206 for(auto first = cbegin(), last = cend(); first != last; ) {
1226 1200 auto next = std::next(first);
1227 1200 first = erase(
1228 next,
1229 std::find_if_not(next, last, [&] (const T& x) {
1230 return pred(x, *first);
1231 }));
1232 }
1233 6 return (size_type)(s - size_);
1234 }
1235
1236 template<typename Compare = std::less<T>>
1237 47 void sort(Compare comp = Compare())
1238 {
1239 /* transfer_sort is usually the fastest, but it consumes the most
1240 * auxiliary memory when sizeof(T) > sizeof(sort_proxy), so we restrict
1241 * its usage to the case sizeof(T) <= sizeof(sort_proxy).
1242 * compact_sort uses the least amount of auxiliary memory (by far), and
1243 * it's also faster than proxy_sort when the auxiliary memory of the latter
1244 * exceeds some threshold seemingly related to the size of the L2 cache;
1245 * we conventionally set the threshold to 2MB for lack of a more precise
1246 * estimation mechanism.
1247 * TODO: In 32-bit mode, the threshold policy is not so clear-cut and the
1248 * cost of moving elements around seems to play a role.
1249 */
1250 BOOST_IF_CONSTEXPR(sizeof(T) <= sizeof(sort_proxy)) {
1251 29 if(transfer_sort(comp)) return;
1252 }
1253 else{
1254 static constexpr std::size_t memory_threshold = 2 * 1024 * 1024;
1255 18 if((std::size_t)size_ * sizeof(sort_proxy) <= memory_threshold) {
1256 10 if(proxy_sort(comp)) return;
1257 }
1258 }
1259 8 compact_sort(comp);
1260 }
1261
1262 1600 iterator get_iterator(const_pointer p) noexcept /* noexcept? */
1263 {
1264 std::less<const T*> less;
1265 4992 for(auto pbb = blist.next; pbb != blist.header(); pbb = pbb-> next) {
1266 3328 auto pb = static_cast_block_pointer(pbb);
1267 6464 if(!less(boost::to_address(p), boost::to_address(pb->data())) &&
1268 4800 less(boost::to_address(p), boost::to_address(pb->data() + N))) {
1269 2000 return {pb, (int)(p - pb->data())};
1270 }
1271 }
1272 return end(); /* shouldn't assert? */
1273 }
1274
1275 800 const_iterator get_iterator(const_pointer p) const noexcept /* noexcept? */
1276 {
1277 800 return const_cast<hub*>(this)->get_iterator(p);
1278 }
1279
1280 template<typename F>
1281 410 void visit(iterator first, iterator last, F f)
1282 {
1283 410 visit_while(first, last, [&] (value_type& x) {
1284 f(x);
1285 return true;
1286 });
1287 410 }
1288
1289 template<typename F>
1290 344 void visit(const_iterator first, const_iterator last, F f) const
1291 {
1292 344 visit_while(first, last, [&] (const value_type& x) {
1293 f(x);
1294 return true;
1295 });
1296 344 }
1297
1298 template<typename F>
1299 2138 iterator visit_while(iterator first, iterator last, F f)
1300 {
1301 56860 for(auto pbb = first.pbb; first != last; ) {
1302 56152 if(!f(*first)) return first;
1303 ++first;
1304 69082 if(first.pbb != pbb) break;
1305 }
1306 2110 if(first.pbb != last.pbb) {
1307 2132 first = visit_while_impl(first.pbb, last.pbb, f);
1308 1784 if(first.pbb != last.pbb) return first;
1309 }
1310 12818 for(; first != last; ++first) if(!f(*first)) return first;
1311 770 return first;
1312 }
1313
1314 template<typename F>
1315 1036 const_iterator visit_while(
1316 const_iterator first, const_iterator last, F f) const
1317 {
1318 1295 auto it =const_cast<hub*>(this)->visit_while(
1319 1295 iterator{first.pbb, first.n}, iterator{last.pbb, last.n},
1320 [&] (const value_type& x) { return f(x); });
1321 1036 return {it.pbb, it.n};
1322 }
1323
1324 template<typename F>
1325 70 void visit_all(F f)
1326 {
1327 70 visit(begin(), end(), std::ref(f));
1328 70 }
1329
1330 template<typename F>
1331 4 void visit_all(F f) const
1332 {
1333 4 visit(begin(), end(), std::ref(f));
1334 4 }
1335
1336 template<typename F>
1337 4 iterator visit_all_while(F f)
1338 {
1339 4 return visit_while(begin(), end(), std::ref(f));
1340 }
1341
1342 template<typename F>
1343 4 const_iterator visit_all_while(F f) const
1344 {
1345 4 return visit_while(begin(), end(), std::ref(f));
1346 }
1347
1348 private:
1349 template<typename U, typename A, typename P>
1350 friend typename hub<U, A>::size_type erase_if(hub<U, A>&, P);
1351
1352 using block_typedefs = hub_detail::block_typedefs<Allocator>;
1353 using block_base = typename block_typedefs::block_base;
1354 using block_base_pointer = typename block_typedefs::block_base_pointer;
1355 using const_block_base_pointer =
1356 typename block_typedefs::const_block_base_pointer;
1357 using block = typename block_typedefs::block;
1358 using block_pointer = typename block_typedefs::block_pointer;
1359 using block_allocator = typename block_typedefs::block_allocator;
1360 using block_list = typename block_typedefs::block_list;
1361 using allocator_base = empty_value<block_allocator, 0>;
1362 using mask_type = typename block_base::mask_type;
1363
1364 static constexpr int N = block_base::N;
1365 static constexpr mask_type full = block_base::full;
1366
1367 20550957 block_allocator& al() noexcept { return allocator_base::get(); }
1368 166 const block_allocator& al() const noexcept { return allocator_base::get(); }
1369
1370 struct reset_on_exit
1371 {
1372 5 ~reset_on_exit() { x.reset(); }
1373
1374 hub& x;
1375 };
1376
1377 8 hub(
1378 hub&& x, const Allocator& al_, std::true_type /* equal allocs */) noexcept:
1379 8 allocator_base{empty_init, al_}, blist{std::move(x.blist)},
1380 8 num_blocks{x.num_blocks}, size_{x.size_}
1381 {
1382 8 x.num_blocks = 0;
1383 8 x.size_ = 0;
1384 8 }
1385
1386 6 hub(
1387 hub&& x, const Allocator& al_, std::false_type /* maybe unequal allocs */):
1388 6 hub{al_}
1389 {
1390 6 if(al() == x.al()) {
1391 2 blist = std::move(x.blist);
1392 2 num_blocks = x.num_blocks;
1393 2 size_ = x.size_;
1394 2 x.num_blocks = 0;
1395 2 x.size_ = 0;
1396 }
1397 else {
1398 4 reset_on_exit on_exit{x}; (void)on_exit;
1399 4 range_insert_impl(x.begin(), x.end(), [this] (T* p, iterator it) {
1400 allocator_construct(al(), p, std::move(*it));
1401 });
1402 4 }
1403 6 }
1404
1405 9 void move_assign(hub& x, std::true_type /* transfer structure */)
1406 {
1407 using pocma =
1408 allocator_propagate_on_container_move_assignment_t<Allocator>;
1409
1410 9 reset();
1411 9 hub_detail::move_assign_if(pocma{}, al(), x.al());
1412 9 blist = std::move(x.blist);
1413 9 num_blocks = x.num_blocks;
1414 9 size_ = x.size_;
1415 9 x.num_blocks = 0;
1416 9 x.size_ = 0;
1417 9 }
1418
1419 3 void move_assign(hub& x, std::false_type /* maybe move data */)
1420 {
1421 3 if(al() == x.al()) {
1422 2 move_assign(x, std::true_type{});
1423 }
1424 else {
1425 1 reset_on_exit on_exit{x}; (void)on_exit;
1426 1 range_assign_impl(
1427 x.begin(), x.end(),
1428 400 [this] (T* p, iterator it)
1429 200 { allocator_construct(al(), p, std::move(*it)); },
1430 400 [] (T* p, iterator it)
1431 200 { *p = std::move(*it); });
1432 1 }
1433 3 }
1434
1435 static block_pointer
1436 15624323 static_cast_block_pointer(block_base_pointer pbb) noexcept
1437 {
1438 15624323 return block_list::static_cast_block_pointer(pbb);
1439 }
1440
1441 156300 block_pointer create_new_available_block()
1442 {
1443 156300 auto pb = allocator_allocate(al(), 1);
1444 156300 pb->mask = 0;
1445 BOOST_TRY {
1446 156300 allocator_rebind_t<Allocator, value_type> val(al());
1447 156492 pb->data_ = allocator_allocate(val, N);
1448 }
1449 BOOST_CATCH(...) {
1450 allocator_deallocate(al(), pb, 1);
1451 BOOST_RETHROW;
1452 }
1453 BOOST_CATCH_END
1454 156300 blist.link_available_at_back(pb);
1455 156300 ++num_blocks;
1456 156300 return pb;
1457 }
1458
1459 156300 void delete_block(block_pointer pb) noexcept
1460 {
1461 156300 allocator_rebind_t<Allocator, value_type> val(al());
1462 156300 allocator_deallocate(val, pb->data(), N);
1463 156300 allocator_deallocate(al(), pb, 1);
1464 156300 }
1465
1466 BOOST_FORCEINLINE block_pointer retrieve_available_block(int& n)
1467 {
1468 9936138 if(BOOST_LIKELY(blist.next_available != blist.header())){
1469 9779852 auto pb = static_cast_block_pointer(blist.next_available);
1470 9779442 n = hub_detail::unchecked_countr_one(pb->mask);
1471 9779032 return pb;
1472 }
1473 else {
1474 156128 n = 0;
1475 156128 return create_new_available_block();
1476 }
1477 }
1478
1479 59422 size_type destroy_all_in_nonempty_block(block_pointer pb) noexcept
1480 {
1481 59422 BOOST_ASSERT(pb->mask != 0);
1482 59494 return destroy_all_in_nonempty_block(pb, std::integral_constant<bool,
1483 std::is_trivially_destructible<T>::value &&
1484 ( hub_detail::is_std_allocator<block_allocator>::value ||
1485 hub_detail::is_std_pmr_polymorphic_allocator<block_allocator>::value ||
1486 59422 !hub_detail::allocator_has_destroy<block_allocator, T*>::value )>{});
1487 }
1488
1489 59180 size_type destroy_all_in_nonempty_block(
1490 block_pointer pb, std::true_type /* trivial destruction */) noexcept
1491 {
1492 59180 return (size_type)core::popcount(pb->mask);
1493 }
1494
1495 242 size_type destroy_all_in_nonempty_block(
1496 block_pointer pb, std::false_type /* use allocator_destroy */) noexcept
1497 {
1498 242 size_type s = 0;
1499 242 auto mask = pb->mask;
1500 do {
1501 5181 auto n = hub_detail::unchecked_countr_zero(mask);
1502 5181 allocator_destroy(al(), boost::to_address(pb->data() + n));
1503 5181 ++s;
1504 5181 mask &= mask - 1;
1505 5181 } while(mask);
1506 242 return s;
1507 }
1508
1509 56876 size_type destroy_all_in_full_block(block_pointer pb) noexcept
1510 {
1511 56876 BOOST_ASSERT(pb->mask == full);
1512 3696940 for(int n = 0; n < N; ++n) {
1513 3651328 allocator_destroy(al(), boost::to_address(pb->data() + n));
1514 }
1515 56876 return (size_type)N;
1516 }
1517
1518 341 void reset() noexcept
1519 {
1520 99807 for(auto pbb = blist.next_available; pbb != blist.header(); ) {
1521 99278 auto pb = static_cast_block_pointer(pbb);
1522 99278 pbb = pb->next_available;
1523 BOOST_IF_CONSTEXPR(!std::is_trivially_destructible<T>::value) {
1524 40079 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb, block);
1525 }
1526 99278 if(pb->mask != 0) {
1527 59393 destroy_all_in_nonempty_block(pb);
1528 67 blist.unlink(pb);
1529 }
1530 99278 delete_block(pb);
1531 }
1532 /* full blocks remaining */
1533 57417 for(auto pbb = blist.next; pbb != blist.header(); ) {
1534 56876 BOOST_ASSERT(pbb->mask == full);
1535 56876 auto pb = static_cast_block_pointer(pbb);
1536 56876 pbb = pb->next;
1537 BOOST_IF_CONSTEXPR(!std::is_trivially_destructible<T>::value) {
1538 37587 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb, block);
1539 }
1540 56876 destroy_all_in_full_block(pb);
1541 56876 delete_block(pb);
1542 }
1543 341 blist.reset();
1544 341 num_blocks = 0;
1545 341 size_ = 0;
1546 341 }
1547
1548 BOOST_FORCEINLINE void erase_impl(block_base_pointer pbb, int n) noexcept
1549 {
1550 5119854 auto pb = static_cast_block_pointer(pbb);
1551 5121128 allocator_destroy(al(), boost::to_address(pb->data() + n));
1552 5119878 if(BOOST_UNLIKELY(pb->mask == full)) blist.link_available_at_front(pb);
1553 5117943 pb->mask &= ~((mask_type)(1) << n);
1554 5119860 if(BOOST_UNLIKELY(pb->mask == 0)) blist.unlink(pb);
1555 5119854 --size_;
1556 5119854 }
1557
1558 template<typename Incrementable, typename Sentinel, typename Construct>
1559 287 void range_insert_impl(
1560 Incrementable first, Sentinel last, Construct construct)
1561 {
1562 923 while(first != last) {
1563 int n;
1564 760 auto pb = retrieve_available_block(n);
1565 for(; ; ) {
1566 58548 construct(boost::to_address(pb->data() + n), first++);
1567 43758 ++size_;
1568 43915 if(BOOST_UNLIKELY(pb->mask == 0)) blist.link_at_back(pb);
1569 51153 pb->mask |= pb->mask +1;
1570 43758 if(pb->mask == full){
1571 636 blist.unlink_available(pb);
1572 636 break;
1573 }
1574 43122 else if(first == last) return;
1575 42839 n = hub_detail::unchecked_countr_one(pb->mask);
1576 }
1577 }
1578 }
1579
1580 template<
1581 typename Incrementable, typename Sentinel,
1582 typename Construct, typename Insert
1583 >
1584 44 void range_assign_impl(
1585 Incrementable first, Sentinel last, Construct construct, Insert insert)
1586 {
1587 44 auto pbb = blist.next;
1588 44 int n = -1;
1589 44 if(first != last) {
1590 /* consume active blocks */
1591 92 for(; pbb != blist.header(); pbb = pbb->next) {
1592 40 auto pb = static_cast_block_pointer(pbb);
1593 40 n = 0;
1594 2173 for(mask_type bit = 1; bit; bit <<= 1, ++n) {
1595 2140 if(pb->mask & bit) { /* full slot */
1596 2120 insert(boost::to_address(pb->data() + n), first++);
1597 }
1598 else { /* empty slot */
1599 662 construct(boost::to_address(pb->data() + n), first++);
1600 460 ++size_;
1601 460 pb->mask |= bit;
1602 463 if(pb->mask == full) blist.unlink_available(pb);
1603 }
1604 2140 if(first == last) goto exit;
1605 }
1606 }
1607 40 exit: ;
1608 }
1609 44 if(first != last) {
1610 /* all active blocks consumed, keep inserting */
1611 33 range_insert_impl(first, last, construct);
1612 }
1613 else{
1614 /* erase remaining original elements */
1615 20 auto it = (n == -1)? const_iterator{pbb}: ++const_iterator{pbb, n};
1616 11 erase(it, cend());
1617 }
1618 44 }
1619
1620 template<typename Compare>
1621 29 bool transfer_sort(Compare comp)
1622 {
1623 /* transfer to a buffer, sort and transfer back */
1624 53 hub_detail::buffer<T,Allocator> buf(size_, al());
1625 29 if(!buf.data) return false;
1626
1627 39 visit_all([&] (value_type& x) { buf.emplace_back(std::move(x)); });
1628 29 std::sort(buf.begin(), buf.end(), comp);
1629 29 visit_all([&] (value_type& x) {
1630 10 x = std::move(*buf.begin());
1631 10 buf.erase_front();
1632 });
1633 29 return true;
1634 29 }
1635
1636 struct sort_proxy
1637 {
1638 T* p;
1639 size_type n;
1640 };
1641
1642 template<typename Compare>
1643 10 bool proxy_sort(Compare comp)
1644 {
1645 /* sort an array of (pointer, index) pairs and relocate according to it */
1646 10 if(size_ > 1) {
1647 8 hub_detail::nodtor_unique_ptr<sort_proxy[]> p
1648 {static_cast<sort_proxy*>(
1649 8 ::operator new[](size_ * sizeof(sort_proxy), std::nothrow))};
1650 8 if(!p) return false;
1651
1652 8 size_type i = 0;
1653 8 visit_all([&] (value_type& x) {
1654 p[i] = {std::addressof(x), i};
1655 ++i;
1656 });
1657
1658 8 std::sort(
1659 8 p.get(), p.get() + size_,
1660 [&] (const sort_proxy& x, const sort_proxy& y) {
1661 return comp(const_cast<const T&>(*x.p), const_cast<const T&>(*y.p));
1662 });
1663
1664 8 i = 0;
1665 7888 for(; i < size_; ++i) {
1666 7880 if(p[i].n != i) {
1667 53 T x = std::move(*(p[i].p));
1668 53 auto j = i;
1669 do {
1670 7819 auto k = p[j].n;
1671 7819 *(p[j].p) = std::move(*p[k].p);
1672 7819 p[j].n = j;
1673 7819 j = k;
1674 7819 } while(p[j].n != i);
1675 53 *(p[j].p) = std::move(x);
1676 53 p[j].n = j;
1677 53 }
1678 }
1679 8 }
1680 10 return true;
1681 }
1682
1683 template<typename Compare>
1684 8 void compact_sort(Compare comp)
1685 {
1686 /* compact elements and build an array of pointers to data chunks of N */
1687 using sort_iterator = hub_detail::sort_iterator<T, N>;
1688
1689 8 if(size_ > 1) {
1690 8 std::size_t n = (std::size_t)((size_ + N - 1) / N);
1691 8 hub_detail::nodtor_unique_ptr<T*[]> p
1692 8 {static_cast<T**>(::operator new[](n * sizeof(T*)))};
1693 8 std::size_t i = 0;
1694 8 compact([&] (block_pointer pb) {
1695 p[i++] = boost::to_address(pb->data());
1696 });
1697 8 BOOST_ASSERT(i == n);
1698
1699 8 std::sort(
1700 sort_iterator{p.get(), 0}, sort_iterator{p.get(), size_}, comp);
1701 8 }
1702 8 }
1703
1704 36 void compact() { compact([] (block_pointer) {}); }
1705
1706 template<typename Track>
1707 16 void compact(Track track)
1708 {
1709 29075 for(auto pbbx = blist.next; pbbx != blist.header(); ) {
1710 29070 auto pbx = static_cast_block_pointer(pbbx);
1711 29070 auto pbby = pbbx->next;
1712 29070 if(pbx->mask != full) {
1713 do{
1714 57734 if(pbby->mask == full) {
1715 do {
1716 8470 track(static_cast_block_pointer(pbby));
1717 8470 pbby = pbby->next;
1718 8470 } while(pbby->mask == full);
1719 blist.unlink(pbx);
1720 14 blist.link_before(pbx, static_cast_block_pointer(pbby));
1721 }
1722 57735 if(pbby == blist.header()) {
1723 16 compact(pbx);
1724 16 track(pbx);
1725 16 return;
1726 }
1727 else{
1728 57718 auto pby = static_cast_block_pointer(pbby);
1729 57718 compact(pbx,pby);
1730 57718 if(pby->mask == 0) {
1731 39838 pbby = pby->next;
1732 blist.unlink(pby);
1733 }
1734 }
1735 57718 }while(pbx->mask != full);
1736 18774 blist.unlink_available(pbx);
1737 }
1738 29054 track(pbx);
1739 29051 pbbx = pbby;
1740 }
1741 }
1742
1743 57718 void compact(block_pointer pbx, block_pointer pby)
1744 {
1745 57718 auto cx = core::popcount(pbx->mask),
1746 57718 cy = core::popcount(pby->mask);
1747 57718 if(cx < cy) {
1748 17940 std::swap(cx, cy);
1749 17940 swap_payload(*pbx, *pby);
1750 }
1751 57718 auto c = (std::min)(N - cx, cy);
1752 648532 while(c--) {
1753 590814 auto n = hub_detail::unchecked_countr_one(pbx->mask);
1754 590814 auto m = N - 1 - hub_detail::unchecked_countl_zero(pby->mask);
1755 590814 allocator_construct(
1756 590814 al(), boost::to_address(pbx->data() + n), std::move(pby->data()[m]));
1757 590814 allocator_destroy(al(), boost::to_address(pby->data() + m));
1758 590814 pbx->mask |= pbx->mask + 1;
1759 590814 pby->mask &= ~((mask_type)(1) << m);
1760 }
1761 57718 }
1762
1763 16 void compact(block_pointer pb)
1764 {
1765 4 for(; ; ) {
1766 20 auto n = hub_detail::unchecked_countr_one(pb->mask);
1767 20 auto m = N - 1 - hub_detail::unchecked_countl_zero(pb->mask);
1768 20 if(n > m) return;
1769 4 allocator_construct(
1770 4 al(), boost::to_address(pb->data() + n), std::move(pb->data()[m]));
1771 4 allocator_destroy(al(), boost::to_address(pb->data() + m));
1772 4 pb->mask |= pb->mask + 1;
1773 4 pb->mask &= ~((mask_type)(1) << m);
1774 }
1775 }
1776
1777 template<typename F>
1778 1436 iterator visit_while_impl(
1779 block_base_pointer pbb, block_base_pointer last_pbb, F&& f)
1780 {
1781 1436 BOOST_ASSERT(pbb != last_pbb);
1782 1436 auto pb = static_cast_block_pointer(pbb);
1783 1436 auto mask = pb->mask;
1784 1436 auto n = hub_detail::unchecked_countr_zero(mask);
1785 1436 auto pd = pb->data();
1786 do {
1787 157098 pbb = pb->next;
1788 157098 auto next_mask = pbb->mask;
1789 157098 auto next_n = hub_detail::unchecked_countr_zero(next_mask);
1790 157506 auto next_pd = static_cast_block_pointer(pbb)->data();
1791 157506 BOOST_CONTAINER_HUB_PREFETCH(next_pd + next_n);
1792 157098 BOOST_CONTAINER_HUB_PREFETCH(pbb->next);
1793 for(; ; ) {
1794 4902006 if(!f(pd[n])) return {pb, n};
1795 4885302 mask &= mask - 1;
1796 4885302 if(!mask) break;
1797 4729132 n = hub_detail::unchecked_countr_zero(mask);
1798 }
1799 156170 pb = static_cast_block_pointer(pbb);
1800 156170 mask = next_mask;
1801 156170 n = next_n;
1802 155994 pd = next_pd;
1803 156170 } while(pb != last_pbb);
1804 508 return {last_pbb};
1805 }
1806
1807 block_list blist;
1808 size_type num_blocks = 0;
1809 size_type size_ = 0;
1810 };
1811
1812 #if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
1813 template<
1814 typename InputIterator,
1815 typename Allocator = std::allocator<
1816 typename std::iterator_traits<InputIterator>::value_type>
1817 >
1818 hub(InputIterator, InputIterator, Allocator = Allocator())
1819 -> hub<
1820 typename std::iterator_traits<InputIterator>::value_type, Allocator>;
1821
1822 #if !defined(BOOST_CONTAINER_HUB_NO_RANGES)
1823 template<
1824 std::ranges::input_range R,
1825 typename Allocator = std::allocator<std::ranges::range_value_t<R>>
1826 >
1827 hub(from_range_t, R&&, Allocator = Allocator())
1828 -> hub<std::ranges::range_value_t<R>, Allocator>;
1829 #endif
1830 #endif
1831
1832 template<typename T, typename Allocator>
1833 8 void swap(hub<T, Allocator>& x, hub<T, Allocator>& y)
1834 noexcept(noexcept(x.swap(y)))
1835 {
1836 8 x.swap(y);
1837 8 }
1838
1839 template<typename T, typename Allocator, typename Predicate>
1840 typename hub<T, Allocator>::size_type
1841 54 erase_if(hub<T, Allocator>& x, Predicate pred)
1842 {
1843 using hub_container = hub<T, Allocator>;
1844 using size_type = typename hub_container::size_type;
1845 using block = typename hub_container::block;
1846
1847 54 auto s = x.size_;
1848 155332 for(auto pbb = x.blist.next; pbb != x.blist.header(); ) {
1849 155262 auto pb = x.static_cast_block_pointer(pbb);
1850 155262 pbb = pb->next;
1851 155262 BOOST_CONTAINER_HUB_PREFETCH_BLOCK(pbb, block);
1852 155262 auto mask = pb->mask;
1853 do {
1854 9935010 auto n = hub_detail::unchecked_countr_zero(mask);
1855 9935510 if(pred(pb->data()[n])) x.erase_impl(pb, n);
1856 9935010 mask &= mask - 1;
1857 9935010 } while(mask);
1858 }
1859 54 return (size_type)(s - x.size_);
1860 }
1861
1862 template<typename T, typename Allocator, typename U = T>
1863 typename hub<T, Allocator>::size_type
1864 8 erase(hub<T, Allocator>& x, const U& value)
1865 {
1866 808 return erase_if(x, [&](const T& v) -> bool { return v == value; });
1867 }
1868
1869 } /* namespace container */
1870
1871 } /* namespace boost */
1872
1873 #if defined(BOOST_MSVC)
1874 #pragma warning(pop) /* C4714 */
1875 #endif
1876
1877 #endif
1878