Stroika Library 3.0d18
 
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 clear ();
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 * \brief simple wrapper on Find() - but returning optional instead of iterators or pointers
373 *
374 * \par Example Usage:
375 * \code
376 * EXPECT_EQ (t.First (key), i);
377 * \endcode
378 *
379 * \par Example Usage:
380 * \code
381 * if (auto o = t.First (key)) {
382 * useO = *o;
383 * }
384 * \endcode
385 *
386 * \par Example Usage:
387 * \code
388 * // find value of first odd key
389 * if (auto o = t.First ([] (auto kvp) { return kvp.fKey & 1; }) {
390 * useO = *o;
391 * }
392 * \endcode
393 *
394 * \note Complexity (key_type): ??
395 * Average: log(N)
396 * Worst: N
397 * \note Complexity (FUNCTION&& f overload):
398 * Average/Worst: O(N)
399 */
400 nonvirtual optional<mapped_type> First (ArgByValueType<key_type> key) const;
401 template <qCompilerAndStdLib_RequiresNotMatchXXXDefined_1_BWA (predicate<typename SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::value_type>) FUNCTION>
402 nonvirtual optional<mapped_type> First (FUNCTION&& firstThat) const;
403
404 public:
405 /**
406 * \note - unlike other modifying operations, this doesn't invalidate any iterators (including the argument iterator).
407 */
408 template <typename CHECKED_T = MAPPED_TYPE>
409 nonvirtual void Update (const ForwardIterator& it, ArgByValueType<CHECKED_T> newValue)
410 requires (not same_as<MAPPED_TYPE, void>);
411
412 public:
413 /**
414 * \brief optimize the memory layout of the SkipList
415 *
416 * calling this will result in maximal search performance until further adds or removes
417 * call when list is relatively stable in size, and it will set links to near classic log(n/2) search time
418 * relatively fast to call, as is order N (single list traversal)
419 *
420 * @aliases Optimize
421 *
422 * \note Runtime performance/complexity:
423 * Average/WorseCase???
424 */
425 nonvirtual void ReBalance ();
426
427 public:
428 /**
429 * make the key faster on finds, possibly slowing other key searches down
430 *
431 * \note Runtime performance/complexity:
432 * Average/WorseCase???
433 */
434 nonvirtual void Prioritize (ArgByValueType<key_type> key);
435
436 public:
437 /**
438 * \note Runtime performance/complexity:
439 * Always: O(N)
440 */
441 template <qCompilerAndStdLib_RequiresNotMatchXXXDefined_1_BWA (invocable<typename SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::value_type>) FUNCTION>
442 nonvirtual void Apply (FUNCTION&& doToElement) const;
443
444 public:
445 /**
446 */
447 constexpr void Invariant () const noexcept;
448
449 public:
450 /**
451 * @todo doc api just for debugging? And not generally useful. And maybe have return tuple, not take var param?
452 // height is highest link height, also counts total links if pass in non-null totalHeight
453 @todo ask sterl about this?
454 */
455 nonvirtual size_t CalcHeight (size_t* totalHeight = nullptr) const;
456
457 public:
458 /**
459 * @todo DOC MENAING - CONTROLS - (TRAITS) and maybe range (type special) - 0..100?)
460 */
461 static size_t GetLinkHeightProbability (); // percent chance. We use 25%, which appears optimal
462
463 public:
464 /**
465 * Instantiate with TRAITS::kKeepStatistics==true to get useful stats.
466 */
467 nonvirtual StatsType GetStats () const;
468
469 private:
470 /*
471 * These return the first and last entries in the tree (defined as the first and last entries that would be returned via
472 * iteration, assuming other users did not alter the tree. Note that these routines require no key compares, and are thus very fast.
473 */
474 nonvirtual Link_* GetFirst_ () const; // synonym for begin (), MakeIterator ()
475
476 private:
477 nonvirtual Link_* GetLast_ () const; // returns iterator to largest key
478
479 private:
480 // @todo maybe make part of traits??? and use in InlineBuffer somehow? instead of vector
481 // 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)
482 static constexpr size_t kMaxLinkHeight_ = sizeof (size_t) * 8;
483
484 private:
485 // @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
486 using LinkVector_ = vector<Link_*>;
487
488 private:
489 // Fundamentally a linked-list, but with a quirky 'next' pointer(s)
490 struct Link_ : public Memory::UseBlockAllocationIfAppropriate<Link_, sizeof (value_type) <= 128> {
491 template <typename MAPPED_TYPE2 = MAPPED_TYPE>
492 constexpr Link_ (ArgByValueType<key_type> key, ArgByValueType<MAPPED_TYPE2> val)
493 requires (not same_as<MAPPED_TYPE2, void>)
494#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
495 : fEntry{key, val} {}
496#else
497 ;
498#endif
499 constexpr Link_ (ArgByValueType<key_type> key)
500 requires (same_as<MAPPED_TYPE, void>)
501#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
502 : fEntry{key} {}
503#else
504 ;
505#endif
506 constexpr Link_ (ArgByValueType<value_type> v);
507
508 value_type fEntry;
509 LinkVector_ fNext; // for a SkipList, you have an array of next pointers, rather than just one
510 };
511 LinkVector_ fHead_{};
512
513 private:
514 /*
515 * Find Link for key in SkipList, else nullptr. In cases of duplicate values, return first found.
516 */
517 template <Common::IAnyOf<KEY_TYPE, typename TRAITS::AlternateFindType> KEYISH_T>
518 nonvirtual Link_* FindLink_ (const KEYISH_T& key) const;
519
520 private:
521 /*
522 * This searches the list for the given key. If found exactly, it is returned. If it occurs multiple times a random one is selected.
523 *
524 * this is specialized for the case of adding or removing elements, as it also returns
525 * all links that will need to be updated for the new element or the element to be removed
526 *
527 * \post (result == nullptr or fKeyThreeWayComparer_ (result->fEntry.fKey, key) == strong_ordering::equal);
528 *
529///??? MAYBE NOT * \post all links in linksPointingToReturnedLink are non-null, and valid Link_* pointers
530 @todo CONSIDER if LinkVector sb replaced with set<Link*>
531 */
532 struct LinkAndInfoAboutBackPointers {
533 Link_* fLink;
534 /**
535 * This is a vector, not a set, because it must reproduce the 'heights' of the linked tree structure.
536 * and nullptr entries in the list are 'sentinel values' indicating start of list (@Sterl why not just inert &fHead directly)
537 */
538 LinkVector_ fLinksPointingToReturnedLink; // @todo consider using set, and unclear what nullptr means in this vector, nor duplicates?
539 };
540 nonvirtual LinkAndInfoAboutBackPointers FindNearest_ (const variant<key_type, ForwardIterator>& keyOrI) const;
541
542 private:
543 // @todo ASK STERL MEANING OF LinkVector_ argument? Is it links to patch, or a starter on links for 'n'
544 // and why not have PatchLinks method?
545 nonvirtual void AddLink_ (Link_* n, const LinkVector_& linksToPatch);
546
547 private:
548 // @todo ask sterl meaning of LinkVector_ argument here? Why not have PatchLinks_ method?
549 nonvirtual void RemoveLink_ (Link_* n, const LinkVector_& linksToPatch);
550
551#if qStroika_Foundation_Debug_AssertionsChecked
552 private:
553 nonvirtual void Invariant_ () const noexcept;
554#endif
555
556 private:
557 nonvirtual void ShrinkHeadLinksIfNeeded_ ();
558
559 private:
560 nonvirtual void GrowHeadLinksIfNeeded_ (size_t newSize, Link_* linkToPointTo);
561
562 private:
563 nonvirtual size_t DetermineLinkHeight_ () const;
564
565#if qCompilerAndStdLib_RequiresNotMatchInlineOutOfLineForTemplateClassBeingDefined_Buggy
566 private:
567 bool Add1_ (ArgByValueType<key_type> key, ForwardIterator* oAddedI);
568 template <typename CHECK_T = MAPPED_TYPE>
569 bool Add2_ (ArgByValueType<key_type> key, ArgByValueType<CHECK_T> val, ForwardIterator* oAddedI);
570#endif
571
572 private:
573 [[no_unique_address]] KeyComparerType fKeyThreeWayComparer_{};
574 size_t fLength_{0};
575 [[no_unique_address]] mutable StatsType fStats_{};
576 };
577
578 /*
579 * ForwardIterator allows you to iterate over a SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>. It is not safe to use a ForwardIterator after any
580 * update to the SkipList.
581 */
582 template <typename KEY_TYPE, typename MAPPED_TYPE, SkipList_Support::IValidTraits<KEY_TYPE> TRAITS>
583 class SkipList<KEY_TYPE, MAPPED_TYPE, TRAITS>::ForwardIterator {
584 public:
585 // stuff STL requires you to set to look like an iterator
586 using iterator_category = forward_iterator_tag;
588 using difference_type = ptrdiff_t;
589 using pointer = const value_type*;
590 using reference = const value_type&;
591
592 public:
593 /**
594 * /0 overload: sets iterator to 'end' - sentinel
595 * /1 (data) overload: sets iterator to begin
596 * /2 (data,startAt) overload: sets iterator to startAt
597 */
598 constexpr ForwardIterator () noexcept = default;
599 explicit constexpr ForwardIterator (const SkipList* data) noexcept;
600 explicit constexpr ForwardIterator (const SkipList* data, UnderlyingIteratorRep startAt) noexcept;
601 constexpr ForwardIterator (const ForwardIterator&) noexcept = default;
602 constexpr ForwardIterator (ForwardIterator&&) noexcept = default;
603
604 public:
605 nonvirtual ForwardIterator& operator= (const ForwardIterator&) = default;
606 nonvirtual ForwardIterator& operator= (ForwardIterator&&) noexcept = default;
607
608#if qStroika_Foundation_Debug_AssertionsChecked
609 public:
610 ~ForwardIterator ();
611#endif
612
613 public:
614 /**
615 * return true if iterator not Done
616 */
617 explicit operator bool () const;
618
619 public:
620 nonvirtual bool Done () const noexcept;
621
622 public:
623 nonvirtual const value_type& operator* () const; // Error to call if Done (), otherwise OK
624
625 public:
626 nonvirtual const value_type* operator->() const; // Error to call if Done (), otherwise OK
627
628 public:
629 /**
630 * \note Runtime performance/complexity:
631 * Average/WorseCase: O(N) - super slow cuz have to traverse on average half the list
632 *
633 * \pre data == fData_ argument constructed with (or as adjusted by Move...); api takes extra param so release builds need not store fData_
634 */
635 nonvirtual size_t CurrentIndex (const SkipList* data) const;
636
637 public:
638 /**
639 * \pre GetUnderlyingData() == rhs.GetUnderlyingData (), or special case of one or the other is nullptr
640 */
641 constexpr bool operator== (const ForwardIterator& rhs) const;
642
643 public:
644 constexpr UnderlyingIteratorRep GetUnderlyingIteratorRep () const;
645
646 public:
647 nonvirtual void SetUnderlyingIteratorRep (const UnderlyingIteratorRep l);
648
649 public:
650 nonvirtual ForwardIterator& operator++ ();
651 nonvirtual ForwardIterator operator++ (int);
652
653 public:
654 // safe to update in place (doesn't change iterators) since doesn't change order of list (since not updating key)
655 template <typename CHECKED_T = MAPPED_TYPE>
656 nonvirtual void UpdateValue (ArgByValueType<CHECKED_T> newValue)
657 requires (not same_as<MAPPED_TYPE, void>);
658
659 public:
660 /**
661 * For debugging, assert the iterator data matches argument data
662 */
663 constexpr void AssertDataMatches (const SkipList* data) const;
664
665 public:
666 constexpr void Invariant () const noexcept;
667
668#if qStroika_Foundation_Debug_AssertionsChecked
669 private:
670 nonvirtual void Invariant_ () const noexcept;
671#endif
672
673 private:
674 const Link_* fCurrent_{nullptr};
675#if qStroika_Foundation_Debug_AssertionsChecked
676 const SkipList* fData_{nullptr};
677#endif
678
679 private:
680 friend class SkipList;
681 };
682
683 static_assert (ranges::input_range<SkipList<int, int>>); // smoke test - make sure basic iteration etc should work (allows formattable to work)
684
685}
686
687/*
688 ********************************************************************************
689 ***************************** Implementation Details ***************************
690 ********************************************************************************
691 */
692#include "SkipList.inl"
693
694#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 void MoveIteratorHereAfterClone(ForwardIterator *pi, const SkipList *movedFrom) const
Definition SkipList.inl:187
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
simple wrapper on Find() - but returning optional instead of iterators or pointers
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...