IDA C++ SDK 9.2
Loading...
Searching...
No Matches
qtree.hpp
Go to the documentation of this file.
1#pragma once
2
3// A C++17 red-black tree providing building blocks for qset and qmap. Intended
4// for C++17. This header must not be included under non-default packing.
5//
6// Hex-Rays uses std::map and std::set in many places, but we cannot expose
7// these types in the Hex-Rays SDK, because the ABI of the STL containers is not
8// stable across compiler versions and standard library implementations. As a
9// result, past SDKs have treated std::map and std::set as opaque types, which
10// clients could only manipulate through helper functions provided by the SDK
11// to handle all of the ordinary operations. This is cumbersome for both
12// Hex-Rays internally as well as for SDK clients.
13//
14// This implementation provides similar functionality without exposing any
15// non-empty type defined in the STL, to allow our SDK to expose map and set
16// types directly, without helper functions.
17//
18// There are a few noteworthy aspects to this implementation:
19//
20// 1. Header-only implementation: SDK clients have access to our entire
21// implementation, without needing to link against SDK exports. When an SDK
22// client wants to perform a lookup, or an insertion, or a removal, their
23// compiler generates that code directly based on the code below. Thus,
24// their compiler needs to produce identical structure layouts to ours.
25//
26// 2. ABI: continuing the previous point, we must be careful to avoid any
27// implementation tricks that could lead to different structure layouts.
28// STL implementations commonly use empty base optimization to avoid storing
29// comparator and allocator instances in each container object, but as of
30// C++17, empty base optimization is not guaranteed to be applied in all
31// cases. Taking it further, we even eschew inheritance entirely due to
32// possible variations in structure layout: `qmap` composes a `qtree` rather
33// than inheriting from it, and forwards the public API to the composed
34// `qtree` instance. We also provide `qpair` as a replacement for
35// `std::pair`, so we own every non-empty type used herein.
36//
37// The concrete node layout is intentionally standard-layout and relies only
38// on platform ABI rules; there are no compiler-specific extensions or
39// inheritance/EBO tricks. This makes the layout stable across MS/Itanium
40// ABIs and compilers that implement them.
41//
42// The most important goal in this interface is that the data layout be
43// stable. Even if we were to identify bugs in the red-black algorithms,
44// we could fix them later without breaking ABI, as long as the data layout
45// is designed to be stable from the start. This goal seems very achievable,
46// given that MSVC has had a more-or-less identical layout for std::map and
47// std::set ever since the very first implementation. Changes to these
48// classes over the years have mostly been about adding new member
49// functions and improving performance, rather than changing the underlying
50// data layout.
51//
52// 3. Comparator and allocator: we require that both the comparator and
53// allocator types be empty and stateless. This way, Hex-Rays cannot rely on
54// hidden state that would not be visible to SDK clients (or would require
55// them to link against SDK exports to access comparator state).
56//
57// 4. Memory allocation: without overriding the allocator template parameter,
58// an ordinary implementation of set and map would ultimately route all
59// allocations and deallocations through operator new and delete. It would
60// be very bad if client code were using different underlying heaps from
61// Hex-Rays. Our template AllocPolicy parameter routes all allocations
62// through qalloc_shim.hpp, which in turn routes them through the IDA memory
63// management APIs. Thus, when client code wants to insert new elements, or
64// remove elements, all memory operations go through the IDA memory
65// management APIs, ensuring that everything is allocated and freed from the
66// same heap. Additionally, we define `operator new` and `operator delete`
67// for the base tree structure, to ensure that, if client code were to use
68// `operator new` to create a new map or set, or use `operator delete` to
69// destroy a map or set, that the allocations for the map and set itself
70// would go through the allocator specified as a template parameter. In
71// other words, both sides should be able to freely allocate and deallocate
72// qmap and qset objects via `operator new` and `operator delete`, and the
73// other side should be able to delete such objects regardless of which side
74// they were allocated on, without worrying about heap mismatches.
75//
76// 5. Exceptions: the trickiest part. Exceptions are another corner of C++ that
77// lacks a standardized ABI, meaning that exceptions thrown from inside of
78// Hex-Rays code may not be catchable from client code, and vice versa.
79//
80// This code offers five possible sources of exceptions:
81// 1. `qmap::at` throws `std::out_of_range`.
82// 2. Memory allocation failures propagating through `node_new`. These can
83// happen any time we insert a new element and need to allocate memory,
84// or when copying the tree.
85// 3. Constructors of the key / value types when inserting new elements.
86// 4. Copy constructors of the key / value types when copying the tree.
87// 5. Client code mutating map values in ways that throw exceptions.
88//
89// Since this is a header-only implementation, exceptions thrown from this
90// code will be thrown by whoever invokes the exceptional functionality. So,
91// for example, if Hex-Rays calls `qmap::at`, it's responsible for the
92// exception. Identically, if a client calls `qmap::at` and it throws, that
93// exception will be thrown from their code, not from inside of Hex-Rays, so
94// they will be able to catch it normally. If they don't, it will propagate
95// up to Hex-Rays code, which might not be able to catch it. Clients must
96// catch exceptions, if they want their code to be exception-safe!
97//
98// On the remaining points 2-5, note that IDA's existing discipline here is
99// not great. For example, qvector -- which has a header-only implementation
100// in pro.h -- calls `qalloc_or_throw` for its memory operations, which is
101// an SDK export. In other words, when a qvector reallocation operation
102// fails, the `throw` originates from inside of IDA rather than from client
103// code. This is bad for ABI compatability, because, as discussed above,
104// exceptions aren't guaranteed to cross module boundaries cleanly.
105//
106// In this implementation of qmap and qset, memory operations are delegated
107// to template policy classes via the `AllocPolicy` parameters, which are
108// discussed more in `qalloc_shim.hpp`. In particular, we require that the
109// provided AllocPolicy's `allocate` function is `noexcept` -- i.e., it
110// returns `nullptr` instead of throwing on failure. `AllocPolicy` gets
111// wrapped up into a `qallocator`, which detects `nullptr` returns and
112// throws `std::bad_alloc`. The upshot of this is that allocation failures
113// will happen in client code, not inside of IDA. This addresses point 2
114// from above. As with point 1, if clients want their code to be
115// exception-safe, they need to catch such exceptions themselves.
116//
117// Finally, points 3-5 are the worst, because they may originate from inside
118// of IDA rather than from client code. Let's imagine that:
119//
120// A. Hex-Rays shared a `qmap<ea_t, eavec_t>` with the client.
121// B. The client made a copy of the `qmap`, which triggered a call to
122// the `eavec_t` copy constructor, and thus, `qalloc_or_throw`.
123// C. IDA threw an exception inside of `qalloc_or_thow`.
124//
125// Because the exception from `qalloc_or_throw` originates inside of IDA,
126// the exception would have to traverse the client stack frames on the way
127// back up to IDA. This might lead to the exception not being caught. (This
128// situation comes from `qvector` using `qalloc_or_throw`, and doesn't
129// strictly have anything to do with `qmap` or `qset`.)
130//
131// (Fortunately, the reverse should not happen: Hex-Rays should never end up
132// in a situation where it catches an exception thrown by a custom client
133// type, since Hex-Rays uses qmap and qset to share data with client code,
134// not the other way around. I.e., Hex-Rays code will never have to
135// construct or copy custom client key or value types. Clients are free to
136// use qmap and qset as replacements for std::map and std::set, but Hex-Rays
137// will never have to construct, copy, or mutate client-defined key/value
138// types; it only has to deal with types defined by Hex-Rays in the SDK.)
139//
140// So, to summarize, the worst case for exceptions here stems from existing
141// deficiencies with regards to shared data types that may throw exceptions
142// across module boundaries, with `qalloc_or_throw` (and hence `qvector` and
143// `qstring`, and `qlist`) being the culprits.
144//
145// The only thing I can think to do about this is to redefine
146// `qalloc_or_throw` and `qrealloc_or_throw` away from being exports, and
147// instead define them as inline functions that throw their exceptions on
148// the SDK client side, like this:
149//
150// // First, add throw_nomem to pro.h
151// // Next, change the definition of qalloc_or_throw and qrealloc_or_throw:
152// INLINE void *qalloc_or_throw(size_t size)
153// {
154// if ( void *p = qalloc(size) )
155// return p;
156// throw_nomem();
157// }
158//
159// INLINE void *qrealloc_or_throw(void *alloc, size_t size)
160// {
161// if ( void *p = qrealloc(alloc, size) )
162// return p;
163// throw_nomem();
164// }
165//
166// Then whoever triggered an allocation would always be responsible for any
167// resulting exceptions. But I'm not sure if this would break anything about
168// the existing SDK <=> IDA regimen, and I don't have any other ideas if it
169// turns out to be unsuitable.
170
171#include <algorithm> // for std::lexicographical_compare
172#include <cstddef>
173#include <cstdint>
174#include <initializer_list>
175#include <iterator>
176#include <new>
177#include <stdexcept>
178#include <type_traits>
179#include <utility>
180
181#include "qallocator.hpp"
182#include "qpair.hpp"
183#include "qiterator.hpp"
184
186{
187
188// Forward declaration of the tester class.
189class qtree_tester;
190
191// --------------------------------------------------------------------------
192// "Key selector" is the thing that makes sets different from maps. Maps store
193// entries as qpair<key, value>, and the key selector extracts the key from
194// the pair. Sets store entries as just the key, so the key selector is just
195// the identity function.
196// --------------------------------------------------------------------------
197// The value selector used for sets: identity function.
198template <class T>
200{
201 using key_type = T;
202 constexpr key_type const &operator()(T const &value) const noexcept
203 {
204 return value;
205 }
206};
207
208// The value selector used for maps: select the 'first' member of a qpair.
209// This is the forward declaration; there is intentionally only one
210// specialization for qpair.
211template <class Pair>
213
214template <class First, class Second>
215struct select_first<qpair<First, Second>>
216{
217 using key_type = std::remove_const_t<First>;
218 constexpr First const &operator()(qpair<First, Second> const &value) const noexcept
219 {
220 return value.first;
221 }
222};
223
224// This is here so we can assert the key selector has `key_type` later on
225template <class, class = void>
226struct has_key_type : std::false_type {};
227
228template <class T>
229struct has_key_type<T, std::void_t<typename T::key_type>> : std::true_type {};
230
231// --------------------------------------------------------------------------
232// is_transparent trait: detects if Compare has a nested type is_transparent.
233// This is used to enable heterogeneous lookup when the comparator supports it.
234// --------------------------------------------------------------------------
235template <class Compare, class = void>
236struct is_transparent : std::false_type
237{
238};
239
240template <class Compare>
241struct is_transparent<Compare, std::void_t<typename Compare::is_transparent>> : std::true_type
242{
243};
244
245template <class Compare>
246static constexpr bool is_transparent_v = is_transparent<Compare>::value;
247
248// --------------------------------------------------------------------------
249// Comparator helper traits used for heterogeneous lookup.
250//
251// We want to enable find/lower_bound/etc. with key types K that are different
252// from key_type (e.g., std::string_view vs std::string), but only when the
253// comparator actually supports comparing those types. The following traits
254// detect exactly that.
255
256// is_cmp_invocable<C, A, B> is true if an expression of the form
257// std::declval<const C&>()(std::declval<A>(), std::declval<B>())
258// is well-formed. In other words: can we call comparator C with argument
259// types (A, B)?
260template<class C, class A, class B, class = void>
261struct is_cmp_invocable : std::false_type {};
262
263template<class C, class A, class B>
264struct is_cmp_invocable<C, A, B,
265 std::void_t<decltype(std::declval<const C &>()(std::declval<A>(), std::declval<B>()))>> : std::true_type
266{
267};
268
269// cmp(const Key&, const K&)
270template<class Compare, class Key, class K>
271static constexpr bool cmp_key_to_k_v =
272 is_transparent_v<Compare>
274
275// cmp(const K&, const Key&)
276template<class Compare, class K, class Key>
277static constexpr bool cmp_k_to_key_v =
278 is_transparent_v<Compare>
280
281// Need equality-style comparability: both directions work
282template<class Compare, class Key, class K>
283static constexpr bool cmp_bidir_v =
284 cmp_key_to_k_v<Compare, Key, K>
285&& cmp_k_to_key_v<Compare, K, Key>;
286
287// --------------------------------------------------------------------------
288// This is for linters that complain about std::launder usage. All of the
289// compilers that we use support C++17, so std::launder is available.
290#ifdef __CODE_CHECKER__
291 template <class T> constexpr T *q_launder(T *p) noexcept { return p; }
292#else
293 template <class T> constexpr T *q_launder(T *p) noexcept { return std::launder(p); }
294#endif
295
296// --------------------------------------------------------------------------
297// Used to implement the `value_compare` type for sets and maps. This is part
298// of the standard interface for associative containers, so we provide it for
299// compatibility. Compares Value by applying KeySelector then KeyCompare.
300// --------------------------------------------------------------------------
301template <class Value, class KeySelector, class KeyCompare>
303{
304 constexpr explicit value_compare_impl() {}
305 constexpr bool operator()(Value const &a, Value const &b) const
306 noexcept(noexcept(KeyCompare {} (KeySelector {} (a), KeySelector {} (b))))
307 {
308 return KeyCompare {} (KeySelector {} (a), KeySelector {} (b));
309 }
310};
311
312// --------------------------------------------------------------------------
313// Red-black color
314enum class Color : uint8_t { Red, Black };
315
316// We currently do not implement the C++17 API surface involving node handles
317// (merge, extract). Also, `AllocPolicy` is not an allocator, but it used to
318// construct an allocator. (Perhaps we should change this?)
319template <class Value, class Compare, class AllocPolicy, bool PackNode, class KeySelector>
320class qtree
321{
322 static_assert(!std::is_reference<Value>::value, "Value must not be a reference type");
323
324 // Because we don't want SDK clients to have to link against data items for these things
325 static_assert(std::is_empty<Compare>::value, "Compare must be empty and stateless");
326 static_assert(std::is_empty<AllocPolicy>::value, "AllocPolicy must be empty");
327 static_assert(std::is_empty<KeySelector>::value, "KeySelector must be empty and stateless");
328
329 // Because we default-construct all of these things
330 static_assert(std::is_default_constructible_v<Compare>, "Compare must be default-constructible");
331 static_assert(std::is_default_constructible_v<AllocPolicy>, "AllocPolicy must be default-constructible");
332 static_assert(std::is_default_constructible_v<KeySelector>, "KeySelector must be default-constructible");
333
334 static_assert(has_key_type<KeySelector>::value, "KeySelector must define a nested 'key_type' typedef.");
335
336 using key_selector = KeySelector;
337
338 friend class qtree_detail::qtree_tester;
339
340 template <class, class, class, class, bool>
341 friend class qmap;
342
343 // Definition of the main red-black tree node structure that holds the Value.
344 // The Value is stored in a std::byte array with proper alignment, and
345 // constructed/destructed in place. This allows us to have a single sentinel
346 // nil node (header_) that does not hold a Value, saving memory (among other
347 // design gains).
348 //
349 // Most of the red-black tree algorithms are implemented in the parent class
350 // qtree, because they often require access to the header and/or root
351 // node, i.e., they don't simply treat single nodes in isolation from one
352 // another. As a consequence, qtree_node_packed doesn't need to know about
353 // comparators and key selectors.
354
355 // This version of `qtree_node` stores `is_nil` and `color` as fields, in
356 // contrast to the one below that packs them into the `parent` pointer. We
357 // need two separate structs for this, despite some duplication, for ABI
358 // reasons.
359 struct qtree_node_unpacked
360 {
361 friend class qtree;
362 qtree_node_unpacked *_parent() noexcept { return parent_; }
363 qtree_node_unpacked const *_parent() const noexcept { return parent_; }
364 void _set_parent(qtree_node_unpacked *p) noexcept { parent_ = p; }
365
366 Color _color() const noexcept { return color_; }
367 void _set_color(Color c) noexcept { color_ = c; }
368
369 uint8_t _is_nil() const noexcept { return is_nil_; }
370 void _set_is_nil(uint8_t is_nil) noexcept { is_nil_ = is_nil; }
371
372 private:
373 alignas(alignof(void *)) qtree_node_unpacked *parent_ = nullptr;
374 alignas(alignof(void *)) qtree_node_unpacked *left_ = nullptr;
375 alignas(alignof(void *)) qtree_node_unpacked *right_ = nullptr;
376 Color color_ = Color::Black;
377 uint8_t is_nil_ = true;
378 alignas(Value) std::byte storage_[sizeof(Value)] = {};
379 };
380
381 // Static assertions that only apply to qtree_node_unpacked
382#ifndef __clang__
383 static_assert(offsetof(qtree_node_unpacked, parent_) == 0, "qtree_node_unpacked::parent_ must be at offset 0");
384 static_assert(offsetof(qtree_node_unpacked, color_) == 3 * sizeof(void *), "qtree_node_unpacked::color_ must follow the three pointer-sized fields");
385 static_assert(offsetof(qtree_node_unpacked, is_nil_) == 3 * sizeof(void *) + 1, "qtree_node_unpacked::is_nil_ must follow color_");
386 static_assert(offsetof(qtree_node_unpacked, storage_) >= 3 * sizeof(void *) + 2, "qtree_node_unpacked::storage_ must follow is_nil_");
387#endif
388
389 // This version packs `color` and `is_nil` into the bottom 2 bits of `parent`.
390 // It relies on a guarantee that the allocator returns addresses whose
391 // alignment is at least 4 (so there will indeed be 2 unused bottom bits).
392 struct qtree_node_packed
393 {
394 friend class qtree;
395 qtree_node_packed *_parent() noexcept { return untag(parent_and_tags_); }
396 qtree_node_packed const *_parent() const noexcept { return untag(parent_and_tags_); }
397 // preserve existing tags when changing the parent pointer
398 void _set_parent(qtree_node_packed *p) noexcept { parent_and_tags_ = tagptr(p, parent_and_tags_); }
399
400 Color _color() const noexcept { return ternary(color_mask_, Color::Red, Color::Black); }
401 void _set_color(Color c) noexcept { set_or_clear(c == Color::Red, color_mask_); }
402
403 uint8_t _is_nil() const noexcept { return ternary(nil_mask_, (uint8_t)1, (uint8_t)0); }
404 void _set_is_nil(uint8_t is_nil) noexcept { set_or_clear(is_nil != 0, nil_mask_); }
405
406 private:
407 // parent pointer + {color,is_nil} packed in the low bits
408 alignas(alignof(void *)) std::uintptr_t parent_and_tags_ = 0;
409 alignas(alignof(void *)) qtree_node_packed *left_ = nullptr;
410 alignas(alignof(void *)) qtree_node_packed *right_ = nullptr;
411 alignas(Value) std::byte storage_[sizeof(Value)] = {};
412
413 static_assert(alignof(void *) >= 4, "pointer tagging requires >= 2 tag bits");
414 // Revisit this if we ever support any weird platforms
415 static_assert(sizeof(void *) == sizeof(std::uintptr_t), "uintptr_t must match pointer size");
416
417 // Support definitions/methods for bit packing.
418 static constexpr std::uintptr_t color_mask_ = 0x1; // bit 0
419 static constexpr std::uintptr_t nil_mask_ = 0x2; // bit 1
420 static constexpr std::uintptr_t tag_mask_ = color_mask_ | nil_mask_;
421 static constexpr std::uintptr_t ptr_mask_ = ~tag_mask_;
422
423 // Remove the tag bits, obtain a pointer
424 static qtree_node_packed *untag(std::uintptr_t v) noexcept
425 {
426 return reinterpret_cast<qtree_node_packed*>(v & ptr_mask_);
427 }
428
429 // Add tag bits to a pointer
430 static std::uintptr_t tagptr(qtree_node_packed *p, std::uintptr_t tags) noexcept
431 {
432 return (reinterpret_cast<std::uintptr_t>(p) & ptr_mask_) | (tags & tag_mask_);
433 }
434
435 // Return a value based on mask
436 template <class T> T ternary(std::uintptr_t mask, T t, T f) const noexcept
437 {
438 return (parent_and_tags_ & mask) ? t : f;
439 }
440
441 // Set or clear a bitmask based on bool
442 void set_or_clear(bool b, std::uintptr_t mask)
443 {
444 if ( b )
445 parent_and_tags_ |= mask;
446 else
447 parent_and_tags_ &= ~mask;
448 }
449 };
450
451 // Static assertions that only apply to qtree_node_packed
452#ifndef __clang__
453 static_assert(offsetof(qtree_node_packed, parent_and_tags_) == 0, "qtree_node_packed::parent_and_tags_ must be at offset 0");
454 static_assert(alignof(Value) > alignof(void *) || offsetof(qtree_node_packed, storage_) == 3 * sizeof(void *), "qtree_node_packed::storage_ must immediately follow right_");
455 static_assert(alignof(Value) <= alignof(void *) || offsetof(qtree_node_packed, storage_) > 3 * sizeof(void *), "qtree_node_packed::storage_ must follow right_");
456#endif
457
458 // Choose the node type (packed or unpacked) based on PackNode template param
459 using node_type = std::conditional_t<PackNode, qtree_node_packed, qtree_node_unpacked>;
460 using node_alloc = qallocator<node_type, AllocPolicy>;
461 using node_traits = std::allocator_traits<node_alloc>;
462 static node_alloc make_node_alloc() noexcept { return node_alloc {}; }
463
464 // Static assertions that apply to both node varieties
465#ifndef __clang__
466 static_assert(alignof(node_type) >= alignof(void *), "node alignment");
467 static_assert(std::is_standard_layout<node_type>::value, "node_type must be standard layout");
468 static_assert(offsetof(node_type, left_) % alignof(void *) == 0, "packed layout not allowed");
469 static_assert(offsetof(node_type, left_) == sizeof(void *), "left_ must immediately follow parent field");
470 static_assert(offsetof(node_type, right_) % alignof(void *) == 0, "packed layout not allowed");
471 static_assert(offsetof(node_type, right_) == 2 * sizeof(void *), "right_ must immediately follow left_");
472 static_assert(offsetof(node_type, storage_) % alignof(Value) == 0, "storage_ must be Value-aligned (don't compile under packing)");
473 static_assert(alignof(node_type) >= (alignof(void *) > alignof(Value) ? alignof(void *) : alignof(Value)),"node alignment must meet pointer/value alignment");
474#endif
475
476public:
477 using value_type = Value;
478 using key_type = typename key_selector::key_type;
479 using key_compare = Compare;
480 using size_type = std::size_t;
481 using difference_type = ptrdiff_t;
485 using const_pointer = value_type const *;
488
489 // This thing defines operator new/delete for the qtree type, such that if
490 // you were to allocate a qtree via operator new, or delete a qtree via
491 // operator delete, the allocation would go through the AllocPolicy.
493
494 // ----------------------- Public interface -------------------------------
495 // One thing to note about qtree is that it always represents the empty
496 // state without any material nodes. Initially, header_ is null and the
497 // tree is completely header-less. On first insertion (or when copying /
498 // moving from a non-empty tree) we allocate the sentinel header node.
499 //
500 // The header node serves double duty: it is the shared nil sentinel and
501 // it caches the root/min/max pointers. The header node is black, its
502 // parent points to the root, its left points to the minimum, and its
503 // right points to the maximum. When the tree is empty but a header has
504 // been allocated, the header's parent, left, and right all point to
505 // itself.
506 qtree() noexcept
507 : header_(nullptr), node_count_(0)
508 {
509 }
510
511 qtree(std::initializer_list<value_type> init) : qtree()
512 {
513 insert(init.begin(), init.end());
514 }
515
516 qtree(qtree const &other)
517 : header_(nullptr), node_count_(0)
518 {
519 if ( other.empty() )
520 return;
521
522 header_ = allocate_header();
523 node_count_ = 0;
524 try
525 {
526 init_header();
527 clone_from(other); // may throw
528 }
529 catch ( ... )
530 {
531 node_delete(header_); // prevent leak of header_ on failure
532 throw;
533 }
534 }
535
536 qtree(qtree &&other) noexcept
537 : header_(other.header_), node_count_(other.node_count_)
538 {
539 other.header_ = nullptr;
540 other.node_count_ = 0;
541 }
542
544 {
545 if ( header_ != nullptr )
546 {
547 clear();
548 node_delete(header_);
549 }
550 }
551
552 qtree &operator=(qtree const &other)
553 {
554 if ( this == &other )
555 return *this;
556 qtree tmp(other);
557 using std::swap;
558 swap(*this, tmp);
559 return *this;
560 }
561
562 qtree &operator=(qtree &&other) noexcept
563 {
564 if ( this == &other )
565 return *this;
566
567 // Destroy current contents
568 if ( header_ != nullptr )
569 {
570 clear();
571 node_delete(header_);
572 }
573
574 header_ = other.header_;
575 node_count_ = other.node_count_;
576
577 other.header_ = nullptr;
578 other.node_count_ = 0;
579 return *this;
580 }
581
582 void swap(qtree &other) noexcept
583 {
584 if ( this == &other )
585 return;
586 using std::swap;
587 swap(header_, other.header_);
588 swap(node_count_, other.node_count_);
589 }
590 friend void swap(qtree &a, qtree &b) noexcept(noexcept(a.swap(b))) { a.swap(b); }
591
592 // ---------------------------------------------------------------------------
593 // The familiar parts of the std::map/set interface: iteration.
594 // ---------------------------------------------------------------------------
595
596 // Bidirectional iterator for in-order traversal of the tree. Nothing fancy.
598 {
599 public:
600 using iterator_category = std::bidirectional_iterator_tag;
601 using difference_type = ptrdiff_t;
605
606 mutable_iterator() noexcept = default;
607
608 reference operator*() const noexcept { return *value_ptr(node_); }
609 pointer operator->() const noexcept { return value_ptr(node_); }
610
612 {
613 increment();
614 return *this;
615 }
616
618 {
619 mutable_iterator tmp(*this);
620 increment();
621 return tmp;
622 }
623
625 {
626 decrement();
627 return *this;
628 }
629
631 {
632 mutable_iterator tmp(*this);
633 decrement();
634 return tmp;
635 }
636
637 friend bool operator==(mutable_iterator lhs, mutable_iterator rhs) noexcept
638 {
639 return lhs.node_ == rhs.node_;
640 }
641
642 friend bool operator!=(mutable_iterator lhs, mutable_iterator rhs) noexcept
643 {
644 return !(lhs == rhs);
645 }
646
647 private:
648 node_type *node_ = nullptr;
649
650 explicit mutable_iterator(node_type *node) noexcept : node_(node)
651 {
652 }
653
654 void increment() { node_ = successor(node_); }
655 // --end(): when node_ is the sentinel, max is header_->right.
656 // ++end(): undefined.
657 void decrement() { node_ = prev_including_header(node_); }
658
659 friend class qtree;
660 friend class const_iterator;
661 template <class, class, class, class, bool>
662 friend class qmap;
663 };
664
665 static_assert(std::is_trivially_copyable<mutable_iterator>::value, "iter must be trivial");
666 static_assert(sizeof(mutable_iterator) == sizeof(void *), "iter size must be 1 ptr");
667
668 // Although const_iterator and iterator are nearly identical, if you were to
669 // try to refactor them together (like I did), you'd find that you suddenly
670 // experience many compiler errors that ultimately stem from const vs.
671 // non-const contexts. So it's simpler to just duplicate the code, and due to
672 // the factoring of most relevant functionality into qtree_node, the code is
673 // simple enough, anyway.
675 {
676 public:
677 using iterator_category = std::bidirectional_iterator_tag;
678 using difference_type = ptrdiff_t;
680 using pointer = value_type const *;
681 using reference = value_type const &;
682
683 const_iterator() noexcept = default;
684 const_iterator(mutable_iterator it) noexcept : node_(it.node_) {}
685
686 reference operator*() const noexcept { return *value_ptr(node_); }
687 pointer operator->() const noexcept { return value_ptr(node_); }
688
690 {
691 increment();
692 return *this;
693 }
694
696 {
697 const_iterator tmp(*this);
698 increment();
699 return tmp;
700 }
701
703 {
704 decrement();
705 return *this;
706 }
707
709 {
710 const_iterator tmp(*this);
711 decrement();
712 return tmp;
713 }
714
715 friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept
716 {
717 return lhs.node_ == rhs.node_;
718 }
719
720 friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept
721 {
722 return !(lhs == rhs);
723 }
724
725 private:
726 node_type *node_ = nullptr;
727
728 explicit const_iterator(node_type *node) noexcept : node_(node)
729 {
730 }
731
732 void increment() { node_ = successor(node_); }
733 // --end(): when node_ is the sentinel, max is header_->right.
734 // ++end(): undefined.
735 void decrement() { node_ = prev_including_header(node_); }
736
737 friend class qtree;
738 };
739
740 static_assert(std::is_trivially_copyable<const_iterator>::value, "citer must be trivial");
741 static_assert(sizeof(const_iterator) == sizeof(void *), "citer size must be 1 ptr");
742
743 // qset uses const_iterator for both iterator and const_iterator; qmap uses
744 // mutable_iterator for iterator and const_iterator for const_iterator.
745 // long line because of checkstyle
746 using iterator = typename std::conditional_t<std::is_same_v<KeySelector, qtree_detail::identity_key<Value>>,const_iterator,mutable_iterator>;
749
750 iterator begin() noexcept
751 {
752 if ( header_ == nullptr || node_count_ == 0 )
753 return end();
754 return iterator(left(header_));
755 }
756 const_iterator begin() const noexcept
757 {
758 if ( header_ == nullptr || node_count_ == 0 )
759 return end();
760 return const_iterator(left(header_));
761 }
762 const_iterator cbegin() const noexcept { return begin(); }
763
764 iterator end() noexcept { return iterator(header_); }
765 const_iterator end() const noexcept { return const_iterator(header_); }
766 const_iterator cend() const noexcept { return end(); }
767
768 reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
770 const_reverse_iterator crbegin() const noexcept { return rbegin(); }
771
772 reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
774 const_reverse_iterator crend() const noexcept { return rend(); }
775
776 // Mixed comparisons between iterator and const_iterator.
777 friend bool operator==(mutable_iterator l, const_iterator r) noexcept { return const_iterator(l) == r; }
778 friend bool operator==(const_iterator l, mutable_iterator r) noexcept { return l == const_iterator(r); }
779 friend bool operator!=(mutable_iterator l, const_iterator r) noexcept { return !(l == r); }
780 friend bool operator!=(const_iterator l, mutable_iterator r) noexcept { return !(l == r); }
781
782 // ---------------------------------------------------------------------------
783 // Below, determines whether heterogeneous lookup is enabled for the given
784 // Compare, key type, and lookup key type K.
785 // ---------------------------------------------------------------------------
786
787 // For find/contains/count: we need both directions.
788 template <class K>
789 using enable_hetero_eq = std::enable_if_t<cmp_bidir_v<Compare, key_type, K>, int>;
790
791 // For lower_bound: we only call cmp(node_key, key).
792 template <class K>
793 using enable_hetero_lb = std::enable_if_t<cmp_key_to_k_v<Compare, key_type, K>, int>;
794
795 // For upper_bound: we only call cmp(key, node_key).
796 template <class K>
797 using enable_hetero_ub = std::enable_if_t<cmp_k_to_key_v<Compare, K, key_type>, int>;
798
799 // For equal_range(K): we call both lower_bound_node(K) and upper_bound_node(K).
800 template <class K>
801 using enable_hetero_eqrange = std::enable_if_t<cmp_key_to_k_v<Compare, key_type, K> && cmp_k_to_key_v<Compare, K, key_type>, int>;
802
803 // Unused, but, outside code can use this to test whether `K` can be used as
804 // a heterogenous key.
805 template<class K>
806 static constexpr bool supports_heterogeneous_key_v = cmp_bidir_v<Compare, key_type, K>;
807
808 // ---------------------------------------------------------------------------
809 // The familiar parts of the std::map/set interface: query/lookup/comparisons.
810 // As in the standard, functions that look keys up are deliberately not
811 // noexcept, because the comparator might throw an exception.
812 // ---------------------------------------------------------------------------
813
814 bool empty() const noexcept { return node_count_ == 0; }
815 size_type size() const noexcept { return node_count_; }
816
817 friend bool operator==(qtree const &a, qtree const &b)
818 {
819 if ( a.size() != b.size() )
820 return false;
821 return std::equal(a.begin(), a.end(), b.begin());
822 }
823
824 friend bool operator!=(qtree const &a, qtree const &b)
825 {
826 return !(a == b);
827 }
828
829 // Relational operators mirror std::set/map semantics: lexicographical compare
830 // of value_type using its natural operator<. For sets, this compares keys.
831 // For maps, this compares std::pair which compares both key AND mapped value.
832 friend bool operator<(qtree const &a, qtree const &b)
833 {
834 return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());
835 }
836
837 friend bool operator>(qtree const &a, qtree const &b)
838 {
839 return b < a;
840 }
841
842 friend bool operator<=(qtree const &a, qtree const &b)
843 {
844 return !(b < a);
845 }
846
847 friend bool operator>=(qtree const &a, qtree const &b)
848 {
849 return !(a < b);
850 }
851
852 [[nodiscard]] iterator find(key_type const &key)
853 {
854 return iterator(find_node(key));
855 }
856
857 [[nodiscard]] const_iterator find(key_type const &key) const
858 {
859 return const_iterator(find_node(key));
860 }
861
862 template <class K, class = enable_hetero_eq<K>>
863 [[nodiscard]] iterator find(K const &key)
864 {
865 return iterator(find_node(key));
866 }
867
868 template <class K, class = enable_hetero_eq<K>>
869 [[nodiscard]] const_iterator find(K const &key) const
870 {
871 return const_iterator(find_node(key));
872 }
873
874 [[nodiscard]] bool contains(key_type const &key) const
875 {
876 return find_node(key) != header_;
877 }
878
879 template <class K, class = enable_hetero_eq<K>>
880 [[nodiscard]] bool contains(K const &key) const
881 {
882 return find_node(key) != header_;
883 }
884
885 [[nodiscard]] size_type count(key_type const &key) const
886 {
887 return contains(key) ? 1u : 0u;
888 }
889
890 template <class K, class = enable_hetero_eq<K>>
891 [[nodiscard]] size_type count(K const &key) const
892 {
893 return contains(key) ? 1u : 0u;
894 }
895
896 [[nodiscard]] iterator lower_bound(key_type const &key)
897 {
898 return iterator(lower_bound_node(key));
899 }
900
901 [[nodiscard]] const_iterator lower_bound(key_type const &key) const
902 {
903 return const_iterator(lower_bound_node(key));
904 }
905
906 template <class K, class = enable_hetero_lb<K>>
907 [[nodiscard]] iterator lower_bound(K const &key)
908 {
909 return iterator(lower_bound_node(key));
910 }
911
912 template <class K, class = enable_hetero_lb<K>>
913 [[nodiscard]] const_iterator lower_bound(K const &key) const
914 {
915 return const_iterator(lower_bound_node(key));
916 }
917
918 [[nodiscard]] iterator upper_bound(key_type const &key)
919 {
920 return iterator(upper_bound_node(key));
921 }
922
923 [[nodiscard]] const_iterator upper_bound(key_type const &key) const
924 {
925 return const_iterator(upper_bound_node(key));
926 }
927
928 template <class K, class = enable_hetero_ub<K>>
929 [[nodiscard]] iterator upper_bound(K const &key)
930 {
931 return iterator(upper_bound_node(key));
932 }
933
934 template <class K, class = enable_hetero_ub<K>>
935 [[nodiscard]] const_iterator upper_bound(K const &key) const
936 {
937 return const_iterator(upper_bound_node(key));
938 }
939
941 {
942 return { lower_bound(key), upper_bound(key) };
943 }
944
946 {
947 return { lower_bound(key), upper_bound(key) };
948 }
949
950 template <class K, class = enable_hetero_eqrange<K>>
951 [[nodiscard]] qpair<iterator, iterator> equal_range(K const &key)
952 {
953 return { iterator(lower_bound_node(key)), iterator(upper_bound_node(key)) };
954 }
955
956 template <class K, class = enable_hetero_eqrange<K>>
957 [[nodiscard]] qpair<const_iterator, const_iterator> equal_range(K const &key) const
958 {
959 return { const_iterator(lower_bound_node(key)), const_iterator(upper_bound_node(key)) };
960 }
961
962 // ---------------------------------------------------------------------------
963 // The familiar parts of the std::map/set interface: insertion.
964 // ---------------------------------------------------------------------------
965
966 template <class InputIt>
967 void insert(InputIt first, InputIt last)
968 {
969 for ( ; first != last; ++first )
970 insert(*first);
971 }
972
973 void insert(std::initializer_list<value_type> init)
974 {
975 insert(init.begin(), init.end());
976 }
977
979 {
980 return lazy_emplace_impl(
981 KeySelector {} (value),
982 [&](value_type *slot) { ::new ((void*)slot) value_type(value); } );
983 }
984
986 {
987 return lazy_emplace_impl(
988 KeySelector {} (value),
989 [&](value_type *slot) { ::new ((void*)slot) value_type(std::move(value)); } );
990 }
991
992 iterator insert(const_iterator hint, value_type const &value)
993 {
994 auto result = lazy_emplace_with_hint(
995 KeySelector {} (value),
996 hint,
997 [&](value_type *slot) { ::new ((void*)slot) value_type(value); } );
998 return result.first;
999 }
1000
1001 iterator insert(const_iterator hint, value_type &&value)
1002 {
1003 auto result = lazy_emplace_with_hint(
1004 KeySelector {} (value),
1005 hint,
1006 [&](value_type *slot) { ::new ((void*)slot) value_type(std::move(value)); } );
1007 return result.first;
1008 }
1009
1010 template <class... Args>
1012 {
1013 value_type tmp(std::forward<Args>(args)...);
1014 return lazy_emplace_impl(
1015 KeySelector {} (tmp),
1016 [&](value_type *slot) { ::new ((void*)slot) value_type(std::move(tmp)); } );
1017 }
1018
1019 template <class... Args>
1020 iterator emplace_hint(const_iterator hint, Args &&...args)
1021 {
1022 value_type tmp(std::forward<Args>(args)...);
1023 auto result = lazy_emplace_with_hint(
1024 KeySelector {} (tmp),
1025 hint,
1026 [&](value_type *slot) { ::new ((void*)slot) value_type(std::move(tmp)); } );
1027 return result.first;
1028 }
1029
1030 // ---------------------------------------------------------------------------
1031 // The familiar parts of the std::map/set interface: removal.
1032 // ---------------------------------------------------------------------------
1033
1034 iterator erase(const_iterator pos)
1035 {
1036 iterator mutable_it(pos.node_);
1037 ++mutable_it;
1038 // Advance iterator before erase_node() so we never touch the freed node.
1039 erase_node(pos.node_);
1040 return mutable_it;
1041 }
1042
1043 iterator erase(const_iterator first, const_iterator last)
1044 {
1045 iterator it(first.node_);
1046 iterator last_it(last.node_);
1047 while ( it != last_it )
1048 it = erase(it);
1049 return it;
1050 }
1051
1053 {
1054 node_type *node = find_node(key);
1055 if ( node == header_ )
1056 return 0;
1057 erase_node(node);
1058 return 1;
1059 }
1060
1061 template <class K, class = enable_hetero_eq<K>>
1062 size_type erase(K const &key)
1063 {
1064 node_type *node = find_node(key);
1065 if ( node == header_ )
1066 return 0;
1067 erase_node(node);
1068 return 1;
1069 }
1070
1071 template<class Pred>
1073 {
1074 size_type n = 0;
1075 for ( auto it = begin(); it != end(); )
1076 {
1077 if ( pred(*it) )
1078 {
1079 it = erase(it);
1080 ++n;
1081 }
1082 else
1083 ++it;
1084 }
1085 return n;
1086 }
1087
1088 void clear() noexcept
1089 {
1090 if ( header_ == nullptr || node_count_ == 0 )
1091 {
1092 // header-less empty, or already empty with a header
1093 node_count_ = 0;
1094 if ( header_ != nullptr )
1095 init_header(); // keep sentinel, just normalize its links
1096 return;
1097 }
1098
1099 clear_subtree(root()); // frees all non-header nodes
1100 init_header(); // make header_ the self-linked nil node again
1101 node_count_ = 0;
1102 }
1103
1104 // ---------------------------------------------------------------------------
1105 // Less-familiar parts of the std::map/set interface.
1106 // ---------------------------------------------------------------------------
1107
1108 // These two are part of std::set/map interface, so we replicate them.
1109 key_compare key_comp() const noexcept { return key_compare {}; }
1110 value_compare value_comp() const noexcept { return value_compare {}; }
1111 allocator_type get_allocator() const noexcept { return allocator_type {}; }
1112 size_type max_size() const noexcept { return get_allocator().max_size(); }
1113
1114private:
1115 // ---------------------------------------------------------------------------
1116 // General red-black tree stuff. Factored out of the qtree_node variants above
1117 // to prevent duplication and allow easier refactoring in the future.
1118 // ---------------------------------------------------------------------------
1119 static node_type *left(node_type *node) noexcept { return node->left_; }
1120 static node_type const *left(const node_type *node) noexcept { return node->left_; }
1121 static void set_left(node_type *node, node_type *left) noexcept { node->left_ = left; }
1122
1123 static node_type *right(node_type *node) noexcept { return node->right_; }
1124 static node_type const *right(const node_type *node) noexcept { return node->right_; }
1125 static void set_right(node_type *node, node_type *right) noexcept { node->right_ = right; }
1126
1127 static node_type *parent(node_type *node) noexcept { return node->_parent(); }
1128 static node_type const *parent(const node_type *node) noexcept { return node->_parent(); }
1129 static void set_parent(node_type *node, node_type *parent) noexcept { node->_set_parent(parent); }
1130
1131 static Color color(const node_type *node) noexcept { return node->_color(); }
1132 static void set_color(node_type *node, Color color_val) noexcept { node->_set_color(color_val); }
1133
1134 static std::uint8_t is_nil(const node_type *node) noexcept { return node->_is_nil(); }
1135 static void set_is_nil(node_type *node, std::uint8_t is_nil_val) noexcept { node->_set_is_nil(is_nil_val); }
1136
1137 // Descend to the left-most non-nil descendant of `node`. In a binary search
1138 // tree this is the smallest key in the subtree, so this helper is used when
1139 // we need to find the in-order beginning of a subtree (e.g., during erase
1140 // and when updating the header extrema). The precondition is that `node`
1141 // represents a material element and therefore is not the sentinel header.
1142 // Can throw due to `QASSERT`.
1143 static node_type *minimum(node_type *node)
1144 {
1145 return minimum_impl(node);
1146 }
1147 static const node_type *minimum(const node_type *node)
1148 {
1149 return minimum_impl(const_cast<node_type *>(node));
1150 }
1151 static node_type *minimum_impl(node_type *node)
1152 {
1153 QASSERT(3437, !is_nil(node));
1154 while ( !is_nil(left(node)) )
1155 {
1156 // Keep walking left until we hit the sentinel; that node owns the
1157 // smallest key in this subtree.
1158 node = left(node);
1159 }
1160 return node;
1161 }
1162
1163 // Mirror of minimum(): walk right until we hit the greatest element in the
1164 // subtree rooted at `node`. Returning the right-most node lets us refresh
1165 // the header extrema quickly after mutations.
1166 // Can throw due to `QASSERT`.
1167 static node_type *maximum(node_type *node)
1168 {
1169 return maximum_impl(node);
1170 }
1171 static const node_type *maximum(const node_type *node)
1172 {
1173 return maximum_impl(const_cast<node_type *>(node));
1174 }
1175 static node_type *maximum_impl(node_type *node)
1176 {
1177 QASSERT(3438, !is_nil(node));
1178 while ( !is_nil(right(node)) )
1179 {
1180 // Mirror minimum(): keep walking right to find the largest key.
1181 node = right(node);
1182 }
1183 return node;
1184 }
1185
1186 // Symmetric to predecessor(): either descend into the left-most child of
1187 // the right subtree, or climb parents until we traverse a left edge. When
1188 // visiting the sentinel (successor of end()), callers will receive header_.
1189 // Can throw due to `QASSERT`.
1190 static node_type *successor(node_type *node)
1191 {
1192 return successor_impl(node);
1193 }
1194 static const node_type *successor(const node_type *node)
1195 {
1196 return successor_impl(const_cast<node_type *>(node));
1197 }
1198 static node_type *successor_impl(node_type *node)
1199 {
1200 if ( is_nil(node) )
1201 {
1202 // end() stays end() if incremented; still outside supported contract.
1203 return node;
1204 }
1205 if ( !is_nil(right(node)) )
1206 {
1207 // Successor is the minimum of the right subtree when it exists.
1208 return minimum(right(node));
1209 }
1210 node_type *parent_node = parent(node);
1211 while ( !is_nil(parent_node) && node == right(parent_node) )
1212 {
1213 // Bubble up until we exit through a right edge; the first left turn
1214 // gives us the next greater node (or the header sentinel).
1215 node = parent_node;
1216 parent_node = parent(parent_node);
1217 }
1218 return parent_node;
1219 }
1220
1221 // Compute the in-order predecessor of `node`. If a left child exists we go
1222 // to its right-most descendant; otherwise we bubble up towards the root
1223 // until we take the first right turn. The header sentinel acts as the
1224 // predecessor of begin() which keeps iterator code simple.
1225 // Can throw due to `QASSERT`.
1226 static node_type *predecessor(node_type *node)
1227 {
1228 return predecessor_impl(node);
1229 }
1230 static node_type *predecessor(const node_type *node)
1231 {
1232 return predecessor_impl(const_cast<node_type *>(node));
1233 }
1234 static node_type *predecessor_impl(node_type *node)
1235 {
1236 if ( is_nil(node) )
1237 {
1238 return node;
1239 }
1240 if ( !is_nil(left(node)) )
1241 {
1242 // Any left child means the predecessor lives at the max of that subtree.
1243 return maximum(left(node));
1244 }
1245 node_type *parent_node = parent(node);
1246 while ( !is_nil(parent_node) && node == left(parent_node) )
1247 {
1248 // Walk up the tree until we step out of a left edge; the first right
1249 // turn yields the predecessor.
1250 node = parent_node;
1251 parent_node = parent(parent_node);
1252 }
1253 return parent_node;
1254 }
1255
1256 static Color node_color(const node_type *node) noexcept
1257 {
1258 return is_nil(node) ? Color::Black : color(node);
1259 }
1260 static Color left_color(const node_type *node) noexcept
1261 {
1262 return is_nil(node) ? Color::Black : node_color(left(node));
1263 }
1264 static Color right_color(const node_type *node) noexcept
1265 {
1266 return is_nil(node) ? Color::Black : node_color(right(node));
1267 }
1268 static node_type *prev_including_header(node_type *node)
1269 {
1270 return is_nil(node) ? right(node) : predecessor(node);
1271 }
1272 static const node_type *prev_including_header(const node_type *node)
1273 {
1274 return is_nil(node) ? right(node) : predecessor(node);
1275 }
1276
1277 // ---------------------------------------------------------------------------
1278 // Utilities for construction, destruction, copying, and swapping.
1279 // ---------------------------------------------------------------------------
1280 static void destroy_value(node_type *node) noexcept
1281 {
1282 if ( !is_nil(node) )
1283 {
1284 value_addr(node)->~Value();
1285 set_is_nil(node, true);
1286 }
1287 }
1288
1289 static Value *value_addr(node_type *node) noexcept { return reinterpret_cast<Value *>(node->storage_); }
1290 static Value const *value_addr(const node_type *node) noexcept { return reinterpret_cast<Value const *>(node->storage_); }
1291
1292 static Value *value_ptr(node_type *node) noexcept { return q_launder(value_addr(node)); }
1293 static Value const *value_ptr(const node_type *node) noexcept { return q_launder(value_addr(node)); }
1294
1295 // Allocate the sentinel header node. The header doubles as the nil
1296 // sentinel and stores cached links to the root/min/max nodes. We only
1297 // allocate it on demand (e.g., on the first insertion or when cloning
1298 // from a non-empty tree) and wire every pointer to itself until the tree
1299 // contains data.
1300 node_type *allocate_header()
1301 {
1302 node_type *h = node_new(); // Obtain storage for the sentinel node.
1303 set_parent(h, h); // Header pretends to be its own parent.
1304 set_left(h, h); // ...and its own min/max until populated.
1305 set_right(h, h);
1306 set_color(h, Color::Black); // Sentinels are always black in RB trees.
1307 set_is_nil(h, true); // Mark as nil so algorithms treat it as such.
1308 return h;
1309 }
1310
1311 // Reset the header to represent an empty tree after we have already
1312 // created it. This keeps the sentinel black and self-referential so
1313 // algorithms can treat `header_` as a nil child without sprinkling
1314 // special cases. When header_ is null, the tree is also empty, but
1315 // without a material sentinel node yet.
1316 void init_header() noexcept
1317 {
1318 set_parent(header_, header_); // Root pointer collapses back to header.
1319 set_left(header_, header_); // Min/max cache also points at the sentinel.
1320 set_right(header_, header_);
1321 set_color(header_, Color::Black);
1322 set_is_nil(header_, true);
1323 }
1324
1325 void ensure_header()
1326 {
1327 if ( header_ == nullptr )
1328 {
1329 header_ = allocate_header();
1330 init_header();
1331 }
1332 }
1333
1334 // Recursively destroy a subtree rooted at `node`. We descend depth-first,
1335 // release both children, destroy the stored Value, and finally return the
1336 // node back to the allocator. Nil children short-circuit the recursion.
1337 void clear_subtree(node_type *node) noexcept
1338 {
1339 if ( is_nil(node) )
1340 return;
1341 if ( !is_nil(left(node)) )
1342 {
1343 // Clear out the entire left branch before destroying this node.
1344 clear_subtree(left(node));
1345 }
1346 if ( !is_nil(right(node)) )
1347 {
1348 // Likewise release the right subtree.
1349 clear_subtree(right(node));
1350 }
1351 destroy_value(node);
1352 node_delete(node);
1353 }
1354
1355 // Clone a subtree from `other_node` into this tree, reparenting the new nodes
1356 // under `parent`. We copy construct each payload, mirror the color and then
1357 // recursively duplicate the left/right descendants. Any exception during
1358 // cloning unwinds via clear_subtree(), so we never leak partially built nodes.
1359 node_type *clone_subtree(node_type const *other_node, node_type *parent_node)
1360 {
1361 if ( is_nil(other_node) )
1362 return header_;
1363
1364 node_type *new_node = node_new(); // Create storage for the cloned node.
1365 try
1366 {
1367 ::new (value_addr(new_node)) Value(*value_ptr(other_node));
1368 set_is_nil(new_node, false);
1369 }
1370 catch ( ... )
1371 {
1372 node_delete(new_node);
1373 throw;
1374 }
1375
1376 set_color(new_node, color(other_node));
1377 set_parent(new_node, parent_node);
1378 set_left(new_node, header_); // Initialize children to sentinels until filled.
1379 set_right(new_node, header_);
1380
1381 try
1382 {
1383 // Recursively clone left and right subtrees under the new node.
1384 set_left(new_node, clone_subtree(left(other_node), new_node));
1385 set_right(new_node, clone_subtree(right(other_node), new_node));
1386 }
1387 catch ( ... )
1388 {
1389 clear_subtree(new_node);
1390 throw;
1391 }
1392
1393 return new_node;
1394 }
1395
1396 // Rebuild this tree from `other`. We clone the other root and then refresh
1397 // the cached extrema and node count. On failure we roll back to an empty
1398 // state, leaving the current tree consistent.
1399 void clone_from(qtree const &other)
1400 {
1401 // Make sure nobody changes the code to do this in the future
1402 QASSERT(3439, !other.empty());
1403
1404 try
1405 {
1406 node_type *new_root = clone_subtree(other.root(), header_); // Deep copy.
1407 set_parent(header_, new_root); // Fix root cache.
1408 set_left(header_, minimum(new_root)); // Refresh minimum.
1409 set_right(header_, maximum(new_root)); // Refresh maximum.
1410 node_count_ = other.node_count_; // Mirror size.
1411 }
1412 catch ( ... )
1413 {
1414 clear();
1415 throw;
1416 }
1417 }
1418
1419 // ---------------------------------------------------------------------------
1420 // Red-black tree algorithms and utilities.
1421 // ---------------------------------------------------------------------------
1422
1423 // Because the comparator is required to be empty and stateless, we can just
1424 // default-construct it on demand.
1425 static constexpr key_compare comp() noexcept { return key_compare {}; }
1426
1427 // Provide mutable access to the root pointer stored off of the header
1428 // sentinel. When header_ is non-null, the header pretends to be the
1429 // parent of the actual root, so returning header_->parent keeps tree
1430 // rotations uniform. When header_ is null the tree is empty and root()
1431 // returns nullptr.
1432 node_type *root() noexcept
1433 {
1434 if ( header_ == nullptr )
1435 return nullptr;
1436 return parent(header_);
1437 }
1438
1439 // Const-qualified facade returning the same cached root pointer.
1440 node_type const *root() const noexcept
1441 {
1442 if ( header_ == nullptr )
1443 return nullptr;
1444 return parent(header_);
1445 }
1446
1447 // Extract the key from a node's stored Value using the key selector policy.
1448 // This indirection lets qtree power both qmap (pair key/value) and qset
1449 // (key-only) without duplicating tree logic.
1450 key_type const &node_key(node_type const *node) const
1451 {
1452 return KeySelector {} (*value_ptr(node));
1453 }
1454
1455 // -----------------------------------------------------------------------------
1456 // rotate_left()
1457 // -----------------------------------------------------------------------------
1458 // Perform the standard left rotation used by red-black trees.
1459 //
1460 // Before rotation (pivoting on 'node'):
1461 //
1462 // parent
1463 // |
1464 // (node)
1465 // . .
1466 // A (right)
1467 // . .
1468 // B C
1469 //
1470 // After rotation:
1471 //
1472 // parent
1473 // |
1474 // (right)
1475 // . .
1476 // (node) C
1477 // . .
1478 // A B
1479 //
1480 // The "right" child moves up to become the subtree root,
1481 // "node" slides left under it, and the B subtree migrates from
1482 // right->left to node->right.
1483 // -----------------------------------------------------------------------------
1484 void rotate_left(node_type *node) noexcept
1485 {
1486 node_type *right_node = right(node);
1487 QASSERT(3440, !is_nil(right_node));
1488
1489 // B subtree moves across
1490 set_right(node, left(right_node));
1491 if ( !is_nil(left(right_node)) )
1492 set_parent(left(right_node), node);
1493
1494 // promote 'right' into node's former parent slot
1495 set_parent(right_node, parent(node));
1496 if ( parent(node) == header_ )
1497 set_parent(header_, right_node); // update cached root
1498 else if ( node == left(parent(node)) )
1499 set_left(parent(node), right_node);
1500 else
1501 set_right(parent(node), right_node);
1502
1503 // complete the rotation
1504 set_left(right_node, node);
1505 set_parent(node, right_node);
1506 }
1507
1508 // -----------------------------------------------------------------------------
1509 // rotate_right()
1510 // -----------------------------------------------------------------------------
1511 // Mirror of rotate_left(): the left child is promoted and 'node' becomes its
1512 // right child.
1513 //
1514 // Before rotation (pivoting on 'node'):
1515 //
1516 // parent
1517 // |
1518 // (node)
1519 // . .
1520 // (left) C
1521 // . .
1522 // A B
1523 //
1524 // After rotation:
1525 //
1526 // parent
1527 // |
1528 // (left)
1529 // . .
1530 // A (node)
1531 // . .
1532 // B C
1533 //
1534 // The "left" child moves up, "node" slides right under it,
1535 // and the B subtree migrates from left->right to node->left.
1536 // -----------------------------------------------------------------------------
1537 void rotate_right(node_type *node) noexcept
1538 {
1539 node_type *left_node = left(node);
1540 QASSERT(3441, !is_nil(left_node));
1541
1542 // B subtree moves across
1543 set_left(node, right(left_node));
1544 if ( !is_nil(right(left_node)) )
1545 set_parent(right(left_node), node);
1546
1547 // promote 'left' into node's former parent slot
1548 set_parent(left_node, parent(node));
1549 if ( parent(node) == header_ )
1550 set_parent(header_, left_node); // update cached root
1551 else if ( node == right(parent(node)) )
1552 set_right(parent(node), left_node);
1553 else
1554 set_left(parent(node), left_node);
1555
1556 // complete the rotation
1557 set_right(left_node, node);
1558 set_parent(node, left_node);
1559 }
1560
1561 // ---------------------------------------------------------------------------
1562 // Red-black tree algorithms and utilities: lookup.
1563 // ---------------------------------------------------------------------------
1564
1565 template <class K>
1566 // Search for an exact key, following the BST invariant relative to the
1567 // stateless comparator. When the key is not present we return the header
1568 // sentinel so callers can interpret "not found" uniformly.
1569 node_type *find_node(K const &key) const
1570 {
1571 node_type *node = const_cast<node_type *>(root());
1572 // Handle headerless case: return the sentinel (which is nullptr here)
1573 if ( node == nullptr )
1574 return node;
1575 const auto cmp = comp();
1576 while ( !is_nil(node) )
1577 {
1578 key_type const &node_k = node_key(node); // Extract current key for comparisons.
1579 if ( cmp(key, node_k) )
1580 {
1581 node = left(node); // Search value is smaller: follow left branch.
1582 }
1583 else if ( cmp(node_k, key) )
1584 {
1585 node = right(node); // Search value is larger: follow right branch.
1586 }
1587 else
1588 {
1589 return node; // Neither less-than holds => keys are equal.
1590 }
1591 }
1592 return const_cast<node_type *>(header_); // Header sentinel denotes "not found".
1593 }
1594
1595 template <class K>
1596 // Locate the first node whose key is not less than `key`. We walk down the
1597 // tree, tracking the last node that satisfied the lower-bound predicate so
1598 // we can return it even when the search terminates via a nil sentinel.
1599 node_type *lower_bound_node(K const &key) const
1600 {
1601 node_type *node = const_cast<node_type *>(root());
1602 // Handle headerless case: return the sentinel (which is nullptr here)
1603 if ( node == nullptr )
1604 return node;
1605 node_type *result = const_cast<node_type *>(header_);
1606 const auto cmp = comp();
1607 while ( !is_nil(node) )
1608 {
1609 if ( !cmp(node_key(node), key) )
1610 {
1611 result = node; // Candidate is >= key; remember it...
1612 node = left(node); // ...and look for an even smaller one.
1613 }
1614 else
1615 {
1616 node = right(node); // Candidate is < key; discard and go right.
1617 }
1618 }
1619 return result;
1620 }
1621
1622 template <class K>
1623 // Locate the first node whose key compares strictly greater than `key`.
1624 // The algorithm mirrors lower_bound_node(), changing only the branch that
1625 // determines when we capture a candidate result.
1626 node_type *upper_bound_node(K const &key) const
1627 {
1628 node_type *node = const_cast<node_type *>(root());
1629 // Handle headerless case: return the sentinel (which is nullptr here)
1630 if ( node == nullptr )
1631 return node;
1632 node_type *result = const_cast<node_type *>(header_);
1633 const auto cmp = comp();
1634 while ( !is_nil(node) )
1635 {
1636 if ( cmp(key, node_key(node)) )
1637 {
1638 result = node; // Found a key strictly greater than target.
1639 node = left(node); // Check if there's an even smaller qualifying key.
1640 }
1641 else
1642 {
1643 node = right(node); // Current key <= target; continue searching right.
1644 }
1645 }
1646 return result;
1647 }
1648
1649 // ---------------------------------------------------------------------------
1650 // Red-black tree algorithms and utilities: insertion.
1651 // ---------------------------------------------------------------------------
1652
1653 // Determine comparator equality by confirming neither side is strictly less
1654 // than the other. This is the STL idiom that works with transparent
1655 // comparators and avoids requiring operator== on the key type.
1656 bool keys_equal(key_type const &lhs, key_type const &rhs) const
1657 {
1658 const auto cmp = comp();
1659 return !cmp(lhs, rhs) && !cmp(rhs, lhs);
1660 }
1661
1662 // Heterogeneous version: compare key_type vs K when comparator supports it.
1663 template <class K, class = enable_hetero_eq<K>>
1664 bool keys_equal(key_type const &lhs, K const &rhs) const
1665 {
1666 const auto cmp = comp();
1667 return !cmp(lhs, rhs) && !cmp(rhs, lhs);
1668 }
1669
1670 // Walk the tree looking for where a new key should be inserted. We bubble
1671 // down while remembering the would-be parent and which side we should attach
1672 // to. If we encounter an equivalent key we return that node so the caller can
1673 // signal "already present". Otherwise we fall through with nullptr so the
1674 // caller inserts under `parent` on the indicated side.
1675 // Works for both key_type and heterogeneous K.
1676 template <class K>
1677 node_type *find_insert_position(K const &key, node_type *&parent_node, bool &go_left)
1678 {
1679 node_type *node = root();
1680 parent_node = header_;
1681 go_left = true;
1682 const auto cmp = comp();
1683 while ( !is_nil(node) )
1684 {
1685 parent_node = node; // Track the node we just visited.
1686 key_type const &node_k = node_key(node);
1687 if ( cmp(key, node_k) )
1688 {
1689 node = left(node); // Descend left for smaller target key...
1690 go_left = true; // ...and remember we want to attach left.
1691 }
1692 else if ( cmp(node_k, key) )
1693 {
1694 node = right(node); // Larger target key => go right instead.
1695 go_left = false; // New node would be the right child.
1696 }
1697 else
1698 {
1699 return node; // Equivalent key found; caller treats as duplicate.
1700 }
1701 }
1702 return nullptr; // Fell off the tree: caller should insert at parent.
1703 }
1704
1705 // Overload used by the hint-aware insertion path. We reuse the core search
1706 // logic above but surface the found node through an out parameter to keep
1707 // the call sites uniform.
1708 template <class K>
1709 node_type *find_insert_position(
1710 K const &key,
1711 node_type *&parent_node,
1712 bool &go_left,
1713 node_type *&existing)
1714 {
1715 existing = find_insert_position(key, parent_node, go_left);
1716 return existing;
1717 }
1718
1719 // Validate whether an iterator hint gives us immediate placement. The
1720 // standard allows us to insert in O(1) when the hint is correct, so we check
1721 // the neighboring nodes relative to the hint and either produce the parent
1722 // slot, identify an existing key, or fall back to the general insertion
1723 // search. Any mismatch returns false so the caller can retry the slow path.
1724 template <class K>
1725 bool try_hint_insert(
1726 K const &key,
1727 const_iterator hint,
1728 node_type *&parent_node,
1729 bool &go_left,
1730 node_type *&existing)
1731 {
1732 node_type *hint_node = hint.node_;
1733 if ( hint_node == nullptr )
1734 return false;
1735
1736 parent_node = header_;
1737 go_left = true;
1738 existing = nullptr;
1739
1740 if ( empty() )
1741 {
1742 parent_node = header_; // Empty tree: new node becomes root.
1743 return true;
1744 }
1745
1746 const auto cmp = comp();
1747 if ( hint_node == header_ )
1748 {
1749 node_type *max_node = right(header_);
1750 if ( is_nil(max_node) )
1751 {
1752 parent_node = header_; // No max yet, so new node becomes root.
1753 return true;
1754 }
1755 key_type const &max_key = node_key(max_node);
1756 if ( cmp(max_key, key) )
1757 {
1758 parent_node = max_node; // Hint is end(), key is greater than max...
1759 go_left = false; // ...so attach to the right of current max.
1760 return true;
1761 }
1762 if ( keys_equal(max_key, key) )
1763 {
1764 existing = max_node; // Exact match found at the cached max.
1765 return true;
1766 }
1767 return false;
1768 }
1769
1770 key_type const &hint_key = node_key(hint_node);
1771 if ( keys_equal(hint_key, key) )
1772 {
1773 existing = hint_node; // Hint points exactly at the element we need.
1774 return true;
1775 }
1776
1777 if ( cmp(key, hint_key) )
1778 {
1779 node_type *prev = predecessor(hint_node);
1780 if ( prev == header_ || cmp(node_key(prev), key) )
1781 {
1782 if ( is_nil(left(hint_node)) )
1783 {
1784 parent_node = hint_node; // Hint is the first element greater than key...
1785 go_left = true; // ...and left slot is empty, so insert there.
1786 return true;
1787 }
1788 if ( prev != header_ && is_nil(right(prev)) )
1789 {
1790 parent_node = prev; // Otherwise hook on the right of the predecessor.
1791 go_left = false;
1792 return true;
1793 }
1794 }
1795 return false;
1796 }
1797
1798 node_type *next = successor(hint_node);
1799 if ( next == header_ || cmp(key, node_key(next)) )
1800 {
1801 if ( is_nil(right(hint_node)) )
1802 {
1803 parent_node = hint_node; // Key sits between hint and successor, attach right.
1804 go_left = false;
1805 return true;
1806 }
1807 if ( next != header_ && is_nil(left(next)) )
1808 {
1809 parent_node = next; // Or attach as successor's left child.
1810 go_left = true;
1811 return true;
1812 }
1813 }
1814 return false;
1815 }
1816
1817 template <class Builder>
1818 // Materialize a brand-new node under `parent` on the requested side and then
1819 // rebalance the tree. The Builder functor constructs the Value directly in
1820 // the node storage so we can support piecewise construction just like
1821 // std::map::emplace().
1822 qpair<iterator, bool> emplace_at(node_type *parent_node, bool go_left, Builder &&builder)
1823 {
1824 node_type *new_node = node_new(); // Allocate a blank node before construction.
1825 try
1826 {
1827 builder(value_addr(new_node)); // Placement-new the Value via the builder.
1828 set_is_nil(new_node, false); // Mark as material now that the Value exists.
1829 }
1830 catch ( ... )
1831 {
1832 node_delete(new_node); // Construction failed: release the raw node.
1833 throw;
1834 }
1835 set_parent(new_node, parent_node); // Hook into the tree structure.
1836 set_left(new_node, header_); // Nil children until rotations wire real ones.
1837 set_right(new_node, header_);
1838 set_color(new_node, Color::Red); // New nodes start red; fixup may recolor.
1839
1840 if ( parent_node == header_ )
1841 {
1842 set_parent(header_, new_node); // Tree was empty: new node becomes root...
1843 set_left(header_, new_node); // ...and both extrema point at it.
1844 set_right(header_, new_node);
1845 set_color(new_node, Color::Black); // Root must be black.
1846 set_parent(new_node, header_); // Root's parent is always the header sentinel.
1847 }
1848 else
1849 {
1850 if ( go_left )
1851 {
1852 set_left(parent_node, new_node);
1853 // If we attached as the left child of the current leftmost, we have a new leftmost.
1854 if ( parent_node == left(header_) )
1855 set_left(header_, new_node);
1856 }
1857 else
1858 {
1859 set_right(parent_node, new_node);
1860 // Symmetrically for rightmost.
1861 if ( parent_node == right(header_) )
1862 set_right(header_, new_node);
1863 }
1864 insert_fixup(new_node); // Restore red-black invariants.
1865 }
1866
1867 ++node_count_; // Container now owns one more element.
1868 return { iterator(new_node), true };
1869 }
1870
1871 // -----------------------------------------------------------------------------
1872 // insert_fixup()
1873 // -----------------------------------------------------------------------------
1874 // Summary (outer-line target shape after fixup, conceptually):
1875 //
1876 // P (black)
1877 // . .
1878 // ... ...
1879 //
1880 // We iteratively repair a "double red" at (node, parent).
1881 // If uncle is red: recolor and bubble up.
1882 // If uncle is black: convert inner triangle to outer line (small rotate),
1883 // then rotate grand to make parent the local root and recolor.
1884 // -----------------------------------------------------------------------------
1885 void insert_fixup(node_type *node) noexcept
1886 {
1887 while ( !is_nil(parent(node)) && color(parent(node)) == Color::Red )
1888 {
1889 node_type *parent_node = parent(node);
1890 node_type *grand = parent(parent_node); // parent is red => grand exists
1891
1892 // -------------------------------------------------------------------------
1893 // CASE GROUP A: parent is left child of grand
1894 // -------------------------------------------------------------------------
1895 if ( parent_node == left(grand) )
1896 {
1897 node_type *uncle = right(grand); // opposite side
1898
1899 // A1) RED UNCLE: recolor and bubble up
1900 //
1901 // Before:
1902 // G(black)
1903 // . .
1904 // P(red) U(red)
1905 // .
1906 // N(red)
1907 //
1908 // After recolor (no rotations):
1909 // G(red)
1910 // . .
1911 // P(black) U(black)
1912 // .
1913 // N(red)
1914 if ( node_color(uncle) == Color::Red )
1915 {
1916 set_color(parent_node, Color::Black);
1917 set_color(uncle, Color::Black);
1918 set_color(grand, Color::Red);
1919 node = grand;
1920 continue;
1921 }
1922
1923 // A2) BLACK UNCLE: two subcases
1924 //
1925 // A2a) INNER TRIANGLE (node is right child): rotate_left at parent
1926 //
1927 // Before:
1928 // G(black)
1929 // . .
1930 // P(red) U(black)
1931 // .
1932 // N(red)
1933 //
1934 // After rotate_left(parent_node):
1935 // G(black)
1936 // . .
1937 // N(red) U(black)
1938 // .
1939 // P(red)
1940 if ( node == right(parent_node) )
1941 {
1942 node = parent_node;
1943 rotate_left(node);
1944 parent_node = parent(node); // refresh
1945 grand = parent(parent_node);
1946 }
1947
1948 // A2b) OUTER LINE (node is left child, or after A2a):
1949 // rotate_right at grand, recolor parent black, grand red
1950 //
1951 // Before:
1952 // G(black)
1953 // . .
1954 // P(red) U(black)
1955 // .
1956 // N(red)
1957 //
1958 // After rotate_right(grand):
1959 // P(black)
1960 // . .
1961 // N(red) G(red)
1962 // .
1963 // U(black)
1964 set_color(parent_node, Color::Black);
1965 set_color(grand, Color::Red);
1966 rotate_right(grand);
1967 }
1968 // -------------------------------------------------------------------------
1969 // CASE GROUP B: parent is right child of grand (mirror of A)
1970 // -------------------------------------------------------------------------
1971 else
1972 {
1973 node_type *uncle = left(grand); // opposite side
1974
1975 // B1) RED UNCLE: recolor and bubble up
1976 //
1977 // Before:
1978 // G(black)
1979 // . .
1980 // U(red) P(red)
1981 // .
1982 // N(red)
1983 //
1984 // After recolor (no rotations):
1985 // G(red)
1986 // . .
1987 // U(black) P(black)
1988 // .
1989 // N(red)
1990 if ( node_color(uncle) == Color::Red )
1991 {
1992 set_color(parent_node, Color::Black);
1993 set_color(uncle, Color::Black);
1994 set_color(grand, Color::Red);
1995 node = grand;
1996 continue;
1997 }
1998
1999 // B2) BLACK UNCLE: two subcases
2000 //
2001 // B2a) INNER TRIANGLE (node is left child): rotate_right at parent
2002 //
2003 // Before:
2004 // G(black)
2005 // . .
2006 // U(black) P(red)
2007 // .
2008 // N(red)
2009 //
2010 // After rotate_right(parent_node):
2011 // G(black)
2012 // . .
2013 // U(black) N(red)
2014 // .
2015 // P(red)
2016 if ( node == left(parent_node) )
2017 {
2018 node = parent_node;
2019 rotate_right(node);
2020 parent_node = parent(node); // refresh
2021 grand = parent(parent_node);
2022 }
2023
2024 // B2b) OUTER LINE (node is right child, or after B2a):
2025 // rotate_left at grand, recolor parent black, grand red
2026 //
2027 // Before:
2028 // G(black)
2029 // . .
2030 // U(black) P(red)
2031 // .
2032 // N(red)
2033 //
2034 // After rotate_left(grand):
2035 // P(black)
2036 // . .
2037 // G(red) N(red)
2038 // .
2039 // U(black)
2040 set_color(parent_node, Color::Black);
2041 set_color(grand, Color::Red);
2042 rotate_left(grand);
2043 }
2044 }
2045
2046 // Root must be black
2047 if ( !is_nil(parent(header_)) )
2048 set_color(parent(header_), Color::Black);
2049 }
2050
2051 template <class Builder>
2052 // Entry point for emplacement without a hint. We either return an existing
2053 // node when the key already lives in the tree or delegate to emplace_at() to
2054 // create and balance a new node.
2055 qpair<iterator, bool> lazy_emplace_impl(key_type const &key, Builder &&builder)
2056 {
2057 if ( header_ == nullptr || node_count_ == 0 )
2058 return emplace_into_empty_tree(std::forward<Builder>(builder));
2059
2060 node_type *parent_node = header_;
2061 bool go_left = true;
2062 node_type *existing = this->find_insert_position(key, parent_node, go_left);
2063 if ( existing != nullptr )
2064 return { iterator(existing), false }; // Key already present; no insertion.
2065 return emplace_at(parent_node, go_left, std::forward<Builder>(builder));
2066 }
2067
2068 // Heterogeneous key overload: enabled only when comparator supports it.
2069 template <class K, class Builder, class = std::enable_if_t<cmp_bidir_v<Compare, key_type, K>>>
2070 qpair<iterator, bool> lazy_emplace_impl(K const &key, Builder &&builder)
2071 {
2072 if ( header_ == nullptr || node_count_ == 0 )
2073 return emplace_into_empty_tree(std::forward<Builder>(builder));
2074
2075 node_type *parent_node = header_;
2076 bool go_left = true;
2077 node_type *existing = this->find_insert_position(key, parent_node, go_left);
2078 if ( existing != nullptr )
2079 return { iterator(existing), false };
2080 return emplace_at(parent_node, go_left, std::forward<Builder>(builder));
2081 }
2082
2083 template <class Builder>
2084 // Hint-aware emplacement. We first attempt the constant-time placement by
2085 // validating that the hint is adjacent to the target location; failing that
2086 // we fall back to the general lazy_emplace_impl() search.
2087 qpair<iterator, bool> lazy_emplace_with_hint(
2088 key_type const &key, const_iterator hint, Builder &&builder)
2089 {
2090 if ( header_ == nullptr || node_count_ == 0 )
2091 return emplace_into_empty_tree(std::forward<Builder>(builder));
2092
2093 node_type *parent_node = header_;
2094 bool go_left = true;
2095 node_type *existing = nullptr;
2096 if ( hint.node_ != nullptr
2097 && try_hint_insert(key, hint, parent_node, go_left, existing) )
2098 {
2099 if ( existing != nullptr )
2100 return { iterator(existing), false }; // Hint pointed to duplicate key.
2101 return emplace_at(parent_node, go_left, std::forward<Builder>(builder)); // Insert using O(1) slot.
2102 }
2103 return lazy_emplace_impl(key, std::forward<Builder>(builder)); // Fallback to regular search.
2104 }
2105
2106 template <class K, class Builder, class = std::enable_if_t<cmp_bidir_v<Compare, key_type, K>>>
2107 qpair<iterator, bool> lazy_emplace_with_hint(
2108 K const &key, const_iterator hint, Builder &&builder)
2109 {
2110 if ( header_ == nullptr || node_count_ == 0 )
2111 return emplace_into_empty_tree(std::forward<Builder>(builder));
2112
2113 node_type *parent_node = header_;
2114 bool go_left = true;
2115 node_type *existing = nullptr;
2116 if ( hint.node_ != nullptr
2117 && try_hint_insert(key, hint, parent_node, go_left, existing) )
2118 {
2119 if ( existing != nullptr )
2120 return { iterator(existing), false };
2121 return emplace_at(parent_node, go_left, std::forward<Builder>(builder));
2122 }
2123 return lazy_emplace_impl(key, std::forward<Builder>(builder));
2124 }
2125
2126 template <class Builder>
2127 qpair<iterator, bool> emplace_into_empty_tree(Builder &&builder)
2128 {
2129 ensure_header();
2130 node_type *parent_node = header_;
2131 bool go_left = true;
2132 return emplace_at(parent_node, go_left, std::forward<Builder>(builder));
2133 }
2134
2135 // ---------------------------------------------------------------------------
2136 // Red-black tree algorithms and utilities: removal.
2137 // ---------------------------------------------------------------------------
2138
2139 // -----------------------------------------------------------------------------
2140 // transplant(u, v)
2141 // -----------------------------------------------------------------------------
2142 // Replace subtree rooted at 'u' with subtree rooted at 'v'.
2143 // Fixes parent pointers; treats 'header_' as pseudo-parent of the root.
2144 //
2145 // Cases:
2146 // - u was root: header_->parent becomes v (or header_ if v is nil)
2147 // - u was left child: u->parent_node->left = v
2148 // - u was right child: u->parent_node-> right = v
2149 // - if v is not nil: v->parent = u->parent
2150 // -----------------------------------------------------------------------------
2151 void transplant(node_type *u, node_type *v) noexcept
2152 {
2153 if ( parent(u) == header_ )
2154 set_parent(header_, is_nil(v) ? header_ : v);
2155 else if ( u == left(parent(u)) )
2156 set_left(parent(u), v);
2157 else
2158 set_right(parent(u), v);
2159
2160 if ( !is_nil(v) )
2161 set_parent(v, parent(u));
2162 }
2163
2164 // -----------------------------------------------------------------------------
2165 // erase_node()
2166 // -----------------------------------------------------------------------------
2167 // Delete 'node' while preserving BST ordering and RB invariants.
2168 // We consider the standard 3 structural cases. We remember the color of the
2169 // physical node that gets removed ('original'). If that color was black,
2170 // we run erase_fixup() starting at 'x' with its current parent 'x_parent'.
2171 // -----------------------------------------------------------------------------
2172 void erase_node(node_type *node)
2173 {
2174 // Handle headerless case. This should only arise if somebody did
2175 // `t.erase(t.begin())` or something, which is an error on an empty tree.
2176 // But, might as well not segfault, right?
2177 if ( node == nullptr )
2178 return;
2179 node_type *y = node; // physical node that will be removed
2180 node_type *x = header_; // child that replaces y (may be header_)
2181 node_type *x_parent = header_; // parent to continue fixup from if x is header_
2182 Color original = color(y);
2183
2184 // Case 1: no left child -> splice in right child
2185 //
2186 // Before:
2187 // node
2188 // . .
2189 // nil R
2190 //
2191 // After:
2192 // R (R may be header_)
2193 //
2194 if ( is_nil(left(node)) )
2195 {
2196 x = right(node);
2197 x_parent = parent(node);
2198 transplant(node, right(node));
2199 }
2200 // Case 2: no right child -> splice in left child
2201 //
2202 // Before:
2203 // node
2204 // . .
2205 // L nil
2206 //
2207 // After:
2208 // L
2209 //
2210 else if ( is_nil(right(node)) )
2211 {
2212 x = left(node);
2213 x_parent = parent(node);
2214 transplant(node, left(node));
2215 }
2216 // Case 3: two children -> use in-order successor 'y'
2217 //
2218 // Find y = minimum(node->right). y has no left child.
2219 // We remove y from its spot, and put y where 'node' was.
2220 //
2221 // Before (y is leftmost in node->right):
2222 // node
2223 // . .
2224 // L R
2225 // .
2226 // y
2227 // .
2228 // x (x is y->right, possibly nil)
2229 //
2230 // After splicing y out and moving it up to node's position:
2231 // y
2232 // . .
2233 // L R
2234 //
2235 else
2236 {
2237 y = minimum(right(node));
2238 original = color(y);
2239 x = right(y); // y has at most one right child
2240
2241 if ( parent(y) == node )
2242 x_parent = y; // x's parent will be y after transplant
2243 else
2244 {
2245 // Splice y out of its current position:
2246 //
2247 // parent(y)
2248 // .
2249 // y
2250 // .
2251 // x
2252 //
2253 // becomes:
2254 //
2255 // parent(y)
2256 // .
2257 // x
2258 //
2259 transplant(y, right(y));
2260
2261 // Move node->right under y:
2262 set_right(y, right(node));
2263 if ( !is_nil(right(y)) )
2264 set_parent(right(y), y);
2265
2266 x_parent = parent(y); // track parent for fixup when x is nil
2267 }
2268
2269 // Replace 'node' by 'y' at node's position
2270 //
2271 // Before:
2272 // ... -> node
2273 //
2274 // After:
2275 // ... -> y
2276 //
2277 transplant(node, y);
2278
2279 // Attach node->left under y
2280 set_left(y, left(node));
2281 if ( !is_nil(left(y)) )
2282 set_parent(left(y), y);
2283
2284 // y takes node's original color
2285 set_color(y, color(node));
2286 }
2287
2288 // Destroy and free the removed node
2289 destroy_value(node);
2290 node_delete(node);
2291 --node_count_;
2292
2293 // If we physically removed a black node, we may have broken black-height
2294 if ( original == Color::Black )
2295 erase_fixup(x, x_parent);
2296
2297 // Refresh header extrema or reset header if empty
2298 if ( node_count_ == 0 )
2299 init_header();
2300 else
2301 {
2302 set_left(header_, minimum(parent(header_)));
2303 set_right(header_, maximum(parent(header_)));
2304 }
2305 }
2306
2307 // -----------------------------------------------------------------------------
2308 // erase_fixup(x, parent)
2309 // -----------------------------------------------------------------------------
2310 // Repair red-black properties after removing a black node.
2311 // Loop while x is black and not the root. Consider sibling w and apply the
2312 // 4 textbook cases (and their mirror).
2313 // Important: `x` may be `header_`, because we represent nil children by the
2314 // shared header sentinel. node_color()/left_color()/right_color() treat any
2315 // nil (header_) as black and never dereference its children, so erase_fixup()
2316 // doesn't need separate header-specific branches.
2317 // -----------------------------------------------------------------------------
2318 void erase_fixup(node_type *x, node_type *parent_node) noexcept
2319 {
2320 while ( x != parent(header_) && node_color(x) == Color::Black )
2321 {
2322 // ----------------------------- LEFT SIDE ---------------------------------
2323 // x is the left child; sibling w = parent()->right
2324 if ( x == left(parent_node) )
2325 {
2326 node_type *w = right(parent_node);
2327
2328 // Case 1 (left): w is red -> rotate_left(parent_node)
2329 //
2330 // Before:
2331 // parent(B)
2332 // . .
2333 // x(B) w(R)
2334 //
2335 // After rotate_left(parent_node):
2336 // w(B)
2337 // . .
2338 // parent(R) ...
2339 // .
2340 // x(B)
2341 if ( node_color(w) == Color::Red )
2342 {
2343 set_color(w, Color::Black);
2344 set_color(parent_node, Color::Red);
2345 rotate_left(parent_node);
2346 w = right(parent_node); // new sibling
2347 }
2348
2349 Color w_left = left_color(w); // black if nil
2350 Color w_right = right_color(w); // black if nil
2351
2352 // Case 2 (left): w's children are both black -> paint w red, move up
2353 //
2354 // parent(?)
2355 // . .
2356 // x(B) w(B)
2357 // . .
2358 // B B
2359 if ( w_left == Color::Black && w_right == Color::Black )
2360 {
2361 if ( !is_nil(w) )
2362 set_color(w, Color::Red);
2363
2364 x = parent_node;
2365 parent_node = parent(parent_node);
2366 }
2367 else
2368 {
2369 // Case 3 (left): w_right is black, w_left is red
2370 // Convert to case 4 shape by rotate_right(w)
2371 //
2372 // Before:
2373 // parent
2374 // . .
2375 // x w(B)
2376 // .
2377 // R
2378 //
2379 // After rotate_right(w):
2380 // parent
2381 // . .
2382 // x R
2383 // .
2384 // w(B)
2385 if ( w_right == Color::Black )
2386 {
2387 if ( !is_nil(w) )
2388 {
2389 if ( !is_nil(left(w)) )
2390 set_color(left(w), Color::Black);
2391
2392 set_color(w, Color::Red);
2393 rotate_right(w);
2394 }
2395 w = right(parent_node);
2396 }
2397
2398 // Case 4 (left): w_right is red -> rotate_left(parent_node), recolor
2399 //
2400 // Before:
2401 // parent(Pcol)
2402 // . .
2403 // x w(R or B)
2404 // .
2405 // R
2406 //
2407 // After rotate_left(parent_node):
2408 // w(Pcol)
2409 // . .
2410 // parent(B) ...
2411 // .
2412 // x(B)
2413 if ( !is_nil(w) )
2414 set_color(w, color(parent_node));
2415
2416 set_color(parent_node, Color::Black);
2417 if ( !is_nil(right(w)) )
2418 set_color(right(w), Color::Black);
2419
2420 rotate_left(parent_node);
2421
2422 // Done: force exit
2423 x = parent(header_);
2424 parent_node = header_;
2425 }
2426 }
2427 // ---------------------------- RIGHT SIDE ---------------------------------
2428 // x is the right child; sibling w = parent()->left (mirror of left side)
2429 else
2430 {
2431 node_type *w = left(parent_node);
2432
2433 // Case 1 (right): w is red -> rotate_right(parent_node)
2434 //
2435 // Before:
2436 // parent(B)
2437 // . .
2438 // w(R) x(B)
2439 //
2440 // After rotate_right(parent_node):
2441 // w(B)
2442 // . .
2443 // ... parent(R)
2444 // .
2445 // x(B)
2446 if ( node_color(w) == Color::Red )
2447 {
2448 set_color(w, Color::Black);
2449 set_color(parent_node, Color::Red);
2450 rotate_right(parent_node);
2451 w = left(parent_node); // new sibling
2452 }
2453
2454 Color w_left = left_color(w); // black if nil
2455 Color w_right = right_color(w); // black if nil
2456
2457 // Case 2 (right): w's children both black -> paint w red, move up
2458 //
2459 // parent(?)
2460 // . .
2461 // w(B) x(B)
2462 // . .
2463 // B B
2464 if ( w_left == Color::Black && w_right == Color::Black )
2465 {
2466 if ( !is_nil(w) )
2467 set_color(w, Color::Red);
2468
2469 x = parent_node;
2470 parent_node = parent(parent_node);
2471 }
2472 else
2473 {
2474 // Case 3 (right): w_left is black, w_right is red
2475 // Convert to case 4 shape by rotate_left(w)
2476 //
2477 // Before:
2478 // parent
2479 // . .
2480 // w(B) x
2481 // .
2482 // R
2483 //
2484 // After rotate_left(w):
2485 // parent
2486 // . .
2487 // R x
2488 // .
2489 // w(B)
2490 if ( w_left == Color::Black )
2491 {
2492 if ( !is_nil(w) )
2493 {
2494 if ( !is_nil(right(w)) )
2495 set_color(right(w), Color::Black);
2496
2497 set_color(w, Color::Red);
2498 rotate_left(w);
2499 }
2500 w = left(parent_node);
2501 }
2502
2503 // Case 4 (right): w_left is red -> rotate_right(parent_node), recolor
2504 //
2505 // Before:
2506 // parent(Pcol)
2507 // . .
2508 // w(R or B) x
2509 // .
2510 // R
2511 //
2512 // After rotate_right(parent_node):
2513 // w(Pcol)
2514 // . .
2515 // ... parent(B)
2516 // .
2517 // x(B)
2518 if ( !is_nil(w) )
2519 set_color(w, color(parent_node));
2520
2521 set_color(parent_node, Color::Black);
2522 if ( !is_nil(left(w)) )
2523 set_color(left(w), Color::Black);
2524
2525 rotate_right(parent_node);
2526
2527 // Done: force exit
2528 x = parent(header_);
2529 parent_node = header_;
2530 }
2531 }
2532 }
2533
2534 // Ensure x and root are black on exit
2535 if ( !is_nil(x) )
2536 set_color(x, Color::Black);
2537
2538 if ( !is_nil(parent(header_)) )
2539 set_color(parent(header_), Color::Black);
2540 }
2541
2542 // ---------------------------------------------------------------------------
2543 // Allocation and deallocation of nodes.
2544 // ---------------------------------------------------------------------------
2545
2546 // Acquire storage for a fresh node via the allocator policy and construct the
2547 // bookkeeping fields in place. The Value payload is filled later by callers.
2548 node_type *node_new()
2549 {
2550 node_alloc a = make_node_alloc();
2551 // The allocator (not `AllocPolicy::allocate`) throws on allocation failure.
2552 node_type *mem = node_traits::allocate(a, 1);
2553 try
2554 {
2555 return ::new (mem) node_type(); // Value-initialize bookkeeping fields.
2556 }
2557 catch ( ... )
2558 {
2559 node_traits::deallocate(a, mem, 1);
2560 throw;
2561 }
2562 }
2563
2564 // Complement to node_new(): destroy the node object (which calls the Value
2565 // destructor when present) and return its memory to the allocator policy.
2566 void node_delete(node_type *p) noexcept
2567 {
2568 if ( p == nullptr )
2569 return;
2570 p->~node_type(); // Run destructor to release Value, if any.
2571 node_alloc a = make_node_alloc();
2572 node_traits::deallocate(a, p, 1); // Return memory to allocator.
2573 }
2574
2575 // Invariants for header_ / "nil":
2576 //
2577 // - When header_ == nullptr, the tree is empty and no sentinel node has
2578 // been allocated yet. All operations must treat this as an empty tree
2579 // and must not dereference header_.
2580 //
2581 // - When header_ != nullptr, it is the unique sentinel node with
2582 // _is_nil() == 1. (Transiently, nodes being destroyed may also be
2583 // marked nil before deallocation.)
2584 //
2585 // - Every "nil" child pointer in the tree is represented by `header_`
2586 // (we do not allocate per-node nils). In the header-less empty state,
2587 // we never store or follow any child pointers.
2588 //
2589 // - When the tree is empty and header_ != nullptr:
2590 // parent(header_) == header_
2591 // left(header_) == header_
2592 // right(header_) == header_
2593 //
2594 // - When the tree is non-empty:
2595 // parent(header_) == root
2596 // left(header_) == minimum(root)
2597 // right(header_) == maximum(root)
2598 //
2599 // Even in the non-empty case, algorithms treat `header_` as the
2600 // unique nil sentinel: is_nil(header_) is true and helpers like
2601 // node_color(), left_color(), right_color() special-case nil nodes
2602 // as black. This lets us reuse header_ as both the nil leaf and the
2603 // (root, min, max) cache without extra nodes.
2604 node_type *header_ = nullptr;
2605 size_type node_count_ = 0;
2606};
2607
2608// Declaration of qset, a set based on qtree.
2609template <class Key, class Compare, class AllocPolicy, bool PackNode = true>
2611
2612// qmap uses composition rather than inheritance, for ABI reasons. This
2613// requires us to reimplement the whole public API of qtree that we wish to
2614// expose as small wrappers that forward to the internal qtree instance.
2615template <class Key, class T, class Compare, class AllocPolicy, bool PackNode = true>
2616class qmap
2617{
2618 using Tree = qtree < qpair<const Key, T>, Compare, AllocPolicy, PackNode,
2620
2621 friend class qtree_tester;
2622
2623public:
2624 // ------------------------ public types ------------------------
2625 using key_type = Key;
2626 using mapped_type = T;
2628 using size_type = typename Tree::size_type;
2630 using key_compare = Compare;
2632 using allocator_policy_type = AllocPolicy;
2633
2637 using const_pointer = value_type const *;
2638
2639 using iterator = typename Tree::iterator;
2644
2645 // This thing defines operator new/delete for the qmap type, such that if
2646 // you were to allocate a qmap via operator new, or delete a qmap via
2647 // operator delete, the allocation would go through the AllocPolicy.
2649
2650 // ------------------------ constructors / assignment ------------------------
2651 qmap() noexcept : tree_() {}
2652
2653 qmap(std::initializer_list<value_type> init) : tree_()
2654 {
2655 tree_.insert(init.begin(), init.end());
2656 }
2657
2658 qmap(qmap const &other) : tree_(other.tree_) {}
2659
2660 qmap(qmap &&other) noexcept
2661 : tree_(std::move(other.tree_))
2662 {
2663 }
2664
2665 ~qmap() = default;
2666
2667 qmap &operator=(qmap const &other)
2668 {
2669 tree_ = other.tree_;
2670 return *this;
2671 }
2672
2673 qmap &operator=(qmap &&other) noexcept
2674 {
2675 tree_ = std::move(other.tree_);
2676 return *this;
2677 }
2678
2679 void swap(qmap &other) noexcept(noexcept(tree_.swap(other.tree_)))
2680 {
2681 tree_.swap(other.tree_);
2682 }
2683 friend void swap(qmap &a, qmap &b) noexcept(noexcept(a.swap(b))) { a.swap(b); }
2684
2685 // Here we duplicate the public API of qtree, in order to avoid ABI issues
2686 // that could arise from inheritance.
2687
2688 // ------------------------ iterators ------------------------
2689 iterator begin() noexcept { return tree_.begin(); }
2690 const_iterator begin() const noexcept { return tree_.begin(); }
2691 const_iterator cbegin() const noexcept { return tree_.cbegin(); }
2692
2693 iterator end() noexcept { return tree_.end(); }
2694 const_iterator end() const noexcept { return tree_.end(); }
2695 const_iterator cend() const noexcept { return tree_.cend(); }
2696
2699 const_reverse_iterator crbegin() const noexcept { return rbegin(); }
2700
2703 const_reverse_iterator crend() const noexcept { return rend(); }
2704
2705 // ------------------------ capacity ------------------------
2706 bool empty() const noexcept { return tree_.empty(); }
2707 size_type size() const noexcept { return tree_.size(); }
2708
2709 // --------- heterogenous comparator helpers ---------------
2710 template <class K>
2711 using enable_hetero_eq = typename Tree::template enable_hetero_eq<K>;
2712
2713 template <class K>
2714 using enable_hetero_lb = typename Tree::template enable_hetero_lb<K>;
2715
2716 template <class K>
2717 using enable_hetero_ub = typename Tree::template enable_hetero_ub<K>;
2718
2719 template <class K>
2720 using enable_hetero_eqrange = typename Tree::template enable_hetero_eqrange<K>;
2721
2722 // ------------------------ lookup (exact key type) ------------------------
2723 // find
2724 [[nodiscard]] iterator find(key_type const &key) { return tree_.find(key); }
2725 [[nodiscard]] const_iterator find(key_type const &key) const { return tree_.find(key); }
2726
2727 template <class K, class = enable_hetero_eq<K>>
2728 [[nodiscard]] iterator find(K const &k) { return tree_.find(k); }
2729
2730 template <class K, class = enable_hetero_eq<K>>
2731 [[nodiscard]] const_iterator find(K const &k) const { return tree_.find(k); }
2732
2733 // contains
2734 [[nodiscard]] bool contains(key_type const &key) const { return tree_.contains(key); }
2735
2736 template <class K, class = enable_hetero_eq<K>>
2737 [[nodiscard]] bool contains(K const &k) const { return tree_.contains(k); }
2738
2739 // count
2740 [[nodiscard]] size_type count(key_type const &key) const { return tree_.count(key); }
2741
2742 template <class K, class = enable_hetero_eq<K>>
2743 [[nodiscard]] size_type count(K const &k) const { return tree_.count(k); }
2744
2745 // lower_bound
2746 [[nodiscard]] iterator lower_bound(key_type const &key) { return tree_.lower_bound(key); }
2747 [[nodiscard]] const_iterator lower_bound(key_type const &key) const { return tree_.lower_bound(key); }
2748
2749 template <class K, class = enable_hetero_lb<K>>
2750 [[nodiscard]] iterator lower_bound(K const &k) { return tree_.lower_bound(k); }
2751
2752 template <class K, class = enable_hetero_lb<K>>
2753 [[nodiscard]] const_iterator lower_bound(K const &k) const { return tree_.lower_bound(k); }
2754
2755 // upper_bound
2756 [[nodiscard]] iterator upper_bound(key_type const &key) { return tree_.upper_bound(key); }
2757 [[nodiscard]] const_iterator upper_bound(key_type const &key) const { return tree_.upper_bound(key); }
2758
2759 template <class K, class = enable_hetero_ub<K>>
2760 [[nodiscard]] iterator upper_bound(K const &k) { return tree_.upper_bound(k); }
2761
2762 template <class K, class = enable_hetero_ub<K>>
2763 [[nodiscard]] const_iterator upper_bound(K const &k) const { return tree_.upper_bound(k); }
2764
2765 // equal_range
2767 {
2768 return tree_.equal_range(key);
2769 }
2771 {
2772 return tree_.equal_range(key);
2773 }
2774
2775 template <class K, class = enable_hetero_eqrange<K>>
2776 [[nodiscard]] qpair<iterator, iterator> equal_range(K const &k) { return tree_.equal_range(k); }
2777
2778 template <class K, class = enable_hetero_eqrange<K>>
2779 [[nodiscard]] qpair<const_iterator, const_iterator> equal_range(K const &k) const { return tree_.equal_range(k); }
2780
2781 // ------------------------ modifiers ------------------------
2782 void clear() noexcept { tree_.clear(); }
2783
2784 // insert
2785 template <class InputIt>
2786 void insert(InputIt first, InputIt last) { tree_.insert(first, last); }
2787
2788 void insert(std::initializer_list<value_type> init)
2789 {
2790 tree_.insert(init.begin(), init.end());
2791 }
2792
2793 qpair<iterator, bool> insert(value_type const &v) { return tree_.insert(v); }
2794 qpair<iterator, bool> insert(value_type &&v) { return tree_.insert(std::move(v)); }
2795
2797 {
2798 return tree_.insert(hint, v);
2799 }
2801 {
2802 return tree_.insert(hint, std::move(v));
2803 }
2804
2805 // erase
2806 iterator erase(const_iterator pos) { return tree_.erase(pos); }
2808 {
2809 return tree_.erase(const_iterator(pos));
2810 }
2812 {
2813 return tree_.erase(first, last);
2814 }
2815
2816 size_type erase(key_type const &key) { return tree_.erase(key); }
2817
2818 template <class K, class = enable_hetero_eq<K>>
2819 size_type erase(K const &k) { return tree_.erase(k); }
2820
2821 template <class Pred>
2822 size_type erase_if(Pred pred) { return tree_.erase_if(pred); }
2823
2824 // ------------------------ observers ------------------------
2825 key_compare key_comp() const noexcept { return tree_.key_comp(); }
2826 value_compare value_comp() const noexcept { return tree_.value_comp(); }
2827 allocator_type get_allocator() const noexcept { return tree_.get_allocator(); }
2828 size_type max_size() const noexcept { return tree_.max_size(); }
2829
2830 // ------------------------ element access ------------------------
2832 {
2833 auto it = find(key);
2834 if ( it == end() )
2835 throw std::out_of_range("qmap::at");
2836 return it->second;
2837 }
2838 mapped_type const &at(key_type const &key) const
2839 {
2840 auto it = find(key);
2841 if ( it == end() )
2842 throw std::out_of_range("qmap::at");
2843 return it->second;
2844 }
2845
2847 {
2848 // Insert default-constructed mapped_type if missing.
2849 return try_emplace(key).first->second;
2850 }
2851
2853 {
2854 // Same as above, but move the key when we actually insert.
2855 return try_emplace(std::move(key)).first->second;
2856 }
2857
2858 // ------------------------ emplacement ------------------------
2859 // Perfectly forwards to the underlying tree; duplicates are checked
2860 // via the KeySelector inside qtree, so mapped_type is only constructed on insert.
2861 template <class... Args>
2863 {
2864 return tree_.emplace(std::forward<Args>(args)...);
2865 }
2866
2867 template <class... Args>
2869 {
2870 return tree_.emplace_hint(hint, std::forward<Args>(args)...);
2871 }
2872
2873 // ------------------------ map-specific insertion ------------------------
2874 template <class... Args>
2875 // Exact-type lvalue key: probe with const&, construct mapped only on insert.
2876 // Overload #1: exact key_type lvalue
2878 {
2879 // long line because checkstyle
2880 return tree_.lazy_emplace_impl(key, [&](value_type *slot) { mapped_type m(std::forward<Args>(args)...); ::new ((void *)slot) value_type(key, std::move(m)); } );
2881 }
2882
2883 template <class... Args>
2884 // Exact-type rvalue key: probe by const& to same object, then move key once.
2885 // Overload #2: exact key_type rvalue (move key once)
2887 {
2888 // long line because checkstyle
2889 key_type const &key_ref = key; // probe without moving
2890 return tree_.lazy_emplace_impl(key_ref, [&](value_type *slot) { mapped_type m(std::forward<Args>(args)...); ::new ((void *)slot) value_type(std::move(key), std::move(m)); } );
2891 }
2892
2893 template <class K, class... Args, std::enable_if_t<cmp_bidir_v<key_compare, key_type, std::decay_t<K>>, int> = 0>
2894 // Heterogeneous key: comparator supports K vs key_type (transparent compare).
2895 // Overload #3: hetero K when comparator supports it (no key materialization on miss)
2897 {
2898 using Kdec = std::decay_t<K>;
2899 Kdec const &key_ref = k; // used both for lookup and lazy construction
2900
2901 // This uses qtree's heterogeneous lazy_emplace_impl(K const&, ...)
2902 // Long line because checkstyle
2903 return tree_.lazy_emplace_impl(key_ref, [&](value_type *slot) { mapped_type m(std::forward<Args>(args)...); ::new ((void *)slot) value_type(key_type(key_ref), std::move(m)); } );
2904 }
2905
2906 template <class K, class... Args, std::enable_if_t<std::is_constructible_v<key_type, K const &> && !cmp_bidir_v<key_compare, key_type, std::decay_t<K>>, int> = 0>
2907 // Convertible key (materialize once) when comparator does *not* support hetero K.
2908 // Overload #4: fallback for K constructible into key_type when comparator is not transparent
2910 {
2911 key_type kk(k);
2912 return try_emplace(std::move(kk), std::forward<Args>(args)...);
2913 }
2914
2915 template <class M>
2917 {
2918 auto r = tree_.lazy_emplace_impl(key, [&](value_type *slot)
2919 {
2920 mapped_type m(std::forward<M>(mapped));
2921 ::new ((void *)slot) value_type(key, std::move(m));
2922 } );
2923 if ( !r.second )
2924 r.first->second = std::forward<M>(mapped);
2925 return r;
2926 }
2927
2928 template <class M>
2930 {
2931 key_type const &key_ref = key; // probe without moving the key yet
2932 auto r = tree_.lazy_emplace_impl(key_ref, [&](value_type *slot)
2933 {
2934 mapped_type m(std::forward<M>(mapped));
2935 ::new ((void *)slot) value_type(std::move(key), std::move(m));
2936 } );
2937 if ( !r.second )
2938 r.first->second = std::forward<M>(mapped);
2939 return r;
2940 }
2941
2942 // Heterogeneous key insert_or_assign: comparator supports K vs key_type.
2943 template <class K, class M, std::enable_if_t<cmp_bidir_v<key_compare, key_type, std::decay_t<K>>, int> = 0>
2945 {
2946 using Kdec = std::decay_t<K>;
2947 Kdec const &key_ref = k;
2948 auto r = tree_.lazy_emplace_impl(key_ref, [&](value_type *slot)
2949 {
2950 mapped_type m(std::forward<M>(mapped));
2951 ::new ((void *)slot) value_type(key_type(key_ref), std::move(m));
2952 } );
2953 if ( !r.second )
2954 r.first->second = std::forward<M>(mapped);
2955 return r;
2956 }
2957
2958 template <class K, class M, std::enable_if_t<std::is_constructible_v<key_type, K const &> && !cmp_bidir_v<key_compare, key_type, std::decay_t<K>>, int> = 0>
2960 {
2961 key_type kk(k); // materialize once
2962 auto r = tree_.lazy_emplace_impl(kk, [&](value_type *slot)
2963 {
2964 mapped_type m(std::forward<M>(mapped));
2965 ::new ((void *)slot) value_type(std::move(kk), std::move(m));
2966 } );
2967 if ( !r.second )
2968 r.first->second = std::forward<M>(mapped);
2969 return r;
2970 }
2971
2972 // ------------------------ equality ------------------------
2973 friend bool operator==(qmap const &a, qmap const &b) { return a.tree_ == b.tree_; }
2974 friend bool operator!=(qmap const &a, qmap const &b) { return !(a == b); }
2975 friend bool operator< (qmap const &a, qmap const &b) { return a.tree_ < b.tree_; }
2976 friend bool operator> (qmap const &a, qmap const &b) { return b.tree_ < a.tree_; }
2977 friend bool operator<=(qmap const &a, qmap const &b) { return !(b < a); }
2978 friend bool operator>=(qmap const &a, qmap const &b) { return !(a < b); }
2979
2980private:
2981 Tree tree_;
2982 // Really hammer those layout guarantees for qtree inside qmap.
2983 static_assert(std::is_standard_layout_v<Tree>, "qtree must be standard layout");
2984 static_assert(alignof(Tree) == alignof(void *), "qtree alignment must match pointer");
2985 static_assert(offsetof(Tree, header_) == 0, "qtree header_ must be first");
2986 static_assert(offsetof(Tree, node_count_) == sizeof(void *), "qtree node_count_ follows header_");
2987 static_assert(sizeof(Tree) == 2 * sizeof(void *), "qtree must be 2x sizeof(void*)");
2988};
2989
2990} // namespace qtree_detail
Definition qiterator.hpp:12
const_reverse_iterator crbegin() const noexcept
Definition qtree.hpp:2699
typename Tree::allocator_type allocator_type
Definition qtree.hpp:2643
qmap(std::initializer_list< value_type > init)
Definition qtree.hpp:2653
iterator find(K const &k)
Definition qtree.hpp:2728
qpair< iterator, bool > insert_or_assign(K const &k, M &&mapped)
Definition qtree.hpp:2959
friend bool operator!=(qmap const &a, qmap const &b)
Definition qtree.hpp:2974
iterator find(key_type const &key)
Definition qtree.hpp:2724
qpair< const_iterator, const_iterator > equal_range(K const &k) const
Definition qtree.hpp:2779
qmap(qmap &&other) noexcept
Definition qtree.hpp:2660
qmap(qmap const &other)
Definition qtree.hpp:2658
void insert(InputIt first, InputIt last)
Definition qtree.hpp:2786
void swap(qmap &other) noexcept(noexcept(tree_.swap(other.tree_)))
Definition qtree.hpp:2679
void clear() noexcept
Definition qtree.hpp:2782
iterator emplace_hint(const_iterator hint, Args &&...args)
Definition qtree.hpp:2868
mapped_type const & at(key_type const &key) const
Definition qtree.hpp:2838
qpair< iterator, bool > emplace(Args &&...args)
Definition qtree.hpp:2862
size_type count(K const &k) const
Definition qtree.hpp:2743
const_iterator lower_bound(K const &k) const
Definition qtree.hpp:2753
iterator end() noexcept
Definition qtree.hpp:2693
friend void swap(qmap &a, qmap &b) noexcept(noexcept(a.swap(b)))
Definition qtree.hpp:2683
qpair< iterator, bool > insert(value_type const &v)
Definition qtree.hpp:2793
friend bool operator<(qmap const &a, qmap const &b)
Definition qtree.hpp:2975
typename Tree::iterator iterator
Definition qtree.hpp:2639
typename Tree::template enable_hetero_eq< K > enable_hetero_eq
Definition qtree.hpp:2711
qpair< iterator, bool > insert_or_assign(K &&k, M &&mapped)
Definition qtree.hpp:2944
qpair< iterator, iterator > equal_range(K const &k)
Definition qtree.hpp:2776
bool contains(K const &k) const
Definition qtree.hpp:2737
qpair< iterator, bool > insert(value_type &&v)
Definition qtree.hpp:2794
iterator erase(const_iterator pos)
Definition qtree.hpp:2806
qpair< iterator, bool > insert_or_assign(key_type &&key, M &&mapped)
Definition qtree.hpp:2929
const_iterator begin() const noexcept
Definition qtree.hpp:2690
friend bool operator<=(qmap const &a, qmap const &b)
Definition qtree.hpp:2977
value_type const * const_pointer
Definition qtree.hpp:2637
typename Tree::value_compare value_compare
Definition qtree.hpp:2631
size_type size() const noexcept
Definition qtree.hpp:2707
value_compare value_comp() const noexcept
Definition qtree.hpp:2826
const_reverse_iterator rbegin() const noexcept
Definition qtree.hpp:2698
iterator insert(const_iterator hint, value_type const &v)
Definition qtree.hpp:2796
iterator erase(const_iterator first, const_iterator last)
Definition qtree.hpp:2811
const_reverse_iterator rend() const noexcept
Definition qtree.hpp:2702
size_type erase_if(Pred pred)
Definition qtree.hpp:2822
iterator lower_bound(K const &k)
Definition qtree.hpp:2750
qpair< iterator, bool > try_emplace(K &&k, Args &&...args)
Definition qtree.hpp:2896
const_iterator upper_bound(key_type const &key) const
Definition qtree.hpp:2757
size_type count(key_type const &key) const
Definition qtree.hpp:2740
friend bool operator>(qmap const &a, qmap const &b)
Definition qtree.hpp:2976
key_compare key_comp() const noexcept
Definition qtree.hpp:2825
typename Tree::template enable_hetero_eqrange< K > enable_hetero_eqrange
Definition qtree.hpp:2720
mapped_type & operator[](key_type &&key)
Definition qtree.hpp:2852
ida_alloc_policy allocator_policy_type
Definition qtree.hpp:2632
typename Tree::difference_type difference_type
Definition qtree.hpp:2629
size_type max_size() const noexcept
Definition qtree.hpp:2828
qpair< iterator, iterator > equal_range(key_type const &key)
Definition qtree.hpp:2766
iterator erase(iterator pos)
Definition qtree.hpp:2807
iterator upper_bound(key_type const &key)
Definition qtree.hpp:2756
typename Tree::size_type size_type
Definition qtree.hpp:2628
iterator upper_bound(K const &k)
Definition qtree.hpp:2760
qmap & operator=(qmap &&other) noexcept
Definition qtree.hpp:2673
const_iterator lower_bound(key_type const &key) const
Definition qtree.hpp:2747
value_type const & const_reference
Definition qtree.hpp:2635
bool contains(key_type const &key) const
Definition qtree.hpp:2734
mapped_type & operator[](key_type const &key)
Definition qtree.hpp:2846
size_type erase(K const &k)
Definition qtree.hpp:2819
const_iterator cbegin() const noexcept
Definition qtree.hpp:2691
reverse_iterator rend() noexcept
Definition qtree.hpp:2701
reverse_iterator rbegin() noexcept
Definition qtree.hpp:2697
iterator begin() noexcept
Definition qtree.hpp:2689
qpair< iterator, bool > insert_or_assign(key_type const &key, M &&mapped)
Definition qtree.hpp:2916
typename Tree::template enable_hetero_lb< K > enable_hetero_lb
Definition qtree.hpp:2714
void insert(std::initializer_list< value_type > init)
Definition qtree.hpp:2788
qpair< const_iterator, const_iterator > equal_range(key_type const &key) const
Definition qtree.hpp:2770
mapped_type & at(key_type const &key)
Definition qtree.hpp:2831
const_iterator find(K const &k) const
Definition qtree.hpp:2731
qiterator_detail::qreverse_iterator< iterator > reverse_iterator
Definition qtree.hpp:2641
CXX17_MEMORY_ALLOCATION_FUNCS_USING_POLICY(AllocPolicy) qmap() noexcept
Definition qtree.hpp:2648
bool empty() const noexcept
Definition qtree.hpp:2706
typename Tree::template enable_hetero_ub< K > enable_hetero_ub
Definition qtree.hpp:2717
typename Tree::const_iterator const_iterator
Definition qtree.hpp:2640
const_iterator find(key_type const &key) const
Definition qtree.hpp:2725
qpair< iterator, bool > try_emplace(key_type &&key, Args &&...args)
Definition qtree.hpp:2886
friend bool operator>=(qmap const &a, qmap const &b)
Definition qtree.hpp:2978
friend bool operator==(qmap const &a, qmap const &b)
Definition qtree.hpp:2973
qpair< iterator, bool > try_emplace(key_type const &key, Args &&...args)
Definition qtree.hpp:2877
qpair< const Key, Value > value_type
Definition qtree.hpp:2627
iterator insert(const_iterator hint, value_type &&v)
Definition qtree.hpp:2800
qiterator_detail::qreverse_iterator< const_iterator > const_reverse_iterator
Definition qtree.hpp:2642
const_iterator end() const noexcept
Definition qtree.hpp:2694
const_iterator upper_bound(K const &k) const
Definition qtree.hpp:2763
iterator lower_bound(key_type const &key)
Definition qtree.hpp:2746
allocator_type get_allocator() const noexcept
Definition qtree.hpp:2827
const_iterator cend() const noexcept
Definition qtree.hpp:2695
const_reverse_iterator crend() const noexcept
Definition qtree.hpp:2703
size_type erase(key_type const &key)
Definition qtree.hpp:2816
qmap & operator=(qmap const &other)
Definition qtree.hpp:2667
qpair< iterator, bool > try_emplace(K const &k, Args &&...args)
Definition qtree.hpp:2909
Definition qtree.hpp:675
reference operator*() const noexcept
Definition qtree.hpp:686
std::bidirectional_iterator_tag iterator_category
Definition qtree.hpp:677
qtree::value_type value_type
Definition qtree.hpp:679
const_iterator operator--(int)
Definition qtree.hpp:708
const_iterator & operator++()
Definition qtree.hpp:689
ptrdiff_t difference_type
Definition qtree.hpp:678
value_type const * pointer
Definition qtree.hpp:680
const_iterator operator++(int)
Definition qtree.hpp:695
friend class qtree
Definition qtree.hpp:737
const_iterator & operator--()
Definition qtree.hpp:702
pointer operator->() const noexcept
Definition qtree.hpp:687
value_type const & reference
Definition qtree.hpp:681
friend bool operator==(const_iterator lhs, const_iterator rhs) noexcept
Definition qtree.hpp:715
friend bool operator!=(const_iterator lhs, const_iterator rhs) noexcept
Definition qtree.hpp:720
ptrdiff_t difference_type
Definition qtree.hpp:601
qtree::value_type value_type
Definition qtree.hpp:602
pointer operator->() const noexcept
Definition qtree.hpp:609
std::bidirectional_iterator_tag iterator_category
Definition qtree.hpp:600
mutable_iterator & operator++()
Definition qtree.hpp:611
mutable_iterator & operator--()
Definition qtree.hpp:624
value_type & reference
Definition qtree.hpp:604
value_type * pointer
Definition qtree.hpp:603
friend bool operator==(mutable_iterator lhs, mutable_iterator rhs) noexcept
Definition qtree.hpp:637
mutable_iterator operator++(int)
Definition qtree.hpp:617
friend class qmap
Definition qtree.hpp:662
mutable_iterator operator--(int)
Definition qtree.hpp:630
friend class qtree
Definition qtree.hpp:659
friend class const_iterator
Definition qtree.hpp:660
friend bool operator!=(mutable_iterator lhs, mutable_iterator rhs) noexcept
Definition qtree.hpp:642
Definition qtree.hpp:321
void swap(qtree &other) noexcept
Definition qtree.hpp:582
std::enable_if_t< cmp_key_to_k_v< Compare, key_type, K > &&cmp_k_to_key_v< Compare, K, key_type >, int > enable_hetero_eqrange
Definition qtree.hpp:801
const_reverse_iterator crbegin() const noexcept
Definition qtree.hpp:770
friend bool operator>=(qtree const &a, qtree const &b)
Definition qtree.hpp:847
typename std::conditional_t< std::is_same_v< KeySelector, qtree_detail::identity_key< Value > >, const_iterator, mutable_iterator > iterator
Definition qtree.hpp:746
const_iterator begin() const noexcept
Definition qtree.hpp:756
iterator lower_bound(key_type const &key)
Definition qtree.hpp:896
void insert(std::initializer_list< value_type > init)
Definition qtree.hpp:973
qiterator_detail::qreverse_iterator< iterator > reverse_iterator
Definition qtree.hpp:747
iterator find(K const &key)
Definition qtree.hpp:863
iterator upper_bound(K const &key)
Definition qtree.hpp:929
iterator find(key_type const &key)
Definition qtree.hpp:852
~qtree()
Definition qtree.hpp:543
size_type erase_if(Pred pred)
Definition qtree.hpp:1072
reverse_iterator rend() noexcept
Definition qtree.hpp:772
void insert(InputIt first, InputIt last)
Definition qtree.hpp:967
qtree(qtree &&other) noexcept
Definition qtree.hpp:536
friend bool operator!=(qtree const &a, qtree const &b)
Definition qtree.hpp:824
iterator emplace_hint(const_iterator hint, Args &&...args)
Definition qtree.hpp:1020
qallocator< value_type, AllocPolicy > allocator_type
Definition qtree.hpp:487
const_iterator upper_bound(K const &key) const
Definition qtree.hpp:935
void clear() noexcept
Definition qtree.hpp:1088
value_compare value_comp() const noexcept
Definition qtree.hpp:1110
iterator end() noexcept
Definition qtree.hpp:764
size_type count(K const &key) const
Definition qtree.hpp:891
const_iterator lower_bound(key_type const &key) const
Definition qtree.hpp:901
const_iterator find(key_type const &key) const
Definition qtree.hpp:857
friend bool operator==(const_iterator l, mutable_iterator r) noexcept
Definition qtree.hpp:778
bool contains(key_type const &key) const
Definition qtree.hpp:874
iterator insert(const_iterator hint, value_type const &value)
Definition qtree.hpp:992
iterator insert(const_iterator hint, value_type &&value)
Definition qtree.hpp:1001
friend bool operator!=(const_iterator l, mutable_iterator r) noexcept
Definition qtree.hpp:780
const_iterator cend() const noexcept
Definition qtree.hpp:766
typename key_selector::key_type key_type
Definition qtree.hpp:478
value_type * pointer
Definition qtree.hpp:484
iterator erase(const_iterator pos)
Definition qtree.hpp:1034
const_reverse_iterator crend() const noexcept
Definition qtree.hpp:774
CXX17_MEMORY_ALLOCATION_FUNCS_USING_POLICY(AllocPolicy) qtree() noexcept
Definition qtree.hpp:492
const_reverse_iterator rbegin() const noexcept
Definition qtree.hpp:769
Value value_type
Definition qtree.hpp:477
size_type erase(K const &key)
Definition qtree.hpp:1062
const_iterator lower_bound(K const &key) const
Definition qtree.hpp:913
const_iterator end() const noexcept
Definition qtree.hpp:765
std::size_t size_type
Definition qtree.hpp:480
friend bool operator>(qtree const &a, qtree const &b)
Definition qtree.hpp:837
qpair< iterator, bool > insert(value_type &&value)
Definition qtree.hpp:985
bool empty() const noexcept
Definition qtree.hpp:814
std::enable_if_t< cmp_k_to_key_v< Compare, K, key_type >, int > enable_hetero_ub
Definition qtree.hpp:797
friend bool operator<=(qtree const &a, qtree const &b)
Definition qtree.hpp:842
const_iterator upper_bound(key_type const &key) const
Definition qtree.hpp:923
friend void swap(qtree &a, qtree &b) noexcept(noexcept(a.swap(b)))
Definition qtree.hpp:590
friend bool operator==(qtree const &a, qtree const &b)
Definition qtree.hpp:817
iterator begin() noexcept
Definition qtree.hpp:750
allocator_type get_allocator() const noexcept
Definition qtree.hpp:1111
qpair< iterator, iterator > equal_range(key_type const &key)
Definition qtree.hpp:940
qpair< iterator, bool > emplace(Args &&...args)
Definition qtree.hpp:1011
const_iterator find(K const &key) const
Definition qtree.hpp:869
friend bool operator<(qtree const &a, qtree const &b)
Definition qtree.hpp:832
key_compare key_comp() const noexcept
Definition qtree.hpp:1109
qtree(std::initializer_list< value_type > init)
Definition qtree.hpp:511
qiterator_detail::qreverse_iterator< const_iterator > const_reverse_iterator
Definition qtree.hpp:748
const_reverse_iterator rend() const noexcept
Definition qtree.hpp:773
size_type count(key_type const &key) const
Definition qtree.hpp:885
iterator erase(const_iterator first, const_iterator last)
Definition qtree.hpp:1043
qtree(qtree const &other)
Definition qtree.hpp:516
ptrdiff_t difference_type
Definition qtree.hpp:481
friend bool operator!=(mutable_iterator l, const_iterator r) noexcept
Definition qtree.hpp:779
qtree & operator=(qtree const &other)
Definition qtree.hpp:552
bool contains(K const &key) const
Definition qtree.hpp:880
std::enable_if_t< cmp_bidir_v< Compare, key_type, K >, int > enable_hetero_eq
Definition qtree.hpp:789
std::enable_if_t< cmp_key_to_k_v< Compare, key_type, K >, int > enable_hetero_lb
Definition qtree.hpp:793
qpair< const_iterator, const_iterator > equal_range(K const &key) const
Definition qtree.hpp:957
iterator upper_bound(key_type const &key)
Definition qtree.hpp:918
friend bool operator==(mutable_iterator l, const_iterator r) noexcept
Definition qtree.hpp:777
qtree_detail::value_compare_impl< value_type, key_selector, key_compare > value_compare
Definition qtree.hpp:486
value_type & reference
Definition qtree.hpp:482
qpair< iterator, iterator > equal_range(K const &key)
Definition qtree.hpp:951
qpair< const_iterator, const_iterator > equal_range(key_type const &key) const
Definition qtree.hpp:945
size_type size() const noexcept
Definition qtree.hpp:815
value_type const * const_pointer
Definition qtree.hpp:485
size_type erase(key_type const &key)
Definition qtree.hpp:1052
Compare key_compare
Definition qtree.hpp:479
const_iterator cbegin() const noexcept
Definition qtree.hpp:762
qpair< iterator, bool > insert(value_type const &value)
Definition qtree.hpp:978
size_type max_size() const noexcept
Definition qtree.hpp:1112
iterator lower_bound(K const &key)
Definition qtree.hpp:907
value_type const & const_reference
Definition qtree.hpp:483
qtree & operator=(qtree &&other) noexcept
Definition qtree.hpp:562
reverse_iterator rbegin() noexcept
Definition qtree.hpp:768
idaman size_t n
Definition pro.h:1000
carglist_t * args
Definition hexrays.hpp:7720
idaman int64 pos
Definition kernwin.hpp:1398
bool result
Definition kernwin.hpp:8332
Definition qtree.hpp:186
constexpr T * q_launder(T *p) noexcept
Definition qtree.hpp:291
qtree< Key, Compare, AllocPolicy, PackNode, qtree_detail::identity_key< Key > > qset
Definition qtree.hpp:2610
Color
Definition qtree.hpp:314
@ Black
Definition qtree.hpp:314
@ Red
Definition qtree.hpp:314
Definition pro.h:3781
idaman size_t const char time_t t
Definition pro.h:606
idaman THREAD_SAFE uval_t ida_export rotate_left(uval_t x, int count, size_t bits, size_t offset)
Rotate left - can be used to rotate a value to the right if the count is negative.
qtree_detail::qmap< Key, Value, Compare, ida_alloc_policy, true > qmap
Definition qmap.hpp:9
qpair(T1, T2) -> qpair< std::decay_t< T1 >, std::decay_t< T2 > >
Definition qallocator.hpp:8
size_type max_size() const noexcept
Definition qallocator.hpp:55
Definition qpair.hpp:9
Definition qtree.hpp:226
Definition qtree.hpp:200
T key_type
Definition qtree.hpp:201
constexpr key_type const & operator()(T const &value) const noexcept
Definition qtree.hpp:202
Definition qtree.hpp:261
Definition qtree.hpp:237
constexpr First const & operator()(qpair< First, Second > const &value) const noexcept
Definition qtree.hpp:218
std::remove_const_t< First > key_type
Definition qtree.hpp:217
Definition qtree.hpp:212
Definition qtree.hpp:303
constexpr bool operator()(Value const &a, Value const &b) const noexcept(noexcept(KeyCompare {}(KeySelector {}(a), KeySelector {}(b))))
Definition qtree.hpp:305
constexpr value_compare_impl()
Definition qtree.hpp:304