Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
SkipList.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Containers_DataStructures_SkipList_h_
5#define _Stroika_Foundation_Containers_DataStructures_SkipList_h_
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9// for now uses std::vector...
10#include <vector>
11
12#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Common/KeyValuePair.h"
16#include "Stroika/Foundation/Containers/Common.h"
19
20/**
21 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
22 */
23
25
27
28 namespace SkipList_Support {
29
30 /**
31 * KEY_TYPE the type of the key element stored in the SkipList.
32 * KEY_COMPARER is nearly always as given
33 * AddOrExtendOrReplaceMode addOrExtendOrReplace defaults to eAddExtras, but here the caller may not want the default. There is no good default here.
34 * ALTERNATE_FIND_TYPE can often be omitted (default) - but allows Find () to be overloaded (argument comparer) on a different type (besides just KEY_TYPE).
35 */
36 template <typename KEY_TYPE, Common::IThreeWayComparer<KEY_TYPE> KEY_COMPARER = compare_three_way,
37 AddOrExtendOrReplaceMode addOrExtendOrReplace = AddOrExtendOrReplaceMode::eAddExtras, typename ALTERNATE_FIND_TYPE = void>
39 /**
40 */
41 using KeyComparerType = KEY_COMPARER;
42
43 /**
44 * Defines how conflicting / duplicate keys are handled: replace, throw, or just OK - like a multiset.
45 */
46 static constexpr AddOrExtendOrReplaceMode kAddOrExtendOrReplaceMode{addOrExtendOrReplace};
47
48 /**
49 * Store stats about performance of SkipList, for tuning purposes
50 */
51 static constexpr bool kKeepStatistics{false};
52
53 /**
54 */
55 static constexpr bool kCostlyInvariants{false};
56
57 /**
58 * like is_transparent mechanism in C++14, except just adds one type (if not void) to the set of types you can find looking for)
59 * \note when using AlternateFindType != void, caller must ALSO provide a compare function which accepts combinations of T, and AlternateFindType
60 */
61 using AlternateFindType = ALTERNATE_FIND_TYPE;
62 };
63
64 /**
65 */
66 template <typename TRAITS, typename KEY_TYPE>
67 concept IValidTraits = Common::IThreeWayComparer<typename TRAITS::KeyComparerType, KEY_TYPE> and requires (TRAITS) {
68 { TRAITS::kKeepStatistics } -> std::convertible_to<bool>;
69 { TRAITS::kAddOrExtendOrReplaceMode } -> std::convertible_to<AddOrExtendOrReplaceMode>;
70 };
71
72 struct Stats_Basic {
73 size_t fCompares{0};
74 // SkipLists don't really do rotations, but we treat link patching as same thing
75 // @todo rename so more appropriate - and looking at code - not clear what this is actually counting
76 size_t fRotations{0};
77
78 /**
79 * @see Characters::ToString ();
80 */
81 //nonvirtual Characters::String ToString () const;
82 };
83
84 /**
85 */
86 template <typename KEY_TYPE, IValidTraits<KEY_TYPE> TRAITS>
87 using StatsType = conditional_t<TRAITS::kKeepStatistics, Stats_Basic, Common::Empty>;
88 }
89
90 /**
91 * SkipList<> is a (low level) data structure class. It is not intended to be directly
92 * used by programmers, except in implementing concrete container reps, but of course can be
93 * where extra performance maybe needed, over convenience and flexibility.
94 *
95 * Key Features (compared with balanced binary tree)
96 * o Explicit Rebalance () call
97 * o (optional)Prioritize() call - can optimize lookup of any key
98 * o fast find, reasonably fast add and remove (about 20-30% as many comparisons as finds)
99 * o robust about order of additions
100 * o space efficient (1.33 links per value, as opposed to tree structures requiring 2 links per value)
101 * o ability to add more than one entry with the same key
102 * o ability to reorder links into nearly optimal configuration. You can optimize the node structure in a skip list in a single pass (order N).
103 * This reduces the total comparisons in a search by between 20 and 40%.
104 * o possible to efficiently parallelize (not yet attempted -- see http://www.1024cores.net/home/parallel-computing/concurrent-skip-list)
105 * o SkipLists support fast forward iteration (linked list traversal). They do not support backwards iteration.
106 *
107 * Design Overview:
108 * This is an ORDERED linked list (so no push_front, cuz order implied by key value and comparer).
109 * To get extra speed on lookups, besides a 'next' pointer, each node may have a list of additional
110 * pointers that go (each progressively) further into the linked list. Ideally, these 'jumps' deeper
111 * into the linked list would be 'well spaced' so that you approach log(N) lookup times trying to find a Node.
112 *
113 * For each link, the fLinks[0] is always == NEXT link.
114 *
115 * \see http://en.wikipedia.org/wiki/Skip_list:
116 * A skip list is a data structure for storing a sorted list of items using a hierarchy of linked lists that connect
117 * increasingly sparse subsequences of the items. These auxiliary lists allow item lookup with efficiency comparable
118 * to balanced binary search trees (that is, with number of probes proportional to log n instead of n).
119 *
120 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
121 *
122 * \note TODOS:
123 * @todo - should we use shared_ptr - must be more careful bout leaks if not using shared_ptr
124 * @todo Cleanup docs
125 * @todo use InlineBuffer instead of vector, and make size of pre-allocated guy fixed in TRAITS (@todo discuss with sterl)
126 * @todo https://stroika.atlassian.net/browse/STK-1016 - ranges/sentinel support
127 *
128 // OLD DOCS to lift from (from SSW impl)
129 In principle you can use different probabilities for having more than one link. The optimal probability for finds is 1/4, and that also produces a list
130 that is more space efficient than a traditional binary tree, as it has only 1.33 nodes per entry, compared with a binary tree using 2.
131
132 Testing SkipList of 100000 entries, sorted add, link creation probability of 0.25
133 Unoptimized SkipList
134 total links = 133298; avg link height = 1.33298; max link height= 9
135 find avg comparisons = 28.8035; expected = 33.2193
136 add avg comparisons = 37.5224; expected = 35.5526
137 remove avg comparisons = 38.1671; expected = 35.5526
138
139 After optimizing links
140 total links = 133330; avg link height = 1.3333; max link height= 9
141 find avg comparisons = 18.1852; expected = 33.2193
142 find reduction = 36.8646%
143
144 The "expected" above is from wikipedia, and is calculated as (log base 1/p n)/p.
145
146 * \original Author: Sterling Wight
147 */
148 template <typename KEY_TYPE, typename MAPPED_TYPE, SkipList_Support::IValidTraits<KEY_TYPE> TRAITS = SkipList_Support::DefaultTraits<KEY_TYPE>>
150 private:
151 struct Link_;
152
153 public:
154 /**
155 * This is the key for the associative container. It need not be unique (see various Add / Lookup APIs).
156 */
157 using key_type = KEY_TYPE;
158
159 public:
160 /**
161 * This is typically NOT void, but may be void
162 */
163 using mapped_type = MAPPED_TYPE;
164
165 public:
166 /**
167 * SkipLists intrinsically require knowing the in-order comparison function applied to key data.
168 */
169 using KeyComparerType = typename TRAITS::KeyComparerType;
170
171 public:
172 /**
173 * This is typically Common::Empty, but can contain real stats used to debug/tune parameters,
174 * if you construct the SkipList with TRAITS having kKeepStatistics true.
175 */
176 using StatsType = SkipList_Support::StatsType<KEY_TYPE, TRAITS>;
177
178 public:
179 /**
180 * KeyValuePair of KEY_TYPE and MAPPED_TYPE. This is what you iterate over, and the SkipList is a container of these.
181 */
183
184 public:
185 /**
186 * Basic (mostly internal) element used by ForwardIterator. Abstract name so can be referenced generically across 'DataStructure' objects
187 */
188 using UnderlyingIteratorRep = const Link_*;
189
190 public:
191 /**
192 */
193 using TraitsType = TRAITS;
194
195 public:
196 /**
197 */
198 SkipList (const KeyComparerType& keyComparer = {});
199 SkipList (SkipList&& src) noexcept;
200 SkipList (const SkipList& src);
201 ~SkipList ();
202
203 public:
204 nonvirtual SkipList& operator= (const SkipList& rhs);
205
206 public:
207 /**
208 */
209 constexpr KeyComparerType key_comp () const;
210
211 public:
212 class ForwardIterator;
213
214 public:
215 /**
216 * You can add more than one item with the same key. If you add different values with the same key, but it is unspecified which item will be returned on subsequent Find or Remove calls.
217 *
218 * Returns true if the list was changed (if eAddReplaces, and key found, return true even if val same as value already there because we cannot generically compare values)
219 *
220 * \note Runtime performance/complexity: ??
221 * Average: log(N)
222 * Worst: N
223 */
224 nonvirtual bool Add (ArgByValueType<key_type> key, ForwardIterator* oAddedI = nullptr)
225 requires (same_as<mapped_type, void>)
226#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
227 {
228 return Add1_ (key, oAddedI);
229 }
230#else
231 ;
232#endif
233 template <typename CHECK_T = MAPPED_TYPE>
234 nonvirtual bool Add (ArgByValueType<key_type> key, ArgByValueType<CHECK_T> val, ForwardIterator* oAddedI = nullptr)
235 requires (not same_as<mapped_type, void>)
236#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
237 {
238 return Add2_ (key, val, oAddedI);
239 }
240#else
241 ;
242#endif
243 nonvirtual bool Add (const value_type& v, ForwardIterator* oAddedI = nullptr);
244
245 public:
246 /**
247 * \brief Remove an item with the given key (require it exists)
248 *
249 * \pre contains (key)
250 *
251 * \note same as Verify (RemoveIf (key))
252 *
253 * \note Runtime performance/complexity:
254 * Average: log(N)
255 * Worst: N
256 *
257 * \see also erase()
258 */
259 nonvirtual void Remove (ArgByValueType<key_type> key);
260 nonvirtual void Remove (const ForwardIterator& it);
261
262 public:
263 /**
264 * \brief remove the element at i, and return valid iterator to the element that was following it (which can be empty iterator)
265 *
266 * \pre i != end ()
267 *
268 * \brief see https://en.cppreference.com/w/cpp/container/vector/erase
269 *
270 * \note Runtime performance/complexity:
271 * Average: log(N)
272 * Worst: N
273 *
274 * \see also Remove()
275 */
276 nonvirtual ForwardIterator erase (const ForwardIterator& i);
277
278 public:
279 /**
280 * \brief Remove the first item with the given key, if any. Return true if a value found and removed, false if no such key found.
281 *
282 * \note Runtime performance/complexity:
283 * Average: log(N)
284 * Worst: N
285 */
286 nonvirtual bool RemoveIf (ArgByValueType<key_type> key);
287
288 public:
289 /**
290 * @todo discuss with sterl - if we allow multiple values with same key, add RemoveAll overload taking key_type, and maybe returning count removed? RemoveAllIf
291 */
292 nonvirtual void RemoveAll ();
293
294 public:
295 /**
296 * \note Runtime performance/complexity:
297 * Always: constant
298 */
299 nonvirtual size_t size () const;
300
301 public:
302 /**
303 * \note Runtime performance/complexity:
304 * Always: constant
305 */
306 nonvirtual bool empty () const;
307
308 public:
309 /**
310 */
311 nonvirtual ForwardIterator begin () const;
312
313 public:
314 /**
315 */
316 constexpr ForwardIterator end () const noexcept;
317
318 public:
319 /*
320 * Support for COW (CopyOnWrite):
321 *
322 * Take iterator 'pi' which is originally a valid iterator from 'movedFrom' - and replace *pi with a valid
323 * iterator from 'this' - which points at the same logical position. This requires that this container
324 * was just 'copied' from 'movedFrom' - and is used to produce an equivalent iterator (since iterators are tied to
325 * the container they were iterating over).
326 */
327 nonvirtual void MoveIteratorHereAfterClone (ForwardIterator* pi, const SkipList* movedFrom) const;
328
329 public:
330 /**
331 * \see https://en.cppreference.com/w/cpp/container/map/contains
332 *
333 * \note Runtime performance/complexity: ??
334 * Average: log(N)
335 * Worst: N
336 */
337 nonvirtual bool contains (ArgByValueType<key_type> key) const;
338
339 public:
340 /**
341 * \note Runtime performance/complexity:
342 * overload: (key_type)
343 * Average/Worst: log(N) ; N
344 * \note Runtime performance/complexity:
345 * overload: (FUNCTION&& f overload)
346 * Average/Worst: O(N)
347 *
348 * \note this is kind of like set<T>::find () - but not exactly, and find() doesn't really have a uniform API across the various stl containers...
349 * which is why we use Find(), instead of find() as a name
350 */
351 nonvirtual ForwardIterator Find (ArgByValueType<key_type> key) const;
352 template <typename ARG_T = typename TRAITS::AlternateFindType>
353 nonvirtual ForwardIterator Find (ARG_T key) const
354 requires (not same_as<typename TRAITS::AlternateFindType, void> and same_as<remove_cvref_t<ARG_T>, typename TRAITS::AlternateFindType>);
355 template <predicate<typename SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::value_type> FUNCTION>
356 nonvirtual ForwardIterator Find (FUNCTION&& firstThat) const
357#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
358 {
359 for (auto i = begin (); i; ++i) {
360 if (firstThat (*i)) {
361 return i;
362 }
363 }
364 return end ();
365 }
366#else
367 ;
368#endif
369
370 public:
371 /**
372 * \par Example Usage:
373 * \code
374 * EXPECT_EQ (t.First (key), i);
375 * \endcode
376 *
377 * \par Example Usage:
378 * \code
379 * if (auto o = t.First (key)) {
380 * useO = *o;
381 * }
382 * \endcode
383 *
384 * \par Example Usage:
385 * \code
386 * // find value of first odd key
387 * if (auto o = t.First ([] (auto kvp) { return kvp.fKey & 1; }) {
388 * useO = *o;
389 * }
390 * \endcode
391 *
392 * \note Complexity (key_type): ??
393 * Average: log(N)
394 * Worst: N
395 * \note Complexity (FUNCTION&& f overload):
396 * Average/Worst: O(N)
397 */
398 nonvirtual optional<mapped_type> First (ArgByValueType<key_type> key) const;
399 template <qCompilerAndStdLib_RequiresNotMatchXXXDefined_1_BWA (predicate<typename SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::value_type>) FUNCTION>
400 nonvirtual optional<mapped_type> First (FUNCTION&& firstThat) const;
401
402 public:
403 /**
404 * \note - unlike modifying operations, this doesn't invalidate any iterators (including the argument iterator).
405 */
406 template <typename CHECKED_T = MAPPED_TYPE>
407 nonvirtual void Update (const ForwardIterator& it, ArgByValueType<CHECKED_T> newValue)
408 requires (not same_as<MAPPED_TYPE, void>);
409
410 public:
411 /**
412 * \brief optimize the memory layout of the SkipList
413 *
414 * calling this will result in maximal search performance until further adds or removes
415 * call when list is relatively stable in size, and it will set links to near classic log(n/2) search time
416 * relatively fast to call, as is order N (single list traversal)
417 *
418 * @aliases Optimize
419 *
420 * \note Runtime performance/complexity:
421 * Average/WorseCase???
422 */
423 nonvirtual void ReBalance ();
424
425 public:
426 /**
427 * make the key faster on finds, possibly slowing other key searches down
428 *
429 * \note Runtime performance/complexity:
430 * Average/WorseCase???
431 */
432 nonvirtual void Prioritize (ArgByValueType<key_type> key);
433
434 public:
435 /**
436 * \note Runtime performance/complexity:
437 * Always: O(N)
438 */
439 template <qCompilerAndStdLib_RequiresNotMatchXXXDefined_1_BWA (invocable<typename SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::value_type>) FUNCTION>
440 nonvirtual void Apply (FUNCTION&& doToElement) const;
441
442 public:
443 constexpr void Invariant () const noexcept;
444
445 public:
446 /**
447 * @todo doc api just for debugging? And not generally useful. And maybe have return tuple, not take var param?
448 // height is highest link height, also counts total links if pass in non-null totalHeight
449 @todo ask sterl about this?
450 */
451 nonvirtual size_t CalcHeight (size_t* totalHeight = nullptr) const;
452
453 public:
454 /**
455 * @todo DOC MENAING - CONTROLS - (TRAITS) and maybe range (type special) - 0..100?)
456 */
457 static size_t GetLinkHeightProbability (); // percent chance. We use 25%, which appears optimal
458
459 public:
460 /**
461 * Instantiate with TRAITS::kKeepStatistics==true to get useful stats.
462 */
463 nonvirtual StatsType GetStats () const;
464
465 private:
466 /*
467 * These return the first and last entries in the tree (defined as the first and last entries that would be returned via
468 * iteration, assuming other users did not alter the tree. Note that these routines require no key compares, and are thus very fast.
469 */
470 nonvirtual Link_* GetFirst_ () const; // synonym for begin (), MakeIterator ()
471
472 private:
473 nonvirtual Link_* GetLast_ () const; // returns iterator to largest key
474
475 private:
476 // @todo maybe make part of traits??? and use in InlineBuffer somehow? instead of vector
477 // maybe no need for MAX - just optimized-for-max - size of inline buffer - not sure why we need any other max (can use stackbuffer for that)
478 static constexpr size_t kMaxLinkHeight_ = sizeof (size_t) * 8;
479
480 private:
481 // @todo consider using Memory::InlineBuffer<> - so fewer memory allocations for some small buffer size???, and tune impl to prefer this size or take param in traits used for this
482 using LinkVector_ = vector<Link_*>;
483
484 private:
485 // Fundamentally a linked-list, but with a quirky 'next' pointer(s)
486 struct Link_ : public Memory::UseBlockAllocationIfAppropriate<Link_, sizeof (value_type) <= 128> {
487 template <typename MAPPED_TYPE2 = MAPPED_TYPE>
488 constexpr Link_ (ArgByValueType<key_type> key, ArgByValueType<MAPPED_TYPE2> val)
489 requires (not same_as<MAPPED_TYPE2, void>)
490#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
491 : fEntry{key, val} {}
492#else
493 ;
494#endif
495 constexpr Link_ (ArgByValueType<key_type> key)
496 requires (same_as<MAPPED_TYPE, void>)
497#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
498 : fEntry{key} {}
499#else
500 ;
501#endif
502 constexpr Link_ (ArgByValueType<value_type> v);
503
504 value_type fEntry;
505 LinkVector_ fNext; // for a SkipList, you have an array of next pointers, rather than just one
506 };
507 LinkVector_ fHead_{};
508
509 private:
510 /*
511 * Find Link for key in SkipList, else nullptr. In cases of duplicate values, return first found.
512 */
513 template <Common::IAnyOf<KEY_TYPE, typename TRAITS::AlternateFindType> KEYISH_T>
514 nonvirtual Link_* FindLink_ (const KEYISH_T& key) const;
515
516 private:
517 /*
518 * This searches the list for the given key. If found exactly, it is returned. If it occurs multiple times a random one is selected.
519 *
520 * this is specialized for the case of adding or removing elements, as it also returns
521 * all links that will need to be updated for the new element or the element to be removed
522 *
523 * \post (result == nullptr or fKeyThreeWayComparer_ (result->fEntry.fKey, key) == strong_ordering::equal);
524 *
525///??? MAYBE NOT * \post all links in linksPointingToReturnedLink are non-null, and valid Link_* pointers
526 @todo CONSIDER if LinkVector sb replaced with set<Link*>
527 */
528 struct LinkAndInfoAboutBackPointers {
529 Link_* fLink;
530 /**
531 * This is a vector, not a set, because it must reproduce the 'heights' of the linked tree structure.
532 * and nullptr entries in the list are 'sentinel values' indicating start of list (@Sterl why not just inert &fHead directly)
533 */
534 LinkVector_ fLinksPointingToReturnedLink; // @todo consider using set, and unclear what nullptr means in this vector, nor duplicates?
535 };
536 nonvirtual LinkAndInfoAboutBackPointers FindNearest_ (const variant<key_type, ForwardIterator>& keyOrI) const;
537
538 private:
539 // @todo ASK STERL MEANING OF LinkVector_ argument? Is it links to patch, or a starter on links for 'n'
540 // and why not have PatchLinks method?
541 nonvirtual void AddLink_ (Link_* n, const LinkVector_& linksToPatch);
542
543 private:
544 // @todo ask sterl meaning of LinkVector_ argument here? Why not have PatchLinks_ method?
545 nonvirtual void RemoveLink_ (Link_* n, const LinkVector_& linksToPatch);
546
547#if qStroika_Foundation_Debug_AssertionsChecked
548 private:
549 nonvirtual void Invariant_ () const noexcept;
550#endif
551
552 private:
553 nonvirtual void ShrinkHeadLinksIfNeeded_ ();
554
555 private:
556 nonvirtual void GrowHeadLinksIfNeeded_ (size_t newSize, Link_* linkToPointTo);
557
558 private:
559 nonvirtual size_t DetermineLinkHeight_ () const;
560
561#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
562 private:
563 bool Add1_ (ArgByValueType<key_type> key, ForwardIterator* oAddedI);
564 template <typename CHECK_T = MAPPED_TYPE>
565 bool Add2_ (ArgByValueType<key_type> key, ArgByValueType<CHECK_T> val, ForwardIterator* oAddedI);
566#endif
567
568 private:
569 [[no_unique_address]] KeyComparerType fKeyThreeWayComparer_{};
570 size_t fLength_{0};
571 [[no_unique_address]] mutable StatsType fStats_{};
572 };
573
574 /*
575 * ForwardIterator allows you to iterate over a SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>. It is not safe to use a ForwardIterator after any
576 * update to the SkipList.
577 */
578 template <typename KEY_TYPE, typename MAPPED_TYPE, SkipList_Support::IValidTraits<KEY_TYPE> TRAITS>
579 class SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator {
580 public:
581 // stuff STL requires you to set to look like an iterator
582 using iterator_category = forward_iterator_tag;
584 using difference_type = ptrdiff_t;
585 using pointer = const value_type*;
586 using reference = const value_type&;
587
588 public:
589 /**
590 * /0 overload: sets iterator to 'end' - sentinel
591 * /1 (data) overload: sets iterator to begin
592 * /2 (data,startAt) overload: sets iterator to startAt
593 */
594 constexpr ForwardIterator () noexcept = default;
595 explicit constexpr ForwardIterator (const SkipList* data) noexcept;
596 explicit constexpr ForwardIterator (const SkipList* data, UnderlyingIteratorRep startAt) noexcept;
597 constexpr ForwardIterator (const ForwardIterator&) noexcept = default;
598 constexpr ForwardIterator (ForwardIterator&&) noexcept = default;
599
600 public:
601 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
602 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
603
604#if qStroika_Foundation_Debug_AssertionsChecked
605 public:
606 ~ForwardIterator ();
607#endif
608
609 public:
610 /**
611 * return true if iterator not Done
612 */
613 explicit operator bool () const;
614
615 public:
616 nonvirtual bool Done () const noexcept;
617
618 public:
619 nonvirtual const value_type& operator* () const; // Error to call if Done (), otherwise OK
620
621 public:
622 nonvirtual const value_type* operator->() const; // Error to call if Done (), otherwise OK
623
624 public:
625 /**
626 * \note Runtime performance/complexity:
627 * Average/WorseCase: O(N) - super slow cuz have to traverse on average half the list
628 *
629 * \pre data == fData_ argument constructed with (or as adjusted by Move...); api takes extra param so release builds need not store fData_
630 */
631 nonvirtual size_t CurrentIndex (const SkipList* data) const;
632
633 public:
634 /**
635 * \pre GetUnderlyingData() == rhs.GetUnderlyingData (), or special case of one or the other is nullptr
636 */
637 constexpr bool operator== (const ForwardIterator& rhs) const;
638
639 public:
640 constexpr UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
641
642 public:
643 nonvirtual void SetUnderlyingIteratorRep (const UnderlyingIteratorRep l);
644
645 public:
646 nonvirtual ForwardIterator& operator++ ();
647 nonvirtual ForwardIterator operator++ (int);
648
649 public:
650 // safe to update in place (doesn't change iterators) since doesn't change order of list (since not updating key)
651 template <typename CHECKED_T = MAPPED_TYPE>
652 nonvirtual void UpdateValue (ArgByValueType<CHECKED_T> newValue)
653 requires (not same_as<MAPPED_TYPE, void>);
654
655 public:
656 /**
657 * For debugging, assert the iterator data matches argument data
658 */
659 constexpr void AssertDataMatches (const SkipList* data) const;
660
661 public:
662 constexpr void Invariant () const noexcept;
663
664#if qStroika_Foundation_Debug_AssertionsChecked
665 private:
666 nonvirtual void Invariant_ () const noexcept;
667#endif
668
669 private:
670 const Link_* fCurrent_{nullptr};
671#if qStroika_Foundation_Debug_AssertionsChecked
672 const SkipList* fData_{nullptr};
673#endif
674
675 private:
676 friend class SkipList;
677 };
678
679 static_assert (ranges::input_range<SkipList<int, int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
680
681}
682
683/*
684 ********************************************************************************
685 ***************************** Implementation Details ***************************
686 ********************************************************************************
687 */
688#include "SkipList.inl"
689
690#endif /*_Stroika_Foundation_Containers_DataStructures_SkipList_h_ */
nonvirtual size_t CalcHeight(size_t *totalHeight=nullptr) const
Definition SkipList.inl:774
Common::KeyValuePair< KEY_TYPE, MAPPED_TYPE > value_type
Definition SkipList.h:182
nonvirtual void Apply(FUNCTION &&doToElement) const
nonvirtual bool contains(ArgByValueType< key_type > key) const
Definition SkipList.inl:296
nonvirtual void ReBalance()
optimize the memory layout of the SkipList
Definition SkipList.inl:714
nonvirtual optional< mapped_type > First(ArgByValueType< key_type > key) const
Definition SkipList.inl:277
typename TRAITS::KeyComparerType KeyComparerType
Definition SkipList.h:169
nonvirtual ForwardIterator Find(ArgByValueType< key_type > key) const
Definition SkipList.inl:252
nonvirtual void Update(const ForwardIterator &it, ArgByValueType< CHECKED_T > newValue)
nonvirtual bool RemoveIf(ArgByValueType< key_type > key)
Remove the first item with the given key, if any. Return true if a value found and removed,...
Definition SkipList.inl:483
nonvirtual void Remove(ArgByValueType< key_type > key)
Remove an item with the given key (require it exists)
Definition SkipList.inl:453
nonvirtual ForwardIterator erase(const ForwardIterator &i)
remove the element at i, and return valid iterator to the element that was following it (which can be...
Definition SkipList.inl:469
SkipList_Support::StatsType< KEY_TYPE, TRAITS > StatsType
Definition SkipList.h:176
nonvirtual bool Add(ArgByValueType< key_type > key, ForwardIterator *oAddedI=nullptr)
Definition SkipList.inl:305
nonvirtual void Prioritize(ArgByValueType< key_type > key)
Definition SkipList.inl:673
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
Definition TypeHints.h:32
constexpr void AssertDataMatches(const DoublyLinkedList *data) const
nonvirtual size_t CurrentIndex(const DoublyLinkedList *data) const
AddOrExtendOrReplaceMode
Mode flag to say if Adding to a container replaces, or if the first addition wins (Logically AddOrExt...