Coverage Report

Created: 2025-10-26 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/memtable/skiplist.h
Line
Count
Source
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
//
6
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7
// Use of this source code is governed by a BSD-style license that can be
8
// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
//
10
// Thread safety
11
// -------------
12
//
13
// Writes require external synchronization, most likely a mutex.
14
// Reads require a guarantee that the SkipList will not be destroyed
15
// while the read is in progress.  Apart from that, reads progress
16
// without any internal locking or synchronization.
17
//
18
// Invariants:
19
//
20
// (1) Allocated nodes are never deleted until the SkipList is
21
// destroyed.  This is trivially guaranteed by the code since we
22
// never delete any skip list nodes.
23
//
24
// (2) The contents of a Node except for the next/prev pointers are
25
// immutable after the Node has been linked into the SkipList.
26
// Only Insert() modifies the list, and it is careful to initialize
27
// a node and use release-stores to publish the nodes in one or
28
// more lists.
29
//
30
// ... prev vs. next pointer ordering ...
31
//
32
33
#pragma once
34
#include <assert.h>
35
#include <stdlib.h>
36
37
#include "memory/allocator.h"
38
#include "port/port.h"
39
#include "util/atomic.h"
40
#include "util/random.h"
41
42
namespace ROCKSDB_NAMESPACE {
43
44
template <typename Key, class Comparator>
45
class SkipList {
46
 private:
47
  struct Node;
48
49
 public:
50
  // Create a new SkipList object that will use "cmp" for comparing keys,
51
  // and will allocate memory using "*allocator".  Objects allocated in the
52
  // allocator must remain allocated for the lifetime of the skiplist object.
53
  explicit SkipList(Comparator cmp, Allocator* allocator,
54
                    int32_t max_height = 12, int32_t branching_factor = 4);
55
  // No copying allowed
56
  SkipList(const SkipList&) = delete;
57
  void operator=(const SkipList&) = delete;
58
59
  // Insert key into the list.
60
  // REQUIRES: nothing that compares equal to key is currently in the list.
61
  void Insert(const Key& key);
62
63
  // Returns true iff an entry that compares equal to key is in the list.
64
  bool Contains(const Key& key) const;
65
66
  // Return estimated number of entries from `start_ikey` to `end_ikey`.
67
  uint64_t ApproximateNumEntries(const Slice& start_ikey,
68
                                 const Slice& end_ikey) const;
69
70
  // Iteration over the contents of a skip list
71
  class Iterator {
72
   public:
73
    // Initialize an iterator over the specified list.
74
    // The returned iterator is not valid.
75
    explicit Iterator(const SkipList* list);
76
77
    // Change the underlying skiplist used for this iterator
78
    // This enables us not changing the iterator without deallocating
79
    // an old one and then allocating a new one
80
    void SetList(const SkipList* list);
81
82
    // Returns true iff the iterator is positioned at a valid node.
83
    bool Valid() const;
84
85
    // Returns the key at the current position.
86
    // REQUIRES: Valid()
87
    const Key& key() const;
88
89
    // Advances to the next position.
90
    // REQUIRES: Valid()
91
    void Next();
92
93
    // Advances to the previous position.
94
    // REQUIRES: Valid()
95
    void Prev();
96
97
    // Advance to the first entry with a key >= target
98
    void Seek(const Key& target);
99
100
    // Retreat to the last entry with a key <= target
101
    void SeekForPrev(const Key& target);
102
103
    // Position at the first entry in list.
104
    // Final state of iterator is Valid() iff list is not empty.
105
    void SeekToFirst();
106
107
    // Position at the last entry in list.
108
    // Final state of iterator is Valid() iff list is not empty.
109
    void SeekToLast();
110
111
   private:
112
    const SkipList* list_;
113
    Node* node_;
114
    // Intentionally copyable
115
  };
116
117
 private:
118
  const uint16_t kMaxHeight_;
119
  const uint16_t kBranching_;
120
  const uint32_t kScaledInverseBranching_;
121
122
  // Immutable after construction
123
  Comparator const compare_;
124
  Allocator* const allocator_;  // Allocator used for allocations of nodes
125
126
  Node* const head_;
127
128
  // Modified only by Insert().  Read racily by readers, but stale
129
  // values are ok.
130
  RelaxedAtomic<int> max_height_;  // Height of the entire list
131
132
  // Used for optimizing sequential insert patterns.  Tricky.  prev_[i] for
133
  // i up to max_height_ is the predecessor of prev_[0] and prev_height_
134
  // is the height of prev_[0].  prev_[0] can only be equal to head before
135
  // insertion, in which case max_height_ and prev_height_ are 1.
136
  int32_t prev_height_;
137
  Node** prev_;
138
139
0
  inline int GetMaxHeight() const { return max_height_.LoadRelaxed(); }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::GetMaxHeight() const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::GetMaxHeight() const
140
141
  Node* NewNode(const Key& key, int height);
142
  int RandomHeight();
143
0
  bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }
144
0
  bool LessThan(const Key& a, const Key& b) const {
145
0
    return (compare_(a, b) < 0);
146
0
  }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::LessThan(rocksdb::WriteBatchIndexEntry* const&, rocksdb::WriteBatchIndexEntry* const&) const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::LessThan(char const* const&, char const* const&) const
147
148
  // Return true if key is greater than the data stored in "n"
149
  bool KeyIsAfterNode(const Key& key, Node* n) const;
150
151
  // Returns the earliest node with a key >= key.
152
  // Return nullptr if there is no such node.
153
  Node* FindGreaterOrEqual(const Key& key) const;
154
155
  // Return the latest node with a key < key.
156
  // Return head_ if there is no such node.
157
  // Fills prev[level] with pointer to previous node at "level" for every
158
  // level in [0..max_height_-1], if prev is non-null.
159
  Node* FindLessThan(const Key& key, Node** prev = nullptr) const;
160
161
  // Return the last node in the list.
162
  // Return head_ if list is empty.
163
  Node* FindLast() const;
164
};
165
166
// Implementation details follow
167
template <typename Key, class Comparator>
168
struct SkipList<Key, Comparator>::Node {
169
0
  explicit Node(const Key& k) : key(k) {}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node::Node(rocksdb::WriteBatchIndexEntry* const&)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node::Node(char const* const&)
170
171
  Key const key;
172
173
  // Accessors/mutators for links.  Wrapped in methods so we can
174
  // add the appropriate barriers as necessary.
175
0
  Node* Next(int n) {
176
0
    assert(n >= 0);
177
    // Use an 'acquire load' so that we observe a fully initialized
178
    // version of the returned Node.
179
0
    return (next_[n].Load());
180
0
  }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node::Next(int)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node::Next(int)
181
0
  void SetNext(int n, Node* x) {
182
0
    assert(n >= 0);
183
    // Use a 'release store' so that anybody who reads through this
184
    // pointer observes a fully initialized version of the inserted node.
185
0
    next_[n].Store(x);
186
0
  }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node::SetNext(int, rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node*)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node::SetNext(int, rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node*)
187
188
  // No-barrier variants that can be safely used in a few locations.
189
0
  Node* NoBarrier_Next(int n) {
190
0
    assert(n >= 0);
191
0
    return next_[n].LoadRelaxed();
192
0
  }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node::NoBarrier_Next(int)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node::NoBarrier_Next(int)
193
0
  void NoBarrier_SetNext(int n, Node* x) {
194
0
    assert(n >= 0);
195
0
    next_[n].StoreRelaxed(x);
196
0
  }
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node::NoBarrier_SetNext(int, rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node*)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node::NoBarrier_SetNext(int, rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node*)
197
198
 private:
199
  // Array of length equal to the node height.  next_[0] is lowest level link.
200
  AcqRelAtomic<Node*> next_[1];
201
};
202
203
template <typename Key, class Comparator>
204
typename SkipList<Key, Comparator>::Node* SkipList<Key, Comparator>::NewNode(
205
0
    const Key& key, int height) {
206
0
  char* mem = allocator_->AllocateAligned(
207
0
      sizeof(Node) + sizeof(AcqRelAtomic<Node*>) * (height - 1));
208
0
  return new (mem) Node(key);
209
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::NewNode(rocksdb::WriteBatchIndexEntry* const&, int)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::NewNode(char const* const&, int)
210
211
template <typename Key, class Comparator>
212
0
inline SkipList<Key, Comparator>::Iterator::Iterator(const SkipList* list) {
213
0
  SetList(list);
214
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::Iterator(rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&> const*)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::Iterator(rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&> const*)
215
216
template <typename Key, class Comparator>
217
0
inline void SkipList<Key, Comparator>::Iterator::SetList(const SkipList* list) {
218
0
  list_ = list;
219
0
  node_ = nullptr;
220
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::SetList(rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&> const*)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::SetList(rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&> const*)
221
222
template <typename Key, class Comparator>
223
0
inline bool SkipList<Key, Comparator>::Iterator::Valid() const {
224
0
  return node_ != nullptr;
225
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::Valid() const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::Valid() const
226
227
template <typename Key, class Comparator>
228
0
inline const Key& SkipList<Key, Comparator>::Iterator::key() const {
229
0
  assert(Valid());
230
0
  return node_->key;
231
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::key() const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::key() const
232
233
template <typename Key, class Comparator>
234
0
inline void SkipList<Key, Comparator>::Iterator::Next() {
235
0
  assert(Valid());
236
0
  node_ = node_->Next(0);
237
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::Next()
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::Next()
238
239
template <typename Key, class Comparator>
240
0
inline void SkipList<Key, Comparator>::Iterator::Prev() {
241
  // Instead of using explicit "prev" links, we just search for the
242
  // last node that falls before key.
243
0
  assert(Valid());
244
0
  node_ = list_->FindLessThan(node_->key);
245
0
  if (node_ == list_->head_) {
246
0
    node_ = nullptr;
247
0
  }
248
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::Prev()
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::Prev()
249
250
template <typename Key, class Comparator>
251
0
inline void SkipList<Key, Comparator>::Iterator::Seek(const Key& target) {
252
0
  node_ = list_->FindGreaterOrEqual(target);
253
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::Seek(rocksdb::WriteBatchIndexEntry* const&)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::Seek(char const* const&)
254
255
template <typename Key, class Comparator>
256
inline void SkipList<Key, Comparator>::Iterator::SeekForPrev(
257
0
    const Key& target) {
258
0
  Seek(target);
259
0
  if (!Valid()) {
260
0
    SeekToLast();
261
0
  }
262
0
  while (Valid() && list_->LessThan(target, key())) {
263
0
    Prev();
264
0
  }
265
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::SeekForPrev(rocksdb::WriteBatchIndexEntry* const&)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::SeekForPrev(char const* const&)
266
267
template <typename Key, class Comparator>
268
0
inline void SkipList<Key, Comparator>::Iterator::SeekToFirst() {
269
0
  node_ = list_->head_->Next(0);
270
0
}
271
272
template <typename Key, class Comparator>
273
0
inline void SkipList<Key, Comparator>::Iterator::SeekToLast() {
274
0
  node_ = list_->FindLast();
275
0
  if (node_ == list_->head_) {
276
0
    node_ = nullptr;
277
0
  }
278
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Iterator::SeekToLast()
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Iterator::SeekToLast()
279
280
template <typename Key, class Comparator>
281
0
int SkipList<Key, Comparator>::RandomHeight() {
282
0
  auto rnd = Random::GetTLSInstance();
283
284
  // Increase height with probability 1 in kBranching
285
0
  int height = 1;
286
0
  while (height < kMaxHeight_ && rnd->Next() < kScaledInverseBranching_) {
287
0
    height++;
288
0
  }
289
0
  assert(height > 0);
290
0
  assert(height <= kMaxHeight_);
291
0
  return height;
292
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::RandomHeight()
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::RandomHeight()
293
294
template <typename Key, class Comparator>
295
0
bool SkipList<Key, Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
296
  // nullptr n is considered infinite
297
0
  return (n != nullptr) && (compare_(n->key, key) < 0);
298
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::KeyIsAfterNode(rocksdb::WriteBatchIndexEntry* const&, rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node*) const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::KeyIsAfterNode(char const* const&, rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node*) const
299
300
template <typename Key, class Comparator>
301
typename SkipList<Key, Comparator>::Node*
302
0
SkipList<Key, Comparator>::FindGreaterOrEqual(const Key& key) const {
303
  // Note: It looks like we could reduce duplication by implementing
304
  // this function as FindLessThan(key)->Next(0), but we wouldn't be able
305
  // to exit early on equality and the result wouldn't even be correct.
306
  // A concurrent insert might occur after FindLessThan(key) but before
307
  // we get a chance to call Next(0).
308
0
  Node* x = head_;
309
0
  int level = GetMaxHeight() - 1;
310
0
  Node* last_bigger = nullptr;
311
0
  while (true) {
312
0
    assert(x != nullptr);
313
0
    Node* next = x->Next(level);
314
    // Make sure the lists are sorted
315
0
    assert(x == head_ || next == nullptr || KeyIsAfterNode(next->key, x));
316
    // Make sure we haven't overshot during our search
317
0
    assert(x == head_ || KeyIsAfterNode(key, x));
318
0
    int cmp =
319
0
        (next == nullptr || next == last_bigger) ? 1 : compare_(next->key, key);
320
0
    if (cmp == 0 || (cmp > 0 && level == 0)) {
321
0
      return next;
322
0
    } else if (cmp < 0) {
323
      // Keep searching in this list
324
0
      x = next;
325
0
    } else {
326
      // Switch to next list, reuse compare_() result
327
0
      last_bigger = next;
328
0
      level--;
329
0
    }
330
0
  }
331
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::FindGreaterOrEqual(rocksdb::WriteBatchIndexEntry* const&) const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::FindGreaterOrEqual(char const* const&) const
332
333
template <typename Key, class Comparator>
334
typename SkipList<Key, Comparator>::Node*
335
0
SkipList<Key, Comparator>::FindLessThan(const Key& key, Node** prev) const {
336
0
  Node* x = head_;
337
0
  int level = GetMaxHeight() - 1;
338
  // KeyIsAfter(key, last_not_after) is definitely false
339
0
  Node* last_not_after = nullptr;
340
0
  while (true) {
341
0
    assert(x != nullptr);
342
0
    Node* next = x->Next(level);
343
0
    assert(x == head_ || next == nullptr || KeyIsAfterNode(next->key, x));
344
0
    assert(x == head_ || KeyIsAfterNode(key, x));
345
0
    if (next != last_not_after && KeyIsAfterNode(key, next)) {
346
      // Keep searching in this list
347
0
      x = next;
348
0
    } else {
349
0
      if (prev != nullptr) {
350
0
        prev[level] = x;
351
0
      }
352
0
      if (level == 0) {
353
0
        return x;
354
0
      } else {
355
        // Switch to next list, reuse KeyIUsAfterNode() result
356
0
        last_not_after = next;
357
0
        level--;
358
0
      }
359
0
    }
360
0
  }
361
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::FindLessThan(rocksdb::WriteBatchIndexEntry* const&, rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Node**) const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::FindLessThan(char const* const&, rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Node**) const
362
363
template <typename Key, class Comparator>
364
typename SkipList<Key, Comparator>::Node* SkipList<Key, Comparator>::FindLast()
365
0
    const {
366
0
  Node* x = head_;
367
0
  int level = GetMaxHeight() - 1;
368
0
  while (true) {
369
0
    Node* next = x->Next(level);
370
0
    if (next == nullptr) {
371
0
      if (level == 0) {
372
0
        return x;
373
0
      } else {
374
        // Switch to next list
375
0
        level--;
376
0
      }
377
0
    } else {
378
0
      x = next;
379
0
    }
380
0
  }
381
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::FindLast() const
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::FindLast() const
382
383
template <typename Key, class Comparator>
384
uint64_t SkipList<Key, Comparator>::ApproximateNumEntries(
385
    const Slice& start_ikey, const Slice& end_ikey) const {
386
  // See InlineSkipList<Comparator>::ApproximateNumEntries() (copy-paste)
387
  Node* lb = head_;
388
  Node* ub = nullptr;
389
  uint64_t count = 0;
390
  for (int level = GetMaxHeight() - 1; level >= 0; level--) {
391
    auto sufficient_samples = static_cast<uint64_t>(level) * kBranching_ + 10U;
392
    if (count >= sufficient_samples) {
393
      // No more counting; apply powers of kBranching and avoid floating point
394
      count *= kBranching_;
395
      continue;
396
    }
397
    count = 0;
398
    Node* next;
399
    // Get a more precise lower bound (for start key)
400
    for (;;) {
401
      next = lb->Next(level);
402
      if (next == ub) {
403
        break;
404
      }
405
      assert(next != nullptr);
406
      if (compare_(next->Key(), start_ikey) >= 0) {
407
        break;
408
      }
409
      lb = next;
410
    }
411
    // Count entries on this level until upper bound (for end key)
412
    for (;;) {
413
      if (next == ub) {
414
        break;
415
      }
416
      assert(next != nullptr);
417
      if (compare_(next->Key(), end_ikey) >= 0) {
418
        // Save refined upper bound to potentially save key comparison
419
        ub = next;
420
        break;
421
      }
422
      count++;
423
      next = next->Next(level);
424
    }
425
  }
426
  return count;
427
}
428
429
template <typename Key, class Comparator>
430
SkipList<Key, Comparator>::SkipList(const Comparator cmp, Allocator* allocator,
431
                                    int32_t max_height,
432
                                    int32_t branching_factor)
433
0
    : kMaxHeight_(static_cast<uint16_t>(max_height)),
434
0
      kBranching_(static_cast<uint16_t>(branching_factor)),
435
0
      kScaledInverseBranching_((Random::kMaxNext + 1) / kBranching_),
436
0
      compare_(cmp),
437
0
      allocator_(allocator),
438
0
      head_(NewNode({} /* any key will do */, max_height)),
439
0
      max_height_(1),
440
0
      prev_height_(1) {
441
0
  assert(max_height > 0 && kMaxHeight_ == static_cast<uint32_t>(max_height));
442
0
  assert(branching_factor > 0 &&
443
0
         kBranching_ == static_cast<uint32_t>(branching_factor));
444
0
  assert(kScaledInverseBranching_ > 0);
445
  // Allocate the prev_ Node* array, directly from the passed-in allocator.
446
  // prev_ does not need to be freed, as its life cycle is tied up with
447
  // the allocator as a whole.
448
0
  prev_ = reinterpret_cast<Node**>(
449
0
      allocator_->AllocateAligned(sizeof(Node*) * kMaxHeight_));
450
0
  for (int i = 0; i < kMaxHeight_; i++) {
451
0
    head_->SetNext(i, nullptr);
452
0
    prev_[i] = head_;
453
0
  }
454
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::SkipList(rocksdb::WriteBatchEntryComparator const&, rocksdb::Allocator*, int, int)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::SkipList(rocksdb::MemTableRep::KeyComparator const&, rocksdb::Allocator*, int, int)
455
456
template <typename Key, class Comparator>
457
0
void SkipList<Key, Comparator>::Insert(const Key& key) {
458
  // fast path for sequential insertion
459
0
  if (!KeyIsAfterNode(key, prev_[0]->NoBarrier_Next(0)) &&
460
0
      (prev_[0] == head_ || KeyIsAfterNode(key, prev_[0]))) {
461
0
    assert(prev_[0] != head_ || (prev_height_ == 1 && GetMaxHeight() == 1));
462
463
    // Outside of this method prev_[1..max_height_] is the predecessor
464
    // of prev_[0], and prev_height_ refers to prev_[0].  Inside Insert
465
    // prev_[0..max_height - 1] is the predecessor of key.  Switch from
466
    // the external state to the internal
467
0
    for (int i = 1; i < prev_height_; i++) {
468
0
      prev_[i] = prev_[0];
469
0
    }
470
0
  } else {
471
    // TODO(opt): we could use a NoBarrier predecessor search as an
472
    // optimization for architectures where memory_order_acquire needs
473
    // a synchronization instruction.  Doesn't matter on x86
474
0
    FindLessThan(key, prev_);
475
0
  }
476
477
  // Our data structure does not allow duplicate insertion
478
0
  assert(prev_[0]->Next(0) == nullptr || !Equal(key, prev_[0]->Next(0)->key));
479
480
0
  int height = RandomHeight();
481
0
  if (height > GetMaxHeight()) {
482
0
    for (int i = GetMaxHeight(); i < height; i++) {
483
0
      prev_[i] = head_;
484
0
    }
485
    // fprintf(stderr, "Change height from %d to %d\n", max_height_, height);
486
487
    // It is ok to mutate max_height_ without any synchronization
488
    // with concurrent readers.  A concurrent reader that observes
489
    // the new value of max_height_ will see either the old value of
490
    // new level pointers from head_ (nullptr), or a new value set in
491
    // the loop below.  In the former case the reader will
492
    // immediately drop to the next level since nullptr sorts after all
493
    // keys.  In the latter case the reader will use the new node.
494
0
    max_height_.StoreRelaxed(height);
495
0
  }
496
497
0
  Node* x = NewNode(key, height);
498
0
  for (int i = 0; i < height; i++) {
499
    // NoBarrier_SetNext() suffices since we will add a barrier when
500
    // we publish a pointer to "x" in prev[i].
501
0
    x->NoBarrier_SetNext(i, prev_[i]->NoBarrier_Next(i));
502
0
    prev_[i]->SetNext(i, x);
503
0
  }
504
0
  prev_[0] = x;
505
0
  prev_height_ = height;
506
0
}
Unexecuted instantiation: rocksdb::SkipList<rocksdb::WriteBatchIndexEntry*, rocksdb::WriteBatchEntryComparator const&>::Insert(rocksdb::WriteBatchIndexEntry* const&)
Unexecuted instantiation: rocksdb::SkipList<char const*, rocksdb::MemTableRep::KeyComparator const&>::Insert(char const* const&)
507
508
template <typename Key, class Comparator>
509
0
bool SkipList<Key, Comparator>::Contains(const Key& key) const {
510
0
  Node* x = FindGreaterOrEqual(key);
511
0
  if (x != nullptr && Equal(key, x->key)) {
512
0
    return true;
513
0
  } else {
514
0
    return false;
515
0
  }
516
0
}
517
518
}  // namespace ROCKSDB_NAMESPACE