filiptepper-leveldb-ruby 0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +24 -0
- data/README +72 -0
- data/ext/leveldb/extconf.rb +14 -0
- data/ext/leveldb/leveldb.cc +530 -0
- data/ext/leveldb/platform.rb +83 -0
- data/leveldb/Makefile +191 -0
- data/leveldb/build_detect_platform +160 -0
- data/leveldb/db/builder.cc +88 -0
- data/leveldb/db/builder.h +34 -0
- data/leveldb/db/c.cc +581 -0
- data/leveldb/db/corruption_test.cc +359 -0
- data/leveldb/db/db_bench.cc +970 -0
- data/leveldb/db/db_impl.cc +1448 -0
- data/leveldb/db/db_impl.h +194 -0
- data/leveldb/db/db_iter.cc +299 -0
- data/leveldb/db/db_iter.h +26 -0
- data/leveldb/db/db_test.cc +1901 -0
- data/leveldb/db/dbformat.cc +140 -0
- data/leveldb/db/dbformat.h +227 -0
- data/leveldb/db/dbformat_test.cc +112 -0
- data/leveldb/db/filename.cc +139 -0
- data/leveldb/db/filename.h +80 -0
- data/leveldb/db/filename_test.cc +122 -0
- data/leveldb/db/log_format.h +35 -0
- data/leveldb/db/log_reader.cc +259 -0
- data/leveldb/db/log_reader.h +108 -0
- data/leveldb/db/log_test.cc +500 -0
- data/leveldb/db/log_writer.cc +103 -0
- data/leveldb/db/log_writer.h +48 -0
- data/leveldb/db/memtable.cc +145 -0
- data/leveldb/db/memtable.h +91 -0
- data/leveldb/db/repair.cc +389 -0
- data/leveldb/db/skiplist.h +379 -0
- data/leveldb/db/skiplist_test.cc +378 -0
- data/leveldb/db/snapshot.h +66 -0
- data/leveldb/db/table_cache.cc +121 -0
- data/leveldb/db/table_cache.h +61 -0
- data/leveldb/db/version_edit.cc +266 -0
- data/leveldb/db/version_edit.h +107 -0
- data/leveldb/db/version_edit_test.cc +46 -0
- data/leveldb/db/version_set.cc +1402 -0
- data/leveldb/db/version_set.h +370 -0
- data/leveldb/db/version_set_test.cc +179 -0
- data/leveldb/db/write_batch.cc +147 -0
- data/leveldb/db/write_batch_internal.h +49 -0
- data/leveldb/db/write_batch_test.cc +120 -0
- data/leveldb/helpers/memenv/memenv.cc +374 -0
- data/leveldb/helpers/memenv/memenv.h +20 -0
- data/leveldb/helpers/memenv/memenv_test.cc +232 -0
- data/leveldb/include/leveldb/c.h +275 -0
- data/leveldb/include/leveldb/cache.h +99 -0
- data/leveldb/include/leveldb/comparator.h +63 -0
- data/leveldb/include/leveldb/db.h +161 -0
- data/leveldb/include/leveldb/env.h +323 -0
- data/leveldb/include/leveldb/filter_policy.h +70 -0
- data/leveldb/include/leveldb/iterator.h +100 -0
- data/leveldb/include/leveldb/options.h +195 -0
- data/leveldb/include/leveldb/slice.h +109 -0
- data/leveldb/include/leveldb/status.h +106 -0
- data/leveldb/include/leveldb/table.h +85 -0
- data/leveldb/include/leveldb/table_builder.h +92 -0
- data/leveldb/include/leveldb/write_batch.h +64 -0
- data/leveldb/port/atomic_pointer.h +144 -0
- data/leveldb/port/port.h +21 -0
- data/leveldb/port/port_android.cc +64 -0
- data/leveldb/port/port_android.h +159 -0
- data/leveldb/port/port_example.h +125 -0
- data/leveldb/port/port_posix.cc +50 -0
- data/leveldb/port/port_posix.h +129 -0
- data/leveldb/port/win/stdint.h +24 -0
- data/leveldb/table/block.cc +267 -0
- data/leveldb/table/block.h +44 -0
- data/leveldb/table/block_builder.cc +109 -0
- data/leveldb/table/block_builder.h +57 -0
- data/leveldb/table/filter_block.cc +111 -0
- data/leveldb/table/filter_block.h +68 -0
- data/leveldb/table/filter_block_test.cc +128 -0
- data/leveldb/table/format.cc +145 -0
- data/leveldb/table/format.h +108 -0
- data/leveldb/table/iterator.cc +67 -0
- data/leveldb/table/iterator_wrapper.h +63 -0
- data/leveldb/table/merger.cc +197 -0
- data/leveldb/table/merger.h +26 -0
- data/leveldb/table/table.cc +276 -0
- data/leveldb/table/table_builder.cc +270 -0
- data/leveldb/table/table_test.cc +838 -0
- data/leveldb/table/two_level_iterator.cc +182 -0
- data/leveldb/table/two_level_iterator.h +34 -0
- data/leveldb/util/arena.cc +68 -0
- data/leveldb/util/arena.h +68 -0
- data/leveldb/util/arena_test.cc +68 -0
- data/leveldb/util/bloom.cc +95 -0
- data/leveldb/util/bloom_test.cc +159 -0
- data/leveldb/util/cache.cc +328 -0
- data/leveldb/util/cache_test.cc +186 -0
- data/leveldb/util/coding.cc +194 -0
- data/leveldb/util/coding.h +104 -0
- data/leveldb/util/coding_test.cc +173 -0
- data/leveldb/util/comparator.cc +76 -0
- data/leveldb/util/crc32c.cc +332 -0
- data/leveldb/util/crc32c.h +45 -0
- data/leveldb/util/crc32c_test.cc +72 -0
- data/leveldb/util/env.cc +96 -0
- data/leveldb/util/env_posix.cc +609 -0
- data/leveldb/util/env_test.cc +104 -0
- data/leveldb/util/filter_policy.cc +11 -0
- data/leveldb/util/hash.cc +45 -0
- data/leveldb/util/hash.h +19 -0
- data/leveldb/util/histogram.cc +139 -0
- data/leveldb/util/histogram.h +42 -0
- data/leveldb/util/logging.cc +81 -0
- data/leveldb/util/logging.h +47 -0
- data/leveldb/util/mutexlock.h +39 -0
- data/leveldb/util/options.cc +29 -0
- data/leveldb/util/posix_logger.h +98 -0
- data/leveldb/util/random.h +59 -0
- data/leveldb/util/status.cc +75 -0
- data/leveldb/util/testharness.cc +77 -0
- data/leveldb/util/testharness.h +138 -0
- data/leveldb/util/testutil.cc +51 -0
- data/leveldb/util/testutil.h +53 -0
- data/lib/leveldb.rb +76 -0
- metadata +175 -0
@@ -0,0 +1,379 @@
|
|
1
|
+
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
4
|
+
//
|
5
|
+
// Thread safety
|
6
|
+
// -------------
|
7
|
+
//
|
8
|
+
// Writes require external synchronization, most likely a mutex.
|
9
|
+
// Reads require a guarantee that the SkipList will not be destroyed
|
10
|
+
// while the read is in progress. Apart from that, reads progress
|
11
|
+
// without any internal locking or synchronization.
|
12
|
+
//
|
13
|
+
// Invariants:
|
14
|
+
//
|
15
|
+
// (1) Allocated nodes are never deleted until the SkipList is
|
16
|
+
// destroyed. This is trivially guaranteed by the code since we
|
17
|
+
// never delete any skip list nodes.
|
18
|
+
//
|
19
|
+
// (2) The contents of a Node except for the next/prev pointers are
|
20
|
+
// immutable after the Node has been linked into the SkipList.
|
21
|
+
// Only Insert() modifies the list, and it is careful to initialize
|
22
|
+
// a node and use release-stores to publish the nodes in one or
|
23
|
+
// more lists.
|
24
|
+
//
|
25
|
+
// ... prev vs. next pointer ordering ...
|
26
|
+
|
27
|
+
#include <assert.h>
|
28
|
+
#include <stdlib.h>
|
29
|
+
#include "port/port.h"
|
30
|
+
#include "util/arena.h"
|
31
|
+
#include "util/random.h"
|
32
|
+
|
33
|
+
namespace leveldb {
|
34
|
+
|
35
|
+
class Arena;
|
36
|
+
|
37
|
+
template<typename Key, class Comparator>
|
38
|
+
class SkipList {
|
39
|
+
private:
|
40
|
+
struct Node;
|
41
|
+
|
42
|
+
public:
|
43
|
+
// Create a new SkipList object that will use "cmp" for comparing keys,
|
44
|
+
// and will allocate memory using "*arena". Objects allocated in the arena
|
45
|
+
// must remain allocated for the lifetime of the skiplist object.
|
46
|
+
explicit SkipList(Comparator cmp, Arena* arena);
|
47
|
+
|
48
|
+
// Insert key into the list.
|
49
|
+
// REQUIRES: nothing that compares equal to key is currently in the list.
|
50
|
+
void Insert(const Key& key);
|
51
|
+
|
52
|
+
// Returns true iff an entry that compares equal to key is in the list.
|
53
|
+
bool Contains(const Key& key) const;
|
54
|
+
|
55
|
+
// Iteration over the contents of a skip list
|
56
|
+
class Iterator {
|
57
|
+
public:
|
58
|
+
// Initialize an iterator over the specified list.
|
59
|
+
// The returned iterator is not valid.
|
60
|
+
explicit Iterator(const SkipList* list);
|
61
|
+
|
62
|
+
// Returns true iff the iterator is positioned at a valid node.
|
63
|
+
bool Valid() const;
|
64
|
+
|
65
|
+
// Returns the key at the current position.
|
66
|
+
// REQUIRES: Valid()
|
67
|
+
const Key& key() const;
|
68
|
+
|
69
|
+
// Advances to the next position.
|
70
|
+
// REQUIRES: Valid()
|
71
|
+
void Next();
|
72
|
+
|
73
|
+
// Advances to the previous position.
|
74
|
+
// REQUIRES: Valid()
|
75
|
+
void Prev();
|
76
|
+
|
77
|
+
// Advance to the first entry with a key >= target
|
78
|
+
void Seek(const Key& target);
|
79
|
+
|
80
|
+
// Position at the first entry in list.
|
81
|
+
// Final state of iterator is Valid() iff list is not empty.
|
82
|
+
void SeekToFirst();
|
83
|
+
|
84
|
+
// Position at the last entry in list.
|
85
|
+
// Final state of iterator is Valid() iff list is not empty.
|
86
|
+
void SeekToLast();
|
87
|
+
|
88
|
+
private:
|
89
|
+
const SkipList* list_;
|
90
|
+
Node* node_;
|
91
|
+
// Intentionally copyable
|
92
|
+
};
|
93
|
+
|
94
|
+
private:
|
95
|
+
enum { kMaxHeight = 12 };
|
96
|
+
|
97
|
+
// Immutable after construction
|
98
|
+
Comparator const compare_;
|
99
|
+
Arena* const arena_; // Arena used for allocations of nodes
|
100
|
+
|
101
|
+
Node* const head_;
|
102
|
+
|
103
|
+
// Modified only by Insert(). Read racily by readers, but stale
|
104
|
+
// values are ok.
|
105
|
+
port::AtomicPointer max_height_; // Height of the entire list
|
106
|
+
|
107
|
+
inline int GetMaxHeight() const {
|
108
|
+
return static_cast<int>(
|
109
|
+
reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load()));
|
110
|
+
}
|
111
|
+
|
112
|
+
// Read/written only by Insert().
|
113
|
+
Random rnd_;
|
114
|
+
|
115
|
+
Node* NewNode(const Key& key, int height);
|
116
|
+
int RandomHeight();
|
117
|
+
bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }
|
118
|
+
|
119
|
+
// Return true if key is greater than the data stored in "n"
|
120
|
+
bool KeyIsAfterNode(const Key& key, Node* n) const;
|
121
|
+
|
122
|
+
// Return the earliest node that comes at or after key.
|
123
|
+
// Return NULL if there is no such node.
|
124
|
+
//
|
125
|
+
// If prev is non-NULL, fills prev[level] with pointer to previous
|
126
|
+
// node at "level" for every level in [0..max_height_-1].
|
127
|
+
Node* FindGreaterOrEqual(const Key& key, Node** prev) const;
|
128
|
+
|
129
|
+
// Return the latest node with a key < key.
|
130
|
+
// Return head_ if there is no such node.
|
131
|
+
Node* FindLessThan(const Key& key) const;
|
132
|
+
|
133
|
+
// Return the last node in the list.
|
134
|
+
// Return head_ if list is empty.
|
135
|
+
Node* FindLast() const;
|
136
|
+
|
137
|
+
// No copying allowed
|
138
|
+
SkipList(const SkipList&);
|
139
|
+
void operator=(const SkipList&);
|
140
|
+
};
|
141
|
+
|
142
|
+
// Implementation details follow
|
143
|
+
template<typename Key, class Comparator>
|
144
|
+
struct SkipList<Key,Comparator>::Node {
|
145
|
+
explicit Node(const Key& k) : key(k) { }
|
146
|
+
|
147
|
+
Key const key;
|
148
|
+
|
149
|
+
// Accessors/mutators for links. Wrapped in methods so we can
|
150
|
+
// add the appropriate barriers as necessary.
|
151
|
+
Node* Next(int n) {
|
152
|
+
assert(n >= 0);
|
153
|
+
// Use an 'acquire load' so that we observe a fully initialized
|
154
|
+
// version of the returned Node.
|
155
|
+
return reinterpret_cast<Node*>(next_[n].Acquire_Load());
|
156
|
+
}
|
157
|
+
void SetNext(int n, Node* x) {
|
158
|
+
assert(n >= 0);
|
159
|
+
// Use a 'release store' so that anybody who reads through this
|
160
|
+
// pointer observes a fully initialized version of the inserted node.
|
161
|
+
next_[n].Release_Store(x);
|
162
|
+
}
|
163
|
+
|
164
|
+
// No-barrier variants that can be safely used in a few locations.
|
165
|
+
Node* NoBarrier_Next(int n) {
|
166
|
+
assert(n >= 0);
|
167
|
+
return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());
|
168
|
+
}
|
169
|
+
void NoBarrier_SetNext(int n, Node* x) {
|
170
|
+
assert(n >= 0);
|
171
|
+
next_[n].NoBarrier_Store(x);
|
172
|
+
}
|
173
|
+
|
174
|
+
private:
|
175
|
+
// Array of length equal to the node height. next_[0] is lowest level link.
|
176
|
+
port::AtomicPointer next_[1];
|
177
|
+
};
|
178
|
+
|
179
|
+
template<typename Key, class Comparator>
|
180
|
+
typename SkipList<Key,Comparator>::Node*
|
181
|
+
SkipList<Key,Comparator>::NewNode(const Key& key, int height) {
|
182
|
+
char* mem = arena_->AllocateAligned(
|
183
|
+
sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));
|
184
|
+
return new (mem) Node(key);
|
185
|
+
}
|
186
|
+
|
187
|
+
template<typename Key, class Comparator>
|
188
|
+
inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) {
|
189
|
+
list_ = list;
|
190
|
+
node_ = NULL;
|
191
|
+
}
|
192
|
+
|
193
|
+
template<typename Key, class Comparator>
|
194
|
+
inline bool SkipList<Key,Comparator>::Iterator::Valid() const {
|
195
|
+
return node_ != NULL;
|
196
|
+
}
|
197
|
+
|
198
|
+
template<typename Key, class Comparator>
|
199
|
+
inline const Key& SkipList<Key,Comparator>::Iterator::key() const {
|
200
|
+
assert(Valid());
|
201
|
+
return node_->key;
|
202
|
+
}
|
203
|
+
|
204
|
+
template<typename Key, class Comparator>
|
205
|
+
inline void SkipList<Key,Comparator>::Iterator::Next() {
|
206
|
+
assert(Valid());
|
207
|
+
node_ = node_->Next(0);
|
208
|
+
}
|
209
|
+
|
210
|
+
template<typename Key, class Comparator>
|
211
|
+
inline void SkipList<Key,Comparator>::Iterator::Prev() {
|
212
|
+
// Instead of using explicit "prev" links, we just search for the
|
213
|
+
// last node that falls before key.
|
214
|
+
assert(Valid());
|
215
|
+
node_ = list_->FindLessThan(node_->key);
|
216
|
+
if (node_ == list_->head_) {
|
217
|
+
node_ = NULL;
|
218
|
+
}
|
219
|
+
}
|
220
|
+
|
221
|
+
template<typename Key, class Comparator>
|
222
|
+
inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) {
|
223
|
+
node_ = list_->FindGreaterOrEqual(target, NULL);
|
224
|
+
}
|
225
|
+
|
226
|
+
template<typename Key, class Comparator>
|
227
|
+
inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() {
|
228
|
+
node_ = list_->head_->Next(0);
|
229
|
+
}
|
230
|
+
|
231
|
+
template<typename Key, class Comparator>
|
232
|
+
inline void SkipList<Key,Comparator>::Iterator::SeekToLast() {
|
233
|
+
node_ = list_->FindLast();
|
234
|
+
if (node_ == list_->head_) {
|
235
|
+
node_ = NULL;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
template<typename Key, class Comparator>
|
240
|
+
int SkipList<Key,Comparator>::RandomHeight() {
|
241
|
+
// Increase height with probability 1 in kBranching
|
242
|
+
static const unsigned int kBranching = 4;
|
243
|
+
int height = 1;
|
244
|
+
while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {
|
245
|
+
height++;
|
246
|
+
}
|
247
|
+
assert(height > 0);
|
248
|
+
assert(height <= kMaxHeight);
|
249
|
+
return height;
|
250
|
+
}
|
251
|
+
|
252
|
+
template<typename Key, class Comparator>
|
253
|
+
bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
|
254
|
+
// NULL n is considered infinite
|
255
|
+
return (n != NULL) && (compare_(n->key, key) < 0);
|
256
|
+
}
|
257
|
+
|
258
|
+
template<typename Key, class Comparator>
|
259
|
+
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)
|
260
|
+
const {
|
261
|
+
Node* x = head_;
|
262
|
+
int level = GetMaxHeight() - 1;
|
263
|
+
while (true) {
|
264
|
+
Node* next = x->Next(level);
|
265
|
+
if (KeyIsAfterNode(key, next)) {
|
266
|
+
// Keep searching in this list
|
267
|
+
x = next;
|
268
|
+
} else {
|
269
|
+
if (prev != NULL) prev[level] = x;
|
270
|
+
if (level == 0) {
|
271
|
+
return next;
|
272
|
+
} else {
|
273
|
+
// Switch to next list
|
274
|
+
level--;
|
275
|
+
}
|
276
|
+
}
|
277
|
+
}
|
278
|
+
}
|
279
|
+
|
280
|
+
template<typename Key, class Comparator>
|
281
|
+
typename SkipList<Key,Comparator>::Node*
|
282
|
+
SkipList<Key,Comparator>::FindLessThan(const Key& key) const {
|
283
|
+
Node* x = head_;
|
284
|
+
int level = GetMaxHeight() - 1;
|
285
|
+
while (true) {
|
286
|
+
assert(x == head_ || compare_(x->key, key) < 0);
|
287
|
+
Node* next = x->Next(level);
|
288
|
+
if (next == NULL || compare_(next->key, key) >= 0) {
|
289
|
+
if (level == 0) {
|
290
|
+
return x;
|
291
|
+
} else {
|
292
|
+
// Switch to next list
|
293
|
+
level--;
|
294
|
+
}
|
295
|
+
} else {
|
296
|
+
x = next;
|
297
|
+
}
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
template<typename Key, class Comparator>
|
302
|
+
typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()
|
303
|
+
const {
|
304
|
+
Node* x = head_;
|
305
|
+
int level = GetMaxHeight() - 1;
|
306
|
+
while (true) {
|
307
|
+
Node* next = x->Next(level);
|
308
|
+
if (next == NULL) {
|
309
|
+
if (level == 0) {
|
310
|
+
return x;
|
311
|
+
} else {
|
312
|
+
// Switch to next list
|
313
|
+
level--;
|
314
|
+
}
|
315
|
+
} else {
|
316
|
+
x = next;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
template<typename Key, class Comparator>
|
322
|
+
SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena)
|
323
|
+
: compare_(cmp),
|
324
|
+
arena_(arena),
|
325
|
+
head_(NewNode(0 /* any key will do */, kMaxHeight)),
|
326
|
+
max_height_(reinterpret_cast<void*>(1)),
|
327
|
+
rnd_(0xdeadbeef) {
|
328
|
+
for (int i = 0; i < kMaxHeight; i++) {
|
329
|
+
head_->SetNext(i, NULL);
|
330
|
+
}
|
331
|
+
}
|
332
|
+
|
333
|
+
template<typename Key, class Comparator>
|
334
|
+
void SkipList<Key,Comparator>::Insert(const Key& key) {
|
335
|
+
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
|
336
|
+
// here since Insert() is externally synchronized.
|
337
|
+
Node* prev[kMaxHeight];
|
338
|
+
Node* x = FindGreaterOrEqual(key, prev);
|
339
|
+
|
340
|
+
// Our data structure does not allow duplicate insertion
|
341
|
+
assert(x == NULL || !Equal(key, x->key));
|
342
|
+
|
343
|
+
int height = RandomHeight();
|
344
|
+
if (height > GetMaxHeight()) {
|
345
|
+
for (int i = GetMaxHeight(); i < height; i++) {
|
346
|
+
prev[i] = head_;
|
347
|
+
}
|
348
|
+
//fprintf(stderr, "Change height from %d to %d\n", max_height_, height);
|
349
|
+
|
350
|
+
// It is ok to mutate max_height_ without any synchronization
|
351
|
+
// with concurrent readers. A concurrent reader that observes
|
352
|
+
// the new value of max_height_ will see either the old value of
|
353
|
+
// new level pointers from head_ (NULL), or a new value set in
|
354
|
+
// the loop below. In the former case the reader will
|
355
|
+
// immediately drop to the next level since NULL sorts after all
|
356
|
+
// keys. In the latter case the reader will use the new node.
|
357
|
+
max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));
|
358
|
+
}
|
359
|
+
|
360
|
+
x = NewNode(key, height);
|
361
|
+
for (int i = 0; i < height; i++) {
|
362
|
+
// NoBarrier_SetNext() suffices since we will add a barrier when
|
363
|
+
// we publish a pointer to "x" in prev[i].
|
364
|
+
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
|
365
|
+
prev[i]->SetNext(i, x);
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
template<typename Key, class Comparator>
|
370
|
+
bool SkipList<Key,Comparator>::Contains(const Key& key) const {
|
371
|
+
Node* x = FindGreaterOrEqual(key, NULL);
|
372
|
+
if (x != NULL && Equal(key, x->key)) {
|
373
|
+
return true;
|
374
|
+
} else {
|
375
|
+
return false;
|
376
|
+
}
|
377
|
+
}
|
378
|
+
|
379
|
+
} // namespace leveldb
|
@@ -0,0 +1,378 @@
|
|
1
|
+
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
4
|
+
|
5
|
+
#include "db/skiplist.h"
|
6
|
+
#include <set>
|
7
|
+
#include "leveldb/env.h"
|
8
|
+
#include "util/arena.h"
|
9
|
+
#include "util/hash.h"
|
10
|
+
#include "util/random.h"
|
11
|
+
#include "util/testharness.h"
|
12
|
+
|
13
|
+
namespace leveldb {
|
14
|
+
|
15
|
+
typedef uint64_t Key;
|
16
|
+
|
17
|
+
struct Comparator {
|
18
|
+
int operator()(const Key& a, const Key& b) const {
|
19
|
+
if (a < b) {
|
20
|
+
return -1;
|
21
|
+
} else if (a > b) {
|
22
|
+
return +1;
|
23
|
+
} else {
|
24
|
+
return 0;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
};
|
28
|
+
|
29
|
+
class SkipTest { };
|
30
|
+
|
31
|
+
TEST(SkipTest, Empty) {
|
32
|
+
Arena arena;
|
33
|
+
Comparator cmp;
|
34
|
+
SkipList<Key, Comparator> list(cmp, &arena);
|
35
|
+
ASSERT_TRUE(!list.Contains(10));
|
36
|
+
|
37
|
+
SkipList<Key, Comparator>::Iterator iter(&list);
|
38
|
+
ASSERT_TRUE(!iter.Valid());
|
39
|
+
iter.SeekToFirst();
|
40
|
+
ASSERT_TRUE(!iter.Valid());
|
41
|
+
iter.Seek(100);
|
42
|
+
ASSERT_TRUE(!iter.Valid());
|
43
|
+
iter.SeekToLast();
|
44
|
+
ASSERT_TRUE(!iter.Valid());
|
45
|
+
}
|
46
|
+
|
47
|
+
TEST(SkipTest, InsertAndLookup) {
|
48
|
+
const int N = 2000;
|
49
|
+
const int R = 5000;
|
50
|
+
Random rnd(1000);
|
51
|
+
std::set<Key> keys;
|
52
|
+
Arena arena;
|
53
|
+
Comparator cmp;
|
54
|
+
SkipList<Key, Comparator> list(cmp, &arena);
|
55
|
+
for (int i = 0; i < N; i++) {
|
56
|
+
Key key = rnd.Next() % R;
|
57
|
+
if (keys.insert(key).second) {
|
58
|
+
list.Insert(key);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
for (int i = 0; i < R; i++) {
|
63
|
+
if (list.Contains(i)) {
|
64
|
+
ASSERT_EQ(keys.count(i), 1);
|
65
|
+
} else {
|
66
|
+
ASSERT_EQ(keys.count(i), 0);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
// Simple iterator tests
|
71
|
+
{
|
72
|
+
SkipList<Key, Comparator>::Iterator iter(&list);
|
73
|
+
ASSERT_TRUE(!iter.Valid());
|
74
|
+
|
75
|
+
iter.Seek(0);
|
76
|
+
ASSERT_TRUE(iter.Valid());
|
77
|
+
ASSERT_EQ(*(keys.begin()), iter.key());
|
78
|
+
|
79
|
+
iter.SeekToFirst();
|
80
|
+
ASSERT_TRUE(iter.Valid());
|
81
|
+
ASSERT_EQ(*(keys.begin()), iter.key());
|
82
|
+
|
83
|
+
iter.SeekToLast();
|
84
|
+
ASSERT_TRUE(iter.Valid());
|
85
|
+
ASSERT_EQ(*(keys.rbegin()), iter.key());
|
86
|
+
}
|
87
|
+
|
88
|
+
// Forward iteration test
|
89
|
+
for (int i = 0; i < R; i++) {
|
90
|
+
SkipList<Key, Comparator>::Iterator iter(&list);
|
91
|
+
iter.Seek(i);
|
92
|
+
|
93
|
+
// Compare against model iterator
|
94
|
+
std::set<Key>::iterator model_iter = keys.lower_bound(i);
|
95
|
+
for (int j = 0; j < 3; j++) {
|
96
|
+
if (model_iter == keys.end()) {
|
97
|
+
ASSERT_TRUE(!iter.Valid());
|
98
|
+
break;
|
99
|
+
} else {
|
100
|
+
ASSERT_TRUE(iter.Valid());
|
101
|
+
ASSERT_EQ(*model_iter, iter.key());
|
102
|
+
++model_iter;
|
103
|
+
iter.Next();
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
// Backward iteration test
|
109
|
+
{
|
110
|
+
SkipList<Key, Comparator>::Iterator iter(&list);
|
111
|
+
iter.SeekToLast();
|
112
|
+
|
113
|
+
// Compare against model iterator
|
114
|
+
for (std::set<Key>::reverse_iterator model_iter = keys.rbegin();
|
115
|
+
model_iter != keys.rend();
|
116
|
+
++model_iter) {
|
117
|
+
ASSERT_TRUE(iter.Valid());
|
118
|
+
ASSERT_EQ(*model_iter, iter.key());
|
119
|
+
iter.Prev();
|
120
|
+
}
|
121
|
+
ASSERT_TRUE(!iter.Valid());
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
// We want to make sure that with a single writer and multiple
|
126
|
+
// concurrent readers (with no synchronization other than when a
|
127
|
+
// reader's iterator is created), the reader always observes all the
|
128
|
+
// data that was present in the skip list when the iterator was
|
129
|
+
// constructor. Because insertions are happening concurrently, we may
|
130
|
+
// also observe new values that were inserted since the iterator was
|
131
|
+
// constructed, but we should never miss any values that were present
|
132
|
+
// at iterator construction time.
|
133
|
+
//
|
134
|
+
// We generate multi-part keys:
|
135
|
+
// <key,gen,hash>
|
136
|
+
// where:
|
137
|
+
// key is in range [0..K-1]
|
138
|
+
// gen is a generation number for key
|
139
|
+
// hash is hash(key,gen)
|
140
|
+
//
|
141
|
+
// The insertion code picks a random key, sets gen to be 1 + the last
|
142
|
+
// generation number inserted for that key, and sets hash to Hash(key,gen).
|
143
|
+
//
|
144
|
+
// At the beginning of a read, we snapshot the last inserted
|
145
|
+
// generation number for each key. We then iterate, including random
|
146
|
+
// calls to Next() and Seek(). For every key we encounter, we
|
147
|
+
// check that it is either expected given the initial snapshot or has
|
148
|
+
// been concurrently added since the iterator started.
|
149
|
+
class ConcurrentTest {
|
150
|
+
private:
|
151
|
+
static const uint32_t K = 4;
|
152
|
+
|
153
|
+
static uint64_t key(Key key) { return (key >> 40); }
|
154
|
+
static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; }
|
155
|
+
static uint64_t hash(Key key) { return key & 0xff; }
|
156
|
+
|
157
|
+
static uint64_t HashNumbers(uint64_t k, uint64_t g) {
|
158
|
+
uint64_t data[2] = { k, g };
|
159
|
+
return Hash(reinterpret_cast<char*>(data), sizeof(data), 0);
|
160
|
+
}
|
161
|
+
|
162
|
+
static Key MakeKey(uint64_t k, uint64_t g) {
|
163
|
+
assert(sizeof(Key) == sizeof(uint64_t));
|
164
|
+
assert(k <= K); // We sometimes pass K to seek to the end of the skiplist
|
165
|
+
assert(g <= 0xffffffffu);
|
166
|
+
return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff));
|
167
|
+
}
|
168
|
+
|
169
|
+
static bool IsValidKey(Key k) {
|
170
|
+
return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff);
|
171
|
+
}
|
172
|
+
|
173
|
+
static Key RandomTarget(Random* rnd) {
|
174
|
+
switch (rnd->Next() % 10) {
|
175
|
+
case 0:
|
176
|
+
// Seek to beginning
|
177
|
+
return MakeKey(0, 0);
|
178
|
+
case 1:
|
179
|
+
// Seek to end
|
180
|
+
return MakeKey(K, 0);
|
181
|
+
default:
|
182
|
+
// Seek to middle
|
183
|
+
return MakeKey(rnd->Next() % K, 0);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
// Per-key generation
|
188
|
+
struct State {
|
189
|
+
port::AtomicPointer generation[K];
|
190
|
+
void Set(int k, intptr_t v) {
|
191
|
+
generation[k].Release_Store(reinterpret_cast<void*>(v));
|
192
|
+
}
|
193
|
+
intptr_t Get(int k) {
|
194
|
+
return reinterpret_cast<intptr_t>(generation[k].Acquire_Load());
|
195
|
+
}
|
196
|
+
|
197
|
+
State() {
|
198
|
+
for (int k = 0; k < K; k++) {
|
199
|
+
Set(k, 0);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
};
|
203
|
+
|
204
|
+
// Current state of the test
|
205
|
+
State current_;
|
206
|
+
|
207
|
+
Arena arena_;
|
208
|
+
|
209
|
+
// SkipList is not protected by mu_. We just use a single writer
|
210
|
+
// thread to modify it.
|
211
|
+
SkipList<Key, Comparator> list_;
|
212
|
+
|
213
|
+
public:
|
214
|
+
ConcurrentTest() : list_(Comparator(), &arena_) { }
|
215
|
+
|
216
|
+
// REQUIRES: External synchronization
|
217
|
+
void WriteStep(Random* rnd) {
|
218
|
+
const uint32_t k = rnd->Next() % K;
|
219
|
+
const intptr_t g = current_.Get(k) + 1;
|
220
|
+
const Key key = MakeKey(k, g);
|
221
|
+
list_.Insert(key);
|
222
|
+
current_.Set(k, g);
|
223
|
+
}
|
224
|
+
|
225
|
+
void ReadStep(Random* rnd) {
|
226
|
+
// Remember the initial committed state of the skiplist.
|
227
|
+
State initial_state;
|
228
|
+
for (int k = 0; k < K; k++) {
|
229
|
+
initial_state.Set(k, current_.Get(k));
|
230
|
+
}
|
231
|
+
|
232
|
+
Key pos = RandomTarget(rnd);
|
233
|
+
SkipList<Key, Comparator>::Iterator iter(&list_);
|
234
|
+
iter.Seek(pos);
|
235
|
+
while (true) {
|
236
|
+
Key current;
|
237
|
+
if (!iter.Valid()) {
|
238
|
+
current = MakeKey(K, 0);
|
239
|
+
} else {
|
240
|
+
current = iter.key();
|
241
|
+
ASSERT_TRUE(IsValidKey(current)) << current;
|
242
|
+
}
|
243
|
+
ASSERT_LE(pos, current) << "should not go backwards";
|
244
|
+
|
245
|
+
// Verify that everything in [pos,current) was not present in
|
246
|
+
// initial_state.
|
247
|
+
while (pos < current) {
|
248
|
+
ASSERT_LT(key(pos), K) << pos;
|
249
|
+
|
250
|
+
// Note that generation 0 is never inserted, so it is ok if
|
251
|
+
// <*,0,*> is missing.
|
252
|
+
ASSERT_TRUE((gen(pos) == 0) ||
|
253
|
+
(gen(pos) > initial_state.Get(key(pos)))
|
254
|
+
) << "key: " << key(pos)
|
255
|
+
<< "; gen: " << gen(pos)
|
256
|
+
<< "; initgen: "
|
257
|
+
<< initial_state.Get(key(pos));
|
258
|
+
|
259
|
+
// Advance to next key in the valid key space
|
260
|
+
if (key(pos) < key(current)) {
|
261
|
+
pos = MakeKey(key(pos) + 1, 0);
|
262
|
+
} else {
|
263
|
+
pos = MakeKey(key(pos), gen(pos) + 1);
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
if (!iter.Valid()) {
|
268
|
+
break;
|
269
|
+
}
|
270
|
+
|
271
|
+
if (rnd->Next() % 2) {
|
272
|
+
iter.Next();
|
273
|
+
pos = MakeKey(key(pos), gen(pos) + 1);
|
274
|
+
} else {
|
275
|
+
Key new_target = RandomTarget(rnd);
|
276
|
+
if (new_target > pos) {
|
277
|
+
pos = new_target;
|
278
|
+
iter.Seek(new_target);
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
};
|
284
|
+
const uint32_t ConcurrentTest::K;
|
285
|
+
|
286
|
+
// Simple test that does single-threaded testing of the ConcurrentTest
|
287
|
+
// scaffolding.
|
288
|
+
TEST(SkipTest, ConcurrentWithoutThreads) {
|
289
|
+
ConcurrentTest test;
|
290
|
+
Random rnd(test::RandomSeed());
|
291
|
+
for (int i = 0; i < 10000; i++) {
|
292
|
+
test.ReadStep(&rnd);
|
293
|
+
test.WriteStep(&rnd);
|
294
|
+
}
|
295
|
+
}
|
296
|
+
|
297
|
+
class TestState {
|
298
|
+
public:
|
299
|
+
ConcurrentTest t_;
|
300
|
+
int seed_;
|
301
|
+
port::AtomicPointer quit_flag_;
|
302
|
+
|
303
|
+
enum ReaderState {
|
304
|
+
STARTING,
|
305
|
+
RUNNING,
|
306
|
+
DONE
|
307
|
+
};
|
308
|
+
|
309
|
+
explicit TestState(int s)
|
310
|
+
: seed_(s),
|
311
|
+
quit_flag_(NULL),
|
312
|
+
state_(STARTING),
|
313
|
+
state_cv_(&mu_) {}
|
314
|
+
|
315
|
+
void Wait(ReaderState s) {
|
316
|
+
mu_.Lock();
|
317
|
+
while (state_ != s) {
|
318
|
+
state_cv_.Wait();
|
319
|
+
}
|
320
|
+
mu_.Unlock();
|
321
|
+
}
|
322
|
+
|
323
|
+
void Change(ReaderState s) {
|
324
|
+
mu_.Lock();
|
325
|
+
state_ = s;
|
326
|
+
state_cv_.Signal();
|
327
|
+
mu_.Unlock();
|
328
|
+
}
|
329
|
+
|
330
|
+
private:
|
331
|
+
port::Mutex mu_;
|
332
|
+
ReaderState state_;
|
333
|
+
port::CondVar state_cv_;
|
334
|
+
};
|
335
|
+
|
336
|
+
static void ConcurrentReader(void* arg) {
|
337
|
+
TestState* state = reinterpret_cast<TestState*>(arg);
|
338
|
+
Random rnd(state->seed_);
|
339
|
+
int64_t reads = 0;
|
340
|
+
state->Change(TestState::RUNNING);
|
341
|
+
while (!state->quit_flag_.Acquire_Load()) {
|
342
|
+
state->t_.ReadStep(&rnd);
|
343
|
+
++reads;
|
344
|
+
}
|
345
|
+
state->Change(TestState::DONE);
|
346
|
+
}
|
347
|
+
|
348
|
+
static void RunConcurrent(int run) {
|
349
|
+
const int seed = test::RandomSeed() + (run * 100);
|
350
|
+
Random rnd(seed);
|
351
|
+
const int N = 1000;
|
352
|
+
const int kSize = 1000;
|
353
|
+
for (int i = 0; i < N; i++) {
|
354
|
+
if ((i % 100) == 0) {
|
355
|
+
fprintf(stderr, "Run %d of %d\n", i, N);
|
356
|
+
}
|
357
|
+
TestState state(seed + 1);
|
358
|
+
Env::Default()->Schedule(ConcurrentReader, &state);
|
359
|
+
state.Wait(TestState::RUNNING);
|
360
|
+
for (int i = 0; i < kSize; i++) {
|
361
|
+
state.t_.WriteStep(&rnd);
|
362
|
+
}
|
363
|
+
state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do
|
364
|
+
state.Wait(TestState::DONE);
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
TEST(SkipTest, Concurrent1) { RunConcurrent(1); }
|
369
|
+
TEST(SkipTest, Concurrent2) { RunConcurrent(2); }
|
370
|
+
TEST(SkipTest, Concurrent3) { RunConcurrent(3); }
|
371
|
+
TEST(SkipTest, Concurrent4) { RunConcurrent(4); }
|
372
|
+
TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
373
|
+
|
374
|
+
} // namespace leveldb
|
375
|
+
|
376
|
+
int main(int argc, char** argv) {
|
377
|
+
return leveldb::test::RunAllTests();
|
378
|
+
}
|