leveldb-ruby 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +17 -0
- data/ext/leveldb/extconf.rb +10 -0
- data/ext/leveldb/leveldb.cc +181 -0
- data/leveldb/Makefile +172 -0
- data/leveldb/db/builder.cc +90 -0
- data/leveldb/db/builder.h +36 -0
- data/leveldb/db/corruption_test.cc +354 -0
- data/leveldb/db/db_bench.cc +677 -0
- data/leveldb/db/db_impl.cc +1236 -0
- data/leveldb/db/db_impl.h +180 -0
- data/leveldb/db/db_iter.cc +298 -0
- data/leveldb/db/db_iter.h +26 -0
- data/leveldb/db/db_test.cc +1192 -0
- data/leveldb/db/dbformat.cc +87 -0
- data/leveldb/db/dbformat.h +165 -0
- data/leveldb/db/dbformat_test.cc +112 -0
- data/leveldb/db/filename.cc +135 -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 +254 -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 +108 -0
- data/leveldb/db/memtable.h +85 -0
- data/leveldb/db/repair.cc +384 -0
- data/leveldb/db/skiplist.h +378 -0
- data/leveldb/db/skiplist_test.cc +378 -0
- data/leveldb/db/snapshot.h +66 -0
- data/leveldb/db/table_cache.cc +95 -0
- data/leveldb/db/table_cache.h +50 -0
- data/leveldb/db/version_edit.cc +268 -0
- data/leveldb/db/version_edit.h +106 -0
- data/leveldb/db/version_edit_test.cc +46 -0
- data/leveldb/db/version_set.cc +1060 -0
- data/leveldb/db/version_set.h +306 -0
- data/leveldb/db/write_batch.cc +138 -0
- data/leveldb/db/write_batch_internal.h +45 -0
- data/leveldb/db/write_batch_test.cc +89 -0
- data/leveldb/include/leveldb/cache.h +99 -0
- data/leveldb/include/leveldb/comparator.h +63 -0
- data/leveldb/include/leveldb/db.h +148 -0
- data/leveldb/include/leveldb/env.h +302 -0
- data/leveldb/include/leveldb/iterator.h +100 -0
- data/leveldb/include/leveldb/options.h +198 -0
- data/leveldb/include/leveldb/slice.h +109 -0
- data/leveldb/include/leveldb/status.h +100 -0
- data/leveldb/include/leveldb/table.h +70 -0
- data/leveldb/include/leveldb/table_builder.h +91 -0
- data/leveldb/include/leveldb/write_batch.h +64 -0
- data/leveldb/port/port.h +23 -0
- data/leveldb/port/port_android.cc +64 -0
- data/leveldb/port/port_android.h +150 -0
- data/leveldb/port/port_chromium.cc +80 -0
- data/leveldb/port/port_chromium.h +97 -0
- data/leveldb/port/port_example.h +115 -0
- data/leveldb/port/port_osx.cc +50 -0
- data/leveldb/port/port_osx.h +125 -0
- data/leveldb/port/port_posix.cc +50 -0
- data/leveldb/port/port_posix.h +94 -0
- data/leveldb/port/sha1_portable.cc +298 -0
- data/leveldb/port/sha1_portable.h +25 -0
- data/leveldb/port/sha1_test.cc +39 -0
- data/leveldb/port/win/stdint.h +24 -0
- data/leveldb/table/block.cc +263 -0
- data/leveldb/table/block.h +43 -0
- data/leveldb/table/block_builder.cc +109 -0
- data/leveldb/table/block_builder.h +57 -0
- data/leveldb/table/format.cc +131 -0
- data/leveldb/table/format.h +103 -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 +175 -0
- data/leveldb/table/table_builder.cc +227 -0
- data/leveldb/table/table_test.cc +845 -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/cache.cc +255 -0
- data/leveldb/util/cache_test.cc +169 -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 +72 -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 +77 -0
- data/leveldb/util/env_chromium.cc +612 -0
- data/leveldb/util/env_posix.cc +606 -0
- data/leveldb/util/env_test.cc +102 -0
- data/leveldb/util/hash.cc +45 -0
- data/leveldb/util/hash.h +19 -0
- data/leveldb/util/histogram.cc +128 -0
- data/leveldb/util/histogram.h +41 -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 +28 -0
- data/leveldb/util/random.h +59 -0
- data/leveldb/util/status.cc +75 -0
- data/leveldb/util/testharness.cc +65 -0
- data/leveldb/util/testharness.h +129 -0
- data/leveldb/util/testutil.cc +51 -0
- data/leveldb/util/testutil.h +53 -0
- data/lib/leveldb.rb +36 -0
- metadata +183 -0
@@ -0,0 +1,182 @@
|
|
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 "table/two_level_iterator.h"
|
6
|
+
|
7
|
+
#include "leveldb/table.h"
|
8
|
+
#include "table/block.h"
|
9
|
+
#include "table/format.h"
|
10
|
+
#include "table/iterator_wrapper.h"
|
11
|
+
|
12
|
+
namespace leveldb {
|
13
|
+
|
14
|
+
namespace {
|
15
|
+
|
16
|
+
typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&);
|
17
|
+
|
18
|
+
class TwoLevelIterator: public Iterator {
|
19
|
+
public:
|
20
|
+
TwoLevelIterator(
|
21
|
+
Iterator* index_iter,
|
22
|
+
BlockFunction block_function,
|
23
|
+
void* arg,
|
24
|
+
const ReadOptions& options);
|
25
|
+
|
26
|
+
virtual ~TwoLevelIterator();
|
27
|
+
|
28
|
+
virtual void Seek(const Slice& target);
|
29
|
+
virtual void SeekToFirst();
|
30
|
+
virtual void SeekToLast();
|
31
|
+
virtual void Next();
|
32
|
+
virtual void Prev();
|
33
|
+
|
34
|
+
virtual bool Valid() const {
|
35
|
+
return data_iter_.Valid();
|
36
|
+
}
|
37
|
+
virtual Slice key() const {
|
38
|
+
assert(Valid());
|
39
|
+
return data_iter_.key();
|
40
|
+
}
|
41
|
+
virtual Slice value() const {
|
42
|
+
assert(Valid());
|
43
|
+
return data_iter_.value();
|
44
|
+
}
|
45
|
+
virtual Status status() const {
|
46
|
+
// It'd be nice if status() returned a const Status& instead of a Status
|
47
|
+
if (!index_iter_.status().ok()) {
|
48
|
+
return index_iter_.status();
|
49
|
+
} else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) {
|
50
|
+
return data_iter_.status();
|
51
|
+
} else {
|
52
|
+
return status_;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
private:
|
57
|
+
void SaveError(const Status& s) {
|
58
|
+
if (status_.ok() && !s.ok()) status_ = s;
|
59
|
+
}
|
60
|
+
void SkipEmptyDataBlocksForward();
|
61
|
+
void SkipEmptyDataBlocksBackward();
|
62
|
+
void SetDataIterator(Iterator* data_iter);
|
63
|
+
void InitDataBlock();
|
64
|
+
|
65
|
+
BlockFunction block_function_;
|
66
|
+
void* arg_;
|
67
|
+
const ReadOptions options_;
|
68
|
+
Status status_;
|
69
|
+
IteratorWrapper index_iter_;
|
70
|
+
IteratorWrapper data_iter_; // May be NULL
|
71
|
+
// If data_iter_ is non-NULL, then "data_block_handle_" holds the
|
72
|
+
// "index_value" passed to block_function_ to create the data_iter_.
|
73
|
+
std::string data_block_handle_;
|
74
|
+
};
|
75
|
+
|
76
|
+
TwoLevelIterator::TwoLevelIterator(
|
77
|
+
Iterator* index_iter,
|
78
|
+
BlockFunction block_function,
|
79
|
+
void* arg,
|
80
|
+
const ReadOptions& options)
|
81
|
+
: block_function_(block_function),
|
82
|
+
arg_(arg),
|
83
|
+
options_(options),
|
84
|
+
index_iter_(index_iter),
|
85
|
+
data_iter_(NULL) {
|
86
|
+
}
|
87
|
+
|
88
|
+
TwoLevelIterator::~TwoLevelIterator() {
|
89
|
+
}
|
90
|
+
|
91
|
+
void TwoLevelIterator::Seek(const Slice& target) {
|
92
|
+
index_iter_.Seek(target);
|
93
|
+
InitDataBlock();
|
94
|
+
if (data_iter_.iter() != NULL) data_iter_.Seek(target);
|
95
|
+
SkipEmptyDataBlocksForward();
|
96
|
+
}
|
97
|
+
|
98
|
+
void TwoLevelIterator::SeekToFirst() {
|
99
|
+
index_iter_.SeekToFirst();
|
100
|
+
InitDataBlock();
|
101
|
+
if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();
|
102
|
+
SkipEmptyDataBlocksForward();
|
103
|
+
}
|
104
|
+
|
105
|
+
void TwoLevelIterator::SeekToLast() {
|
106
|
+
index_iter_.SeekToLast();
|
107
|
+
InitDataBlock();
|
108
|
+
if (data_iter_.iter() != NULL) data_iter_.SeekToLast();
|
109
|
+
SkipEmptyDataBlocksBackward();
|
110
|
+
}
|
111
|
+
|
112
|
+
void TwoLevelIterator::Next() {
|
113
|
+
assert(Valid());
|
114
|
+
data_iter_.Next();
|
115
|
+
SkipEmptyDataBlocksForward();
|
116
|
+
}
|
117
|
+
|
118
|
+
void TwoLevelIterator::Prev() {
|
119
|
+
assert(Valid());
|
120
|
+
data_iter_.Prev();
|
121
|
+
SkipEmptyDataBlocksBackward();
|
122
|
+
}
|
123
|
+
|
124
|
+
|
125
|
+
void TwoLevelIterator::SkipEmptyDataBlocksForward() {
|
126
|
+
while (data_iter_.iter() == NULL || !data_iter_.Valid()) {
|
127
|
+
// Move to next block
|
128
|
+
if (!index_iter_.Valid()) {
|
129
|
+
SetDataIterator(NULL);
|
130
|
+
return;
|
131
|
+
}
|
132
|
+
index_iter_.Next();
|
133
|
+
InitDataBlock();
|
134
|
+
if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
void TwoLevelIterator::SkipEmptyDataBlocksBackward() {
|
139
|
+
while (data_iter_.iter() == NULL || !data_iter_.Valid()) {
|
140
|
+
// Move to next block
|
141
|
+
if (!index_iter_.Valid()) {
|
142
|
+
SetDataIterator(NULL);
|
143
|
+
return;
|
144
|
+
}
|
145
|
+
index_iter_.Prev();
|
146
|
+
InitDataBlock();
|
147
|
+
if (data_iter_.iter() != NULL) data_iter_.SeekToLast();
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
void TwoLevelIterator::SetDataIterator(Iterator* data_iter) {
|
152
|
+
if (data_iter_.iter() != NULL) SaveError(data_iter_.status());
|
153
|
+
data_iter_.Set(data_iter);
|
154
|
+
}
|
155
|
+
|
156
|
+
void TwoLevelIterator::InitDataBlock() {
|
157
|
+
if (!index_iter_.Valid()) {
|
158
|
+
SetDataIterator(NULL);
|
159
|
+
} else {
|
160
|
+
Slice handle = index_iter_.value();
|
161
|
+
if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) {
|
162
|
+
// data_iter_ is already constructed with this iterator, so
|
163
|
+
// no need to change anything
|
164
|
+
} else {
|
165
|
+
Iterator* iter = (*block_function_)(arg_, options_, handle);
|
166
|
+
data_block_handle_.assign(handle.data(), handle.size());
|
167
|
+
SetDataIterator(iter);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
}
|
173
|
+
|
174
|
+
Iterator* NewTwoLevelIterator(
|
175
|
+
Iterator* index_iter,
|
176
|
+
BlockFunction block_function,
|
177
|
+
void* arg,
|
178
|
+
const ReadOptions& options) {
|
179
|
+
return new TwoLevelIterator(index_iter, block_function, arg, options);
|
180
|
+
}
|
181
|
+
|
182
|
+
}
|
@@ -0,0 +1,34 @@
|
|
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
|
+
#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_
|
6
|
+
#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_
|
7
|
+
|
8
|
+
#include "leveldb/iterator.h"
|
9
|
+
|
10
|
+
namespace leveldb {
|
11
|
+
|
12
|
+
struct ReadOptions;
|
13
|
+
|
14
|
+
// Return a new two level iterator. A two-level iterator contains an
|
15
|
+
// index iterator whose values point to a sequence of blocks where
|
16
|
+
// each block is itself a sequence of key,value pairs. The returned
|
17
|
+
// two-level iterator yields the concatenation of all key/value pairs
|
18
|
+
// in the sequence of blocks. Takes ownership of "index_iter" and
|
19
|
+
// will delete it when no longer needed.
|
20
|
+
//
|
21
|
+
// Uses a supplied function to convert an index_iter value into
|
22
|
+
// an iterator over the contents of the corresponding block.
|
23
|
+
extern Iterator* NewTwoLevelIterator(
|
24
|
+
Iterator* index_iter,
|
25
|
+
Iterator* (*block_function)(
|
26
|
+
void* arg,
|
27
|
+
const ReadOptions& options,
|
28
|
+
const Slice& index_value),
|
29
|
+
void* arg,
|
30
|
+
const ReadOptions& options);
|
31
|
+
|
32
|
+
}
|
33
|
+
|
34
|
+
#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_
|
@@ -0,0 +1,68 @@
|
|
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 "util/arena.h"
|
6
|
+
#include <assert.h>
|
7
|
+
|
8
|
+
namespace leveldb {
|
9
|
+
|
10
|
+
static const int kBlockSize = 4096;
|
11
|
+
|
12
|
+
Arena::Arena() {
|
13
|
+
blocks_memory_ = 0;
|
14
|
+
alloc_ptr_ = NULL; // First allocation will allocate a block
|
15
|
+
alloc_bytes_remaining_ = 0;
|
16
|
+
}
|
17
|
+
|
18
|
+
Arena::~Arena() {
|
19
|
+
for (size_t i = 0; i < blocks_.size(); i++) {
|
20
|
+
delete[] blocks_[i];
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
char* Arena::AllocateFallback(size_t bytes) {
|
25
|
+
if (bytes > kBlockSize / 4) {
|
26
|
+
// Object is more than a quarter of our block size. Allocate it separately
|
27
|
+
// to avoid wasting too much space in leftover bytes.
|
28
|
+
char* result = AllocateNewBlock(bytes);
|
29
|
+
return result;
|
30
|
+
}
|
31
|
+
|
32
|
+
// We waste the remaining space in the current block.
|
33
|
+
alloc_ptr_ = AllocateNewBlock(kBlockSize);
|
34
|
+
alloc_bytes_remaining_ = kBlockSize;
|
35
|
+
|
36
|
+
char* result = alloc_ptr_;
|
37
|
+
alloc_ptr_ += bytes;
|
38
|
+
alloc_bytes_remaining_ -= bytes;
|
39
|
+
return result;
|
40
|
+
}
|
41
|
+
|
42
|
+
char* Arena::AllocateAligned(size_t bytes) {
|
43
|
+
const int align = sizeof(void*); // We'll align to pointer size
|
44
|
+
assert((align & (align-1)) == 0); // Pointer size should be a power of 2
|
45
|
+
size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);
|
46
|
+
size_t slop = (current_mod == 0 ? 0 : align - current_mod);
|
47
|
+
size_t needed = bytes + slop;
|
48
|
+
char* result;
|
49
|
+
if (needed <= alloc_bytes_remaining_) {
|
50
|
+
result = alloc_ptr_ + slop;
|
51
|
+
alloc_ptr_ += needed;
|
52
|
+
alloc_bytes_remaining_ -= needed;
|
53
|
+
} else {
|
54
|
+
// AllocateFallback always returned aligned memory
|
55
|
+
result = AllocateFallback(bytes);
|
56
|
+
}
|
57
|
+
assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);
|
58
|
+
return result;
|
59
|
+
}
|
60
|
+
|
61
|
+
char* Arena::AllocateNewBlock(size_t block_bytes) {
|
62
|
+
char* result = new char[block_bytes];
|
63
|
+
blocks_memory_ += block_bytes;
|
64
|
+
blocks_.push_back(result);
|
65
|
+
return result;
|
66
|
+
}
|
67
|
+
|
68
|
+
}
|
@@ -0,0 +1,68 @@
|
|
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
|
+
#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_
|
6
|
+
#define STORAGE_LEVELDB_UTIL_ARENA_H_
|
7
|
+
|
8
|
+
#include <cstddef>
|
9
|
+
#include <vector>
|
10
|
+
#include <assert.h>
|
11
|
+
#include <stdint.h>
|
12
|
+
|
13
|
+
namespace leveldb {
|
14
|
+
|
15
|
+
class Arena {
|
16
|
+
public:
|
17
|
+
Arena();
|
18
|
+
~Arena();
|
19
|
+
|
20
|
+
// Return a pointer to a newly allocated memory block of "bytes" bytes.
|
21
|
+
char* Allocate(size_t bytes);
|
22
|
+
|
23
|
+
// Allocate memory with the normal alignment guarantees provided by malloc
|
24
|
+
char* AllocateAligned(size_t bytes);
|
25
|
+
|
26
|
+
// Returns an estimate of the total memory usage of data allocated
|
27
|
+
// by the arena (including space allocated but not yet used for user
|
28
|
+
// allocations).
|
29
|
+
size_t MemoryUsage() const {
|
30
|
+
return blocks_memory_ + blocks_.capacity() * sizeof(char*);
|
31
|
+
}
|
32
|
+
|
33
|
+
private:
|
34
|
+
char* AllocateFallback(size_t bytes);
|
35
|
+
char* AllocateNewBlock(size_t block_bytes);
|
36
|
+
|
37
|
+
// Allocation state
|
38
|
+
char* alloc_ptr_;
|
39
|
+
size_t alloc_bytes_remaining_;
|
40
|
+
|
41
|
+
// Array of new[] allocated memory blocks
|
42
|
+
std::vector<char*> blocks_;
|
43
|
+
|
44
|
+
// Bytes of memory in blocks allocated so far
|
45
|
+
size_t blocks_memory_;
|
46
|
+
|
47
|
+
// No copying allowed
|
48
|
+
Arena(const Arena&);
|
49
|
+
void operator=(const Arena&);
|
50
|
+
};
|
51
|
+
|
52
|
+
inline char* Arena::Allocate(size_t bytes) {
|
53
|
+
// The semantics of what to return are a bit messy if we allow
|
54
|
+
// 0-byte allocations, so we disallow them here (we don't need
|
55
|
+
// them for our internal use).
|
56
|
+
assert(bytes > 0);
|
57
|
+
if (bytes <= alloc_bytes_remaining_) {
|
58
|
+
char* result = alloc_ptr_;
|
59
|
+
alloc_ptr_ += bytes;
|
60
|
+
alloc_bytes_remaining_ -= bytes;
|
61
|
+
return result;
|
62
|
+
}
|
63
|
+
return AllocateFallback(bytes);
|
64
|
+
}
|
65
|
+
|
66
|
+
}
|
67
|
+
|
68
|
+
#endif // STORAGE_LEVELDB_UTIL_ARENA_H_
|
@@ -0,0 +1,68 @@
|
|
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 "util/arena.h"
|
6
|
+
|
7
|
+
#include "util/random.h"
|
8
|
+
#include "util/testharness.h"
|
9
|
+
|
10
|
+
namespace leveldb {
|
11
|
+
|
12
|
+
class ArenaTest { };
|
13
|
+
|
14
|
+
TEST(ArenaTest, Empty) {
|
15
|
+
Arena arena;
|
16
|
+
}
|
17
|
+
|
18
|
+
TEST(ArenaTest, Simple) {
|
19
|
+
std::vector<std::pair<size_t, char*> > allocated;
|
20
|
+
Arena arena;
|
21
|
+
const int N = 100000;
|
22
|
+
size_t bytes = 0;
|
23
|
+
Random rnd(301);
|
24
|
+
for (int i = 0; i < N; i++) {
|
25
|
+
size_t s;
|
26
|
+
if (i % (N / 10) == 0) {
|
27
|
+
s = i;
|
28
|
+
} else {
|
29
|
+
s = rnd.OneIn(4000) ? rnd.Uniform(6000) :
|
30
|
+
(rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20));
|
31
|
+
}
|
32
|
+
if (s == 0) {
|
33
|
+
// Our arena disallows size 0 allocations.
|
34
|
+
s = 1;
|
35
|
+
}
|
36
|
+
char* r;
|
37
|
+
if (rnd.OneIn(10)) {
|
38
|
+
r = arena.AllocateAligned(s);
|
39
|
+
} else {
|
40
|
+
r = arena.Allocate(s);
|
41
|
+
}
|
42
|
+
|
43
|
+
for (int b = 0; b < s; b++) {
|
44
|
+
// Fill the "i"th allocation with a known bit pattern
|
45
|
+
r[b] = i % 256;
|
46
|
+
}
|
47
|
+
bytes += s;
|
48
|
+
allocated.push_back(std::make_pair(s, r));
|
49
|
+
ASSERT_GE(arena.MemoryUsage(), bytes);
|
50
|
+
if (i > N/10) {
|
51
|
+
ASSERT_LE(arena.MemoryUsage(), bytes * 1.10);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
for (int i = 0; i < allocated.size(); i++) {
|
55
|
+
size_t num_bytes = allocated[i].first;
|
56
|
+
const char* p = allocated[i].second;
|
57
|
+
for (int b = 0; b < num_bytes; b++) {
|
58
|
+
// Check the "i"th allocation for the known bit pattern
|
59
|
+
ASSERT_EQ(int(p[b]) & 0xff, i % 256);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
}
|
65
|
+
|
66
|
+
int main(int argc, char** argv) {
|
67
|
+
return leveldb::test::RunAllTests();
|
68
|
+
}
|
@@ -0,0 +1,255 @@
|
|
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
|
+
#if defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_ANDROID)
|
6
|
+
#include <unordered_set>
|
7
|
+
#elif defined(LEVELDB_PLATFORM_OSX)
|
8
|
+
#include <ext/hash_set>
|
9
|
+
#elif defined(LEVELDB_PLATFORM_CHROMIUM)
|
10
|
+
#include "base/hash_tables.h"
|
11
|
+
#else
|
12
|
+
#include <hash_set> // TODO(sanjay): Switch to unordered_set when possible.
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#include <assert.h>
|
16
|
+
|
17
|
+
#include "leveldb/cache.h"
|
18
|
+
#include "port/port.h"
|
19
|
+
#include "util/hash.h"
|
20
|
+
#include "util/mutexlock.h"
|
21
|
+
|
22
|
+
namespace leveldb {
|
23
|
+
|
24
|
+
Cache::~Cache() {
|
25
|
+
}
|
26
|
+
|
27
|
+
namespace {
|
28
|
+
|
29
|
+
// LRU cache implementation
|
30
|
+
|
31
|
+
// An entry is a variable length heap-allocated structure. Entries
|
32
|
+
// are kept in a circular doubly linked list ordered by access time.
|
33
|
+
struct LRUHandle {
|
34
|
+
void* value;
|
35
|
+
void (*deleter)(const Slice&, void* value);
|
36
|
+
LRUHandle* next;
|
37
|
+
LRUHandle* prev;
|
38
|
+
size_t charge; // TODO(opt): Only allow uint32_t?
|
39
|
+
size_t key_length;
|
40
|
+
size_t refs; // TODO(opt): Pack with "key_length"?
|
41
|
+
char key_data[1]; // Beginning of key
|
42
|
+
|
43
|
+
Slice key() const {
|
44
|
+
// For cheaper lookups, we allow a temporary Handle object
|
45
|
+
// to store a pointer to a key in "value".
|
46
|
+
if (next == this) {
|
47
|
+
return *(reinterpret_cast<Slice*>(value));
|
48
|
+
} else {
|
49
|
+
return Slice(key_data, key_length);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
};
|
53
|
+
|
54
|
+
// Pick a platform specific hash_set instantiation
|
55
|
+
#if defined(LEVELDB_PLATFORM_CHROMIUM) && defined(OS_WIN)
|
56
|
+
// Microsoft's hash_set deviates from the standard. See
|
57
|
+
// http://msdn.microsoft.com/en-us/library/1t4xas78(v=vs.80).aspx
|
58
|
+
// for details. Basically the 2 param () operator is a less than and
|
59
|
+
// the 1 param () operator is a hash function.
|
60
|
+
struct HandleHashCompare : public stdext::hash_compare<LRUHandle*> {
|
61
|
+
size_t operator() (LRUHandle* h) const {
|
62
|
+
Slice k = h->key();
|
63
|
+
return Hash(k.data(), k.size(), 0);
|
64
|
+
}
|
65
|
+
bool operator() (LRUHandle* a, LRUHandle* b) const {
|
66
|
+
return a->key().compare(b->key()) < 0;
|
67
|
+
}
|
68
|
+
};
|
69
|
+
typedef base::hash_set<LRUHandle*, HandleHashCompare> HandleTable;
|
70
|
+
#else
|
71
|
+
struct HandleHash {
|
72
|
+
inline size_t operator()(LRUHandle* h) const {
|
73
|
+
Slice k = h->key();
|
74
|
+
return Hash(k.data(), k.size(), 0);
|
75
|
+
}
|
76
|
+
};
|
77
|
+
|
78
|
+
struct HandleEq {
|
79
|
+
inline bool operator()(LRUHandle* a, LRUHandle* b) const {
|
80
|
+
return a->key() == b->key();
|
81
|
+
}
|
82
|
+
};
|
83
|
+
# if defined(LEVELDB_PLATFORM_CHROMIUM)
|
84
|
+
typedef base::hash_set<LRUHandle*, HandleHash, HandleEq> HandleTable;
|
85
|
+
# elif defined(LEVELDB_PLATFORM_POSIX) || defined(LEVELDB_PLATFORM_ANDROID)
|
86
|
+
typedef std::unordered_set<LRUHandle*, HandleHash, HandleEq> HandleTable;
|
87
|
+
# else
|
88
|
+
typedef __gnu_cxx::hash_set<LRUHandle*, HandleHash, HandleEq> HandleTable;
|
89
|
+
# endif
|
90
|
+
#endif
|
91
|
+
|
92
|
+
class LRUCache : public Cache {
|
93
|
+
public:
|
94
|
+
explicit LRUCache(size_t capacity);
|
95
|
+
virtual ~LRUCache();
|
96
|
+
|
97
|
+
virtual Handle* Insert(const Slice& key, void* value, size_t charge,
|
98
|
+
void (*deleter)(const Slice& key, void* value));
|
99
|
+
virtual Handle* Lookup(const Slice& key);
|
100
|
+
virtual void Release(Handle* handle);
|
101
|
+
virtual void* Value(Handle* handle);
|
102
|
+
virtual void Erase(const Slice& key);
|
103
|
+
virtual uint64_t NewId();
|
104
|
+
|
105
|
+
private:
|
106
|
+
void LRU_Remove(LRUHandle* e);
|
107
|
+
void LRU_Append(LRUHandle* e);
|
108
|
+
void Unref(LRUHandle* e);
|
109
|
+
|
110
|
+
// Constructor parameters
|
111
|
+
const size_t capacity_;
|
112
|
+
|
113
|
+
// mutex_ protects the following state.
|
114
|
+
port::Mutex mutex_;
|
115
|
+
size_t usage_;
|
116
|
+
uint64_t last_id_;
|
117
|
+
|
118
|
+
// Dummy head of LRU list.
|
119
|
+
// lru.prev is newest entry, lru.next is oldest entry.
|
120
|
+
LRUHandle lru_;
|
121
|
+
|
122
|
+
HandleTable table_;
|
123
|
+
};
|
124
|
+
|
125
|
+
LRUCache::LRUCache(size_t capacity)
|
126
|
+
: capacity_(capacity),
|
127
|
+
usage_(0),
|
128
|
+
last_id_(0) {
|
129
|
+
// Make empty circular linked list
|
130
|
+
lru_.next = &lru_;
|
131
|
+
lru_.prev = &lru_;
|
132
|
+
}
|
133
|
+
|
134
|
+
LRUCache::~LRUCache() {
|
135
|
+
table_.clear();
|
136
|
+
for (LRUHandle* e = lru_.next; e != &lru_; ) {
|
137
|
+
LRUHandle* next = e->next;
|
138
|
+
assert(e->refs == 1); // Error if caller has an unreleased handle
|
139
|
+
Unref(e);
|
140
|
+
e = next;
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
void LRUCache::Unref(LRUHandle* e) {
|
145
|
+
assert(e->refs > 0);
|
146
|
+
e->refs--;
|
147
|
+
if (e->refs <= 0) {
|
148
|
+
usage_ -= e->charge;
|
149
|
+
(*e->deleter)(e->key(), e->value);
|
150
|
+
free(e);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
void LRUCache::LRU_Remove(LRUHandle* e) {
|
155
|
+
e->next->prev = e->prev;
|
156
|
+
e->prev->next = e->next;
|
157
|
+
}
|
158
|
+
|
159
|
+
void LRUCache::LRU_Append(LRUHandle* e) {
|
160
|
+
// Make "e" newest entry by inserting just before lru_
|
161
|
+
e->next = &lru_;
|
162
|
+
e->prev = lru_.prev;
|
163
|
+
e->prev->next = e;
|
164
|
+
e->next->prev = e;
|
165
|
+
}
|
166
|
+
|
167
|
+
Cache::Handle* LRUCache::Lookup(const Slice& key) {
|
168
|
+
MutexLock l(&mutex_);
|
169
|
+
|
170
|
+
LRUHandle dummy;
|
171
|
+
dummy.next = &dummy;
|
172
|
+
dummy.value = const_cast<Slice*>(&key);
|
173
|
+
HandleTable::iterator iter = table_.find(&dummy);
|
174
|
+
if (iter == table_.end()) {
|
175
|
+
return NULL;
|
176
|
+
} else {
|
177
|
+
LRUHandle* e = const_cast<LRUHandle*>(*iter);
|
178
|
+
e->refs++;
|
179
|
+
LRU_Remove(e);
|
180
|
+
LRU_Append(e);
|
181
|
+
return reinterpret_cast<Handle*>(e);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
void* LRUCache::Value(Handle* handle) {
|
186
|
+
return reinterpret_cast<LRUHandle*>(handle)->value;
|
187
|
+
}
|
188
|
+
|
189
|
+
void LRUCache::Release(Handle* handle) {
|
190
|
+
MutexLock l(&mutex_);
|
191
|
+
Unref(reinterpret_cast<LRUHandle*>(handle));
|
192
|
+
}
|
193
|
+
|
194
|
+
Cache::Handle* LRUCache::Insert(const Slice& key, void* value, size_t charge,
|
195
|
+
void (*deleter)(const Slice& key, void* value)) {
|
196
|
+
MutexLock l(&mutex_);
|
197
|
+
|
198
|
+
LRUHandle* e = reinterpret_cast<LRUHandle*>(
|
199
|
+
malloc(sizeof(LRUHandle)-1 + key.size()));
|
200
|
+
e->value = value;
|
201
|
+
e->deleter = deleter;
|
202
|
+
e->charge = charge;
|
203
|
+
e->key_length = key.size();
|
204
|
+
e->refs = 2; // One from LRUCache, one for the returned handle
|
205
|
+
memcpy(e->key_data, key.data(), key.size());
|
206
|
+
LRU_Append(e);
|
207
|
+
usage_ += charge;
|
208
|
+
|
209
|
+
std::pair<HandleTable::iterator,bool> p = table_.insert(e);
|
210
|
+
if (!p.second) {
|
211
|
+
// Kill existing entry
|
212
|
+
LRUHandle* old = const_cast<LRUHandle*>(*(p.first));
|
213
|
+
LRU_Remove(old);
|
214
|
+
table_.erase(p.first);
|
215
|
+
table_.insert(e);
|
216
|
+
Unref(old);
|
217
|
+
}
|
218
|
+
|
219
|
+
while (usage_ > capacity_ && lru_.next != &lru_) {
|
220
|
+
LRUHandle* old = lru_.next;
|
221
|
+
LRU_Remove(old);
|
222
|
+
table_.erase(old);
|
223
|
+
Unref(old);
|
224
|
+
}
|
225
|
+
|
226
|
+
return reinterpret_cast<Handle*>(e);
|
227
|
+
}
|
228
|
+
|
229
|
+
void LRUCache::Erase(const Slice& key) {
|
230
|
+
MutexLock l(&mutex_);
|
231
|
+
|
232
|
+
LRUHandle dummy;
|
233
|
+
dummy.next = &dummy;
|
234
|
+
dummy.value = const_cast<Slice*>(&key);
|
235
|
+
HandleTable::iterator iter = table_.find(&dummy);
|
236
|
+
if (iter != table_.end()) {
|
237
|
+
LRUHandle* e = const_cast<LRUHandle*>(*iter);
|
238
|
+
LRU_Remove(e);
|
239
|
+
table_.erase(iter);
|
240
|
+
Unref(e);
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
uint64_t LRUCache::NewId() {
|
245
|
+
MutexLock l(&mutex_);
|
246
|
+
return ++(last_id_);
|
247
|
+
}
|
248
|
+
|
249
|
+
} // end anonymous namespace
|
250
|
+
|
251
|
+
Cache* NewLRUCache(size_t capacity) {
|
252
|
+
return new LRUCache(capacity);
|
253
|
+
}
|
254
|
+
|
255
|
+
}
|