leveldb 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +95 -0
- data/ext/Rakefile +11 -0
- data/ext/leveldb/LICENSE +27 -0
- data/ext/leveldb/Makefile +206 -0
- data/ext/leveldb/build_config.mk +13 -0
- data/ext/leveldb/db/builder.cc +88 -0
- data/ext/leveldb/db/builder.h +34 -0
- data/ext/leveldb/db/c.cc +595 -0
- data/ext/leveldb/db/c_test.c +390 -0
- data/ext/leveldb/db/corruption_test.cc +359 -0
- data/ext/leveldb/db/db_bench.cc +979 -0
- data/ext/leveldb/db/db_impl.cc +1485 -0
- data/ext/leveldb/db/db_impl.h +203 -0
- data/ext/leveldb/db/db_iter.cc +299 -0
- data/ext/leveldb/db/db_iter.h +26 -0
- data/ext/leveldb/db/db_test.cc +2092 -0
- data/ext/leveldb/db/dbformat.cc +140 -0
- data/ext/leveldb/db/dbformat.h +227 -0
- data/ext/leveldb/db/dbformat_test.cc +112 -0
- data/ext/leveldb/db/filename.cc +139 -0
- data/ext/leveldb/db/filename.h +80 -0
- data/ext/leveldb/db/filename_test.cc +122 -0
- data/ext/leveldb/db/leveldb_main.cc +238 -0
- data/ext/leveldb/db/log_format.h +35 -0
- data/ext/leveldb/db/log_reader.cc +259 -0
- data/ext/leveldb/db/log_reader.h +108 -0
- data/ext/leveldb/db/log_test.cc +500 -0
- data/ext/leveldb/db/log_writer.cc +103 -0
- data/ext/leveldb/db/log_writer.h +48 -0
- data/ext/leveldb/db/memtable.cc +145 -0
- data/ext/leveldb/db/memtable.h +91 -0
- data/ext/leveldb/db/repair.cc +389 -0
- data/ext/leveldb/db/skiplist.h +379 -0
- data/ext/leveldb/db/skiplist_test.cc +378 -0
- data/ext/leveldb/db/snapshot.h +66 -0
- data/ext/leveldb/db/table_cache.cc +121 -0
- data/ext/leveldb/db/table_cache.h +61 -0
- data/ext/leveldb/db/version_edit.cc +266 -0
- data/ext/leveldb/db/version_edit.h +107 -0
- data/ext/leveldb/db/version_edit_test.cc +46 -0
- data/ext/leveldb/db/version_set.cc +1443 -0
- data/ext/leveldb/db/version_set.h +383 -0
- data/ext/leveldb/db/version_set_test.cc +179 -0
- data/ext/leveldb/db/write_batch.cc +147 -0
- data/ext/leveldb/db/write_batch_internal.h +49 -0
- data/ext/leveldb/db/write_batch_test.cc +120 -0
- data/ext/leveldb/doc/bench/db_bench_sqlite3.cc +718 -0
- data/ext/leveldb/doc/bench/db_bench_tree_db.cc +528 -0
- data/ext/leveldb/helpers/memenv/memenv.cc +384 -0
- data/ext/leveldb/helpers/memenv/memenv.h +20 -0
- data/ext/leveldb/helpers/memenv/memenv_test.cc +232 -0
- data/ext/leveldb/include/leveldb/c.h +291 -0
- data/ext/leveldb/include/leveldb/cache.h +99 -0
- data/ext/leveldb/include/leveldb/comparator.h +63 -0
- data/ext/leveldb/include/leveldb/db.h +161 -0
- data/ext/leveldb/include/leveldb/env.h +333 -0
- data/ext/leveldb/include/leveldb/filter_policy.h +70 -0
- data/ext/leveldb/include/leveldb/iterator.h +100 -0
- data/ext/leveldb/include/leveldb/options.h +195 -0
- data/ext/leveldb/include/leveldb/slice.h +109 -0
- data/ext/leveldb/include/leveldb/status.h +106 -0
- data/ext/leveldb/include/leveldb/table.h +85 -0
- data/ext/leveldb/include/leveldb/table_builder.h +92 -0
- data/ext/leveldb/include/leveldb/write_batch.h +64 -0
- data/ext/leveldb/issues/issue178_test.cc +92 -0
- data/ext/leveldb/port/atomic_pointer.h +224 -0
- data/ext/leveldb/port/port.h +19 -0
- data/ext/leveldb/port/port_example.h +135 -0
- data/ext/leveldb/port/port_posix.cc +54 -0
- data/ext/leveldb/port/port_posix.h +157 -0
- data/ext/leveldb/port/thread_annotations.h +59 -0
- data/ext/leveldb/port/win/stdint.h +24 -0
- data/ext/leveldb/table/block.cc +268 -0
- data/ext/leveldb/table/block.h +44 -0
- data/ext/leveldb/table/block_builder.cc +109 -0
- data/ext/leveldb/table/block_builder.h +57 -0
- data/ext/leveldb/table/filter_block.cc +111 -0
- data/ext/leveldb/table/filter_block.h +68 -0
- data/ext/leveldb/table/filter_block_test.cc +128 -0
- data/ext/leveldb/table/format.cc +145 -0
- data/ext/leveldb/table/format.h +108 -0
- data/ext/leveldb/table/iterator.cc +67 -0
- data/ext/leveldb/table/iterator_wrapper.h +63 -0
- data/ext/leveldb/table/merger.cc +197 -0
- data/ext/leveldb/table/merger.h +26 -0
- data/ext/leveldb/table/table.cc +275 -0
- data/ext/leveldb/table/table_builder.cc +270 -0
- data/ext/leveldb/table/table_test.cc +868 -0
- data/ext/leveldb/table/two_level_iterator.cc +182 -0
- data/ext/leveldb/table/two_level_iterator.h +34 -0
- data/ext/leveldb/util/arena.cc +68 -0
- data/ext/leveldb/util/arena.h +68 -0
- data/ext/leveldb/util/arena_test.cc +68 -0
- data/ext/leveldb/util/bloom.cc +95 -0
- data/ext/leveldb/util/bloom_test.cc +160 -0
- data/ext/leveldb/util/cache.cc +325 -0
- data/ext/leveldb/util/cache_test.cc +186 -0
- data/ext/leveldb/util/coding.cc +194 -0
- data/ext/leveldb/util/coding.h +104 -0
- data/ext/leveldb/util/coding_test.cc +196 -0
- data/ext/leveldb/util/comparator.cc +81 -0
- data/ext/leveldb/util/crc32c.cc +332 -0
- data/ext/leveldb/util/crc32c.h +45 -0
- data/ext/leveldb/util/crc32c_test.cc +72 -0
- data/ext/leveldb/util/env.cc +96 -0
- data/ext/leveldb/util/env_posix.cc +698 -0
- data/ext/leveldb/util/env_test.cc +104 -0
- data/ext/leveldb/util/filter_policy.cc +11 -0
- data/ext/leveldb/util/hash.cc +52 -0
- data/ext/leveldb/util/hash.h +19 -0
- data/ext/leveldb/util/histogram.cc +139 -0
- data/ext/leveldb/util/histogram.h +42 -0
- data/ext/leveldb/util/logging.cc +81 -0
- data/ext/leveldb/util/logging.h +47 -0
- data/ext/leveldb/util/mutexlock.h +41 -0
- data/ext/leveldb/util/options.cc +29 -0
- data/ext/leveldb/util/posix_logger.h +98 -0
- data/ext/leveldb/util/random.h +59 -0
- data/ext/leveldb/util/status.cc +75 -0
- data/ext/leveldb/util/testharness.cc +77 -0
- data/ext/leveldb/util/testharness.h +138 -0
- data/ext/leveldb/util/testutil.cc +51 -0
- data/ext/leveldb/util/testutil.h +53 -0
- data/lib/leveldb/version.rb +3 -0
- data/lib/leveldb.rb +1006 -0
- metadata +228 -0
|
@@ -0,0 +1,384 @@
|
|
|
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 "helpers/memenv/memenv.h"
|
|
6
|
+
|
|
7
|
+
#include "leveldb/env.h"
|
|
8
|
+
#include "leveldb/status.h"
|
|
9
|
+
#include "port/port.h"
|
|
10
|
+
#include "util/mutexlock.h"
|
|
11
|
+
#include <map>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <vector>
|
|
15
|
+
|
|
16
|
+
namespace leveldb {
|
|
17
|
+
|
|
18
|
+
namespace {
|
|
19
|
+
|
|
20
|
+
class FileState {
|
|
21
|
+
public:
|
|
22
|
+
// FileStates are reference counted. The initial reference count is zero
|
|
23
|
+
// and the caller must call Ref() at least once.
|
|
24
|
+
FileState() : refs_(0), size_(0) {}
|
|
25
|
+
|
|
26
|
+
// Increase the reference count.
|
|
27
|
+
void Ref() {
|
|
28
|
+
MutexLock lock(&refs_mutex_);
|
|
29
|
+
++refs_;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Decrease the reference count. Delete if this is the last reference.
|
|
33
|
+
void Unref() {
|
|
34
|
+
bool do_delete = false;
|
|
35
|
+
|
|
36
|
+
{
|
|
37
|
+
MutexLock lock(&refs_mutex_);
|
|
38
|
+
--refs_;
|
|
39
|
+
assert(refs_ >= 0);
|
|
40
|
+
if (refs_ <= 0) {
|
|
41
|
+
do_delete = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (do_delete) {
|
|
46
|
+
delete this;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
uint64_t Size() const { return size_; }
|
|
51
|
+
|
|
52
|
+
Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
|
|
53
|
+
if (offset > size_) {
|
|
54
|
+
return Status::IOError("Offset greater than file size.");
|
|
55
|
+
}
|
|
56
|
+
const uint64_t available = size_ - offset;
|
|
57
|
+
if (n > available) {
|
|
58
|
+
n = available;
|
|
59
|
+
}
|
|
60
|
+
if (n == 0) {
|
|
61
|
+
*result = Slice();
|
|
62
|
+
return Status::OK();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
size_t block = offset / kBlockSize;
|
|
66
|
+
size_t block_offset = offset % kBlockSize;
|
|
67
|
+
|
|
68
|
+
if (n <= kBlockSize - block_offset) {
|
|
69
|
+
// The requested bytes are all in the first block.
|
|
70
|
+
*result = Slice(blocks_[block] + block_offset, n);
|
|
71
|
+
return Status::OK();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
size_t bytes_to_copy = n;
|
|
75
|
+
char* dst = scratch;
|
|
76
|
+
|
|
77
|
+
while (bytes_to_copy > 0) {
|
|
78
|
+
size_t avail = kBlockSize - block_offset;
|
|
79
|
+
if (avail > bytes_to_copy) {
|
|
80
|
+
avail = bytes_to_copy;
|
|
81
|
+
}
|
|
82
|
+
memcpy(dst, blocks_[block] + block_offset, avail);
|
|
83
|
+
|
|
84
|
+
bytes_to_copy -= avail;
|
|
85
|
+
dst += avail;
|
|
86
|
+
block++;
|
|
87
|
+
block_offset = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
*result = Slice(scratch, n);
|
|
91
|
+
return Status::OK();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
Status Append(const Slice& data) {
|
|
95
|
+
const char* src = data.data();
|
|
96
|
+
size_t src_len = data.size();
|
|
97
|
+
|
|
98
|
+
while (src_len > 0) {
|
|
99
|
+
size_t avail;
|
|
100
|
+
size_t offset = size_ % kBlockSize;
|
|
101
|
+
|
|
102
|
+
if (offset != 0) {
|
|
103
|
+
// There is some room in the last block.
|
|
104
|
+
avail = kBlockSize - offset;
|
|
105
|
+
} else {
|
|
106
|
+
// No room in the last block; push new one.
|
|
107
|
+
blocks_.push_back(new char[kBlockSize]);
|
|
108
|
+
avail = kBlockSize;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (avail > src_len) {
|
|
112
|
+
avail = src_len;
|
|
113
|
+
}
|
|
114
|
+
memcpy(blocks_.back() + offset, src, avail);
|
|
115
|
+
src_len -= avail;
|
|
116
|
+
src += avail;
|
|
117
|
+
size_ += avail;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return Status::OK();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private:
|
|
124
|
+
// Private since only Unref() should be used to delete it.
|
|
125
|
+
~FileState() {
|
|
126
|
+
for (std::vector<char*>::iterator i = blocks_.begin(); i != blocks_.end();
|
|
127
|
+
++i) {
|
|
128
|
+
delete [] *i;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// No copying allowed.
|
|
133
|
+
FileState(const FileState&);
|
|
134
|
+
void operator=(const FileState&);
|
|
135
|
+
|
|
136
|
+
port::Mutex refs_mutex_;
|
|
137
|
+
int refs_; // Protected by refs_mutex_;
|
|
138
|
+
|
|
139
|
+
// The following fields are not protected by any mutex. They are only mutable
|
|
140
|
+
// while the file is being written, and concurrent access is not allowed
|
|
141
|
+
// to writable files.
|
|
142
|
+
std::vector<char*> blocks_;
|
|
143
|
+
uint64_t size_;
|
|
144
|
+
|
|
145
|
+
enum { kBlockSize = 8 * 1024 };
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
class SequentialFileImpl : public SequentialFile {
|
|
149
|
+
public:
|
|
150
|
+
explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
|
|
151
|
+
file_->Ref();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
~SequentialFileImpl() {
|
|
155
|
+
file_->Unref();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
virtual Status Read(size_t n, Slice* result, char* scratch) {
|
|
159
|
+
Status s = file_->Read(pos_, n, result, scratch);
|
|
160
|
+
if (s.ok()) {
|
|
161
|
+
pos_ += result->size();
|
|
162
|
+
}
|
|
163
|
+
return s;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
virtual Status Skip(uint64_t n) {
|
|
167
|
+
if (pos_ > file_->Size()) {
|
|
168
|
+
return Status::IOError("pos_ > file_->Size()");
|
|
169
|
+
}
|
|
170
|
+
const size_t available = file_->Size() - pos_;
|
|
171
|
+
if (n > available) {
|
|
172
|
+
n = available;
|
|
173
|
+
}
|
|
174
|
+
pos_ += n;
|
|
175
|
+
return Status::OK();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private:
|
|
179
|
+
FileState* file_;
|
|
180
|
+
size_t pos_;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
class RandomAccessFileImpl : public RandomAccessFile {
|
|
184
|
+
public:
|
|
185
|
+
explicit RandomAccessFileImpl(FileState* file) : file_(file) {
|
|
186
|
+
file_->Ref();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
~RandomAccessFileImpl() {
|
|
190
|
+
file_->Unref();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
|
194
|
+
char* scratch) const {
|
|
195
|
+
return file_->Read(offset, n, result, scratch);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private:
|
|
199
|
+
FileState* file_;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
class WritableFileImpl : public WritableFile {
|
|
203
|
+
public:
|
|
204
|
+
WritableFileImpl(FileState* file) : file_(file) {
|
|
205
|
+
file_->Ref();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
~WritableFileImpl() {
|
|
209
|
+
file_->Unref();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
virtual Status Append(const Slice& data) {
|
|
213
|
+
return file_->Append(data);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
virtual Status Close() { return Status::OK(); }
|
|
217
|
+
virtual Status Flush() { return Status::OK(); }
|
|
218
|
+
virtual Status Sync() { return Status::OK(); }
|
|
219
|
+
|
|
220
|
+
private:
|
|
221
|
+
FileState* file_;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
class NoOpLogger : public Logger {
|
|
225
|
+
public:
|
|
226
|
+
virtual void Logv(const char* format, va_list ap) { }
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
class InMemoryEnv : public EnvWrapper {
|
|
230
|
+
public:
|
|
231
|
+
explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { }
|
|
232
|
+
|
|
233
|
+
virtual ~InMemoryEnv() {
|
|
234
|
+
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
|
|
235
|
+
i->second->Unref();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Partial implementation of the Env interface.
|
|
240
|
+
virtual Status NewSequentialFile(const std::string& fname,
|
|
241
|
+
SequentialFile** result) {
|
|
242
|
+
MutexLock lock(&mutex_);
|
|
243
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
|
244
|
+
*result = NULL;
|
|
245
|
+
return Status::IOError(fname, "File not found");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
*result = new SequentialFileImpl(file_map_[fname]);
|
|
249
|
+
return Status::OK();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
virtual Status NewRandomAccessFile(const std::string& fname,
|
|
253
|
+
RandomAccessFile** result) {
|
|
254
|
+
MutexLock lock(&mutex_);
|
|
255
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
|
256
|
+
*result = NULL;
|
|
257
|
+
return Status::IOError(fname, "File not found");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
*result = new RandomAccessFileImpl(file_map_[fname]);
|
|
261
|
+
return Status::OK();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
virtual Status NewWritableFile(const std::string& fname,
|
|
265
|
+
WritableFile** result) {
|
|
266
|
+
MutexLock lock(&mutex_);
|
|
267
|
+
if (file_map_.find(fname) != file_map_.end()) {
|
|
268
|
+
DeleteFileInternal(fname);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
FileState* file = new FileState();
|
|
272
|
+
file->Ref();
|
|
273
|
+
file_map_[fname] = file;
|
|
274
|
+
|
|
275
|
+
*result = new WritableFileImpl(file);
|
|
276
|
+
return Status::OK();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
virtual bool FileExists(const std::string& fname) {
|
|
280
|
+
MutexLock lock(&mutex_);
|
|
281
|
+
return file_map_.find(fname) != file_map_.end();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
virtual Status GetChildren(const std::string& dir,
|
|
285
|
+
std::vector<std::string>* result) {
|
|
286
|
+
MutexLock lock(&mutex_);
|
|
287
|
+
result->clear();
|
|
288
|
+
|
|
289
|
+
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
|
|
290
|
+
const std::string& filename = i->first;
|
|
291
|
+
|
|
292
|
+
if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&
|
|
293
|
+
Slice(filename).starts_with(Slice(dir))) {
|
|
294
|
+
result->push_back(filename.substr(dir.size() + 1));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return Status::OK();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
void DeleteFileInternal(const std::string& fname) {
|
|
302
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
file_map_[fname]->Unref();
|
|
307
|
+
file_map_.erase(fname);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
virtual Status DeleteFile(const std::string& fname) {
|
|
311
|
+
MutexLock lock(&mutex_);
|
|
312
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
|
313
|
+
return Status::IOError(fname, "File not found");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
DeleteFileInternal(fname);
|
|
317
|
+
return Status::OK();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
virtual Status CreateDir(const std::string& dirname) {
|
|
321
|
+
return Status::OK();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
virtual Status DeleteDir(const std::string& dirname) {
|
|
325
|
+
return Status::OK();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) {
|
|
329
|
+
MutexLock lock(&mutex_);
|
|
330
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
|
331
|
+
return Status::IOError(fname, "File not found");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
*file_size = file_map_[fname]->Size();
|
|
335
|
+
return Status::OK();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
virtual Status RenameFile(const std::string& src,
|
|
339
|
+
const std::string& target) {
|
|
340
|
+
MutexLock lock(&mutex_);
|
|
341
|
+
if (file_map_.find(src) == file_map_.end()) {
|
|
342
|
+
return Status::IOError(src, "File not found");
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
DeleteFileInternal(target);
|
|
346
|
+
file_map_[target] = file_map_[src];
|
|
347
|
+
file_map_.erase(src);
|
|
348
|
+
return Status::OK();
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
virtual Status LockFile(const std::string& fname, FileLock** lock) {
|
|
352
|
+
*lock = new FileLock;
|
|
353
|
+
return Status::OK();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
virtual Status UnlockFile(FileLock* lock) {
|
|
357
|
+
delete lock;
|
|
358
|
+
return Status::OK();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
virtual Status GetTestDirectory(std::string* path) {
|
|
362
|
+
*path = "/test";
|
|
363
|
+
return Status::OK();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
virtual Status NewLogger(const std::string& fname, Logger** result) {
|
|
367
|
+
*result = new NoOpLogger;
|
|
368
|
+
return Status::OK();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private:
|
|
372
|
+
// Map from filenames to FileState objects, representing a simple file system.
|
|
373
|
+
typedef std::map<std::string, FileState*> FileSystem;
|
|
374
|
+
port::Mutex mutex_;
|
|
375
|
+
FileSystem file_map_; // Protected by mutex_.
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
} // namespace
|
|
379
|
+
|
|
380
|
+
Env* NewMemEnv(Env* base_env) {
|
|
381
|
+
return new InMemoryEnv(base_env);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
} // namespace leveldb
|
|
@@ -0,0 +1,20 @@
|
|
|
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_HELPERS_MEMENV_MEMENV_H_
|
|
6
|
+
#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_
|
|
7
|
+
|
|
8
|
+
namespace leveldb {
|
|
9
|
+
|
|
10
|
+
class Env;
|
|
11
|
+
|
|
12
|
+
// Returns a new environment that stores its data in memory and delegates
|
|
13
|
+
// all non-file-storage tasks to base_env. The caller must delete the result
|
|
14
|
+
// when it is no longer needed.
|
|
15
|
+
// *base_env must remain live while the result is in use.
|
|
16
|
+
Env* NewMemEnv(Env* base_env);
|
|
17
|
+
|
|
18
|
+
} // namespace leveldb
|
|
19
|
+
|
|
20
|
+
#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_
|
|
@@ -0,0 +1,232 @@
|
|
|
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 "helpers/memenv/memenv.h"
|
|
6
|
+
|
|
7
|
+
#include "db/db_impl.h"
|
|
8
|
+
#include "leveldb/db.h"
|
|
9
|
+
#include "leveldb/env.h"
|
|
10
|
+
#include "util/testharness.h"
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace leveldb {
|
|
15
|
+
|
|
16
|
+
class MemEnvTest {
|
|
17
|
+
public:
|
|
18
|
+
Env* env_;
|
|
19
|
+
|
|
20
|
+
MemEnvTest()
|
|
21
|
+
: env_(NewMemEnv(Env::Default())) {
|
|
22
|
+
}
|
|
23
|
+
~MemEnvTest() {
|
|
24
|
+
delete env_;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
TEST(MemEnvTest, Basics) {
|
|
29
|
+
uint64_t file_size;
|
|
30
|
+
WritableFile* writable_file;
|
|
31
|
+
std::vector<std::string> children;
|
|
32
|
+
|
|
33
|
+
ASSERT_OK(env_->CreateDir("/dir"));
|
|
34
|
+
|
|
35
|
+
// Check that the directory is empty.
|
|
36
|
+
ASSERT_TRUE(!env_->FileExists("/dir/non_existent"));
|
|
37
|
+
ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok());
|
|
38
|
+
ASSERT_OK(env_->GetChildren("/dir", &children));
|
|
39
|
+
ASSERT_EQ(0, children.size());
|
|
40
|
+
|
|
41
|
+
// Create a file.
|
|
42
|
+
ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
|
43
|
+
delete writable_file;
|
|
44
|
+
|
|
45
|
+
// Check that the file exists.
|
|
46
|
+
ASSERT_TRUE(env_->FileExists("/dir/f"));
|
|
47
|
+
ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
|
|
48
|
+
ASSERT_EQ(0, file_size);
|
|
49
|
+
ASSERT_OK(env_->GetChildren("/dir", &children));
|
|
50
|
+
ASSERT_EQ(1, children.size());
|
|
51
|
+
ASSERT_EQ("f", children[0]);
|
|
52
|
+
|
|
53
|
+
// Write to the file.
|
|
54
|
+
ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
|
55
|
+
ASSERT_OK(writable_file->Append("abc"));
|
|
56
|
+
delete writable_file;
|
|
57
|
+
|
|
58
|
+
// Check for expected size.
|
|
59
|
+
ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));
|
|
60
|
+
ASSERT_EQ(3, file_size);
|
|
61
|
+
|
|
62
|
+
// Check that renaming works.
|
|
63
|
+
ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());
|
|
64
|
+
ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g"));
|
|
65
|
+
ASSERT_TRUE(!env_->FileExists("/dir/f"));
|
|
66
|
+
ASSERT_TRUE(env_->FileExists("/dir/g"));
|
|
67
|
+
ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));
|
|
68
|
+
ASSERT_EQ(3, file_size);
|
|
69
|
+
|
|
70
|
+
// Check that opening non-existent file fails.
|
|
71
|
+
SequentialFile* seq_file;
|
|
72
|
+
RandomAccessFile* rand_file;
|
|
73
|
+
ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok());
|
|
74
|
+
ASSERT_TRUE(!seq_file);
|
|
75
|
+
ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok());
|
|
76
|
+
ASSERT_TRUE(!rand_file);
|
|
77
|
+
|
|
78
|
+
// Check that deleting works.
|
|
79
|
+
ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok());
|
|
80
|
+
ASSERT_OK(env_->DeleteFile("/dir/g"));
|
|
81
|
+
ASSERT_TRUE(!env_->FileExists("/dir/g"));
|
|
82
|
+
ASSERT_OK(env_->GetChildren("/dir", &children));
|
|
83
|
+
ASSERT_EQ(0, children.size());
|
|
84
|
+
ASSERT_OK(env_->DeleteDir("/dir"));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
TEST(MemEnvTest, ReadWrite) {
|
|
88
|
+
WritableFile* writable_file;
|
|
89
|
+
SequentialFile* seq_file;
|
|
90
|
+
RandomAccessFile* rand_file;
|
|
91
|
+
Slice result;
|
|
92
|
+
char scratch[100];
|
|
93
|
+
|
|
94
|
+
ASSERT_OK(env_->CreateDir("/dir"));
|
|
95
|
+
|
|
96
|
+
ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
|
97
|
+
ASSERT_OK(writable_file->Append("hello "));
|
|
98
|
+
ASSERT_OK(writable_file->Append("world"));
|
|
99
|
+
delete writable_file;
|
|
100
|
+
|
|
101
|
+
// Read sequentially.
|
|
102
|
+
ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));
|
|
103
|
+
ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello".
|
|
104
|
+
ASSERT_EQ(0, result.compare("hello"));
|
|
105
|
+
ASSERT_OK(seq_file->Skip(1));
|
|
106
|
+
ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world".
|
|
107
|
+
ASSERT_EQ(0, result.compare("world"));
|
|
108
|
+
ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF.
|
|
109
|
+
ASSERT_EQ(0, result.size());
|
|
110
|
+
ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file.
|
|
111
|
+
ASSERT_OK(seq_file->Read(1000, &result, scratch));
|
|
112
|
+
ASSERT_EQ(0, result.size());
|
|
113
|
+
delete seq_file;
|
|
114
|
+
|
|
115
|
+
// Random reads.
|
|
116
|
+
ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file));
|
|
117
|
+
ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world".
|
|
118
|
+
ASSERT_EQ(0, result.compare("world"));
|
|
119
|
+
ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello".
|
|
120
|
+
ASSERT_EQ(0, result.compare("hello"));
|
|
121
|
+
ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d".
|
|
122
|
+
ASSERT_EQ(0, result.compare("d"));
|
|
123
|
+
|
|
124
|
+
// Too high offset.
|
|
125
|
+
ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok());
|
|
126
|
+
delete rand_file;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
TEST(MemEnvTest, Locks) {
|
|
130
|
+
FileLock* lock;
|
|
131
|
+
|
|
132
|
+
// These are no-ops, but we test they return success.
|
|
133
|
+
ASSERT_OK(env_->LockFile("some file", &lock));
|
|
134
|
+
ASSERT_OK(env_->UnlockFile(lock));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
TEST(MemEnvTest, Misc) {
|
|
138
|
+
std::string test_dir;
|
|
139
|
+
ASSERT_OK(env_->GetTestDirectory(&test_dir));
|
|
140
|
+
ASSERT_TRUE(!test_dir.empty());
|
|
141
|
+
|
|
142
|
+
WritableFile* writable_file;
|
|
143
|
+
ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file));
|
|
144
|
+
|
|
145
|
+
// These are no-ops, but we test they return success.
|
|
146
|
+
ASSERT_OK(writable_file->Sync());
|
|
147
|
+
ASSERT_OK(writable_file->Flush());
|
|
148
|
+
ASSERT_OK(writable_file->Close());
|
|
149
|
+
delete writable_file;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
TEST(MemEnvTest, LargeWrite) {
|
|
153
|
+
const size_t kWriteSize = 300 * 1024;
|
|
154
|
+
char* scratch = new char[kWriteSize * 2];
|
|
155
|
+
|
|
156
|
+
std::string write_data;
|
|
157
|
+
for (size_t i = 0; i < kWriteSize; ++i) {
|
|
158
|
+
write_data.append(1, static_cast<char>(i));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
WritableFile* writable_file;
|
|
162
|
+
ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
|
163
|
+
ASSERT_OK(writable_file->Append("foo"));
|
|
164
|
+
ASSERT_OK(writable_file->Append(write_data));
|
|
165
|
+
delete writable_file;
|
|
166
|
+
|
|
167
|
+
SequentialFile* seq_file;
|
|
168
|
+
Slice result;
|
|
169
|
+
ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));
|
|
170
|
+
ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".
|
|
171
|
+
ASSERT_EQ(0, result.compare("foo"));
|
|
172
|
+
|
|
173
|
+
size_t read = 0;
|
|
174
|
+
std::string read_data;
|
|
175
|
+
while (read < kWriteSize) {
|
|
176
|
+
ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));
|
|
177
|
+
read_data.append(result.data(), result.size());
|
|
178
|
+
read += result.size();
|
|
179
|
+
}
|
|
180
|
+
ASSERT_TRUE(write_data == read_data);
|
|
181
|
+
delete seq_file;
|
|
182
|
+
delete [] scratch;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
TEST(MemEnvTest, DBTest) {
|
|
186
|
+
Options options;
|
|
187
|
+
options.create_if_missing = true;
|
|
188
|
+
options.env = env_;
|
|
189
|
+
DB* db;
|
|
190
|
+
|
|
191
|
+
const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")};
|
|
192
|
+
const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")};
|
|
193
|
+
|
|
194
|
+
ASSERT_OK(DB::Open(options, "/dir/db", &db));
|
|
195
|
+
for (size_t i = 0; i < 3; ++i) {
|
|
196
|
+
ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i]));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
for (size_t i = 0; i < 3; ++i) {
|
|
200
|
+
std::string res;
|
|
201
|
+
ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));
|
|
202
|
+
ASSERT_TRUE(res == vals[i]);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
Iterator* iterator = db->NewIterator(ReadOptions());
|
|
206
|
+
iterator->SeekToFirst();
|
|
207
|
+
for (size_t i = 0; i < 3; ++i) {
|
|
208
|
+
ASSERT_TRUE(iterator->Valid());
|
|
209
|
+
ASSERT_TRUE(keys[i] == iterator->key());
|
|
210
|
+
ASSERT_TRUE(vals[i] == iterator->value());
|
|
211
|
+
iterator->Next();
|
|
212
|
+
}
|
|
213
|
+
ASSERT_TRUE(!iterator->Valid());
|
|
214
|
+
delete iterator;
|
|
215
|
+
|
|
216
|
+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db);
|
|
217
|
+
ASSERT_OK(dbi->TEST_CompactMemTable());
|
|
218
|
+
|
|
219
|
+
for (size_t i = 0; i < 3; ++i) {
|
|
220
|
+
std::string res;
|
|
221
|
+
ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));
|
|
222
|
+
ASSERT_TRUE(res == vals[i]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
delete db;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
} // namespace leveldb
|
|
229
|
+
|
|
230
|
+
int main(int argc, char** argv) {
|
|
231
|
+
return leveldb::test::RunAllTests();
|
|
232
|
+
}
|