leveldb-ruby 0.10 → 0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/leveldb/extconf.rb +2 -6
- data/ext/leveldb/leveldb.cc +377 -33
- data/ext/leveldb/platform.rb +83 -0
- data/leveldb/Makefile +29 -28
- data/leveldb/build_detect_platform +23 -4
- data/leveldb/db/builder.cc +1 -1
- data/leveldb/db/builder.h +1 -1
- data/leveldb/db/corruption_test.cc +1 -1
- data/leveldb/db/db_bench.cc +2 -2
- data/leveldb/db/db_impl.cc +8 -16
- data/leveldb/db/db_impl.h +1 -1
- data/leveldb/db/db_iter.cc +1 -1
- data/leveldb/db/db_iter.h +1 -1
- data/leveldb/db/db_test.cc +180 -9
- data/leveldb/db/dbformat.cc +9 -7
- data/leveldb/db/dbformat.h +2 -2
- data/leveldb/db/dbformat_test.cc +1 -1
- data/leveldb/db/filename.cc +6 -2
- data/leveldb/db/filename.h +1 -1
- data/leveldb/db/filename_test.cc +1 -1
- data/leveldb/db/log_format.h +2 -2
- data/leveldb/db/log_reader.cc +2 -2
- data/leveldb/db/log_reader.h +2 -2
- data/leveldb/db/log_test.cc +2 -2
- data/leveldb/db/log_writer.cc +2 -2
- data/leveldb/db/log_writer.h +2 -2
- data/leveldb/db/memtable.cc +1 -1
- data/leveldb/db/memtable.h +1 -1
- data/leveldb/db/repair.cc +2 -2
- data/leveldb/db/skiplist.h +1 -1
- data/leveldb/db/skiplist_test.cc +1 -1
- data/leveldb/db/snapshot.h +1 -1
- data/leveldb/db/table_cache.cc +1 -1
- data/leveldb/db/table_cache.h +1 -1
- data/leveldb/db/version_edit.cc +1 -1
- data/leveldb/db/version_edit.h +1 -1
- data/leveldb/db/version_edit_test.cc +1 -1
- data/leveldb/db/version_set.cc +39 -14
- data/leveldb/db/version_set.h +1 -1
- data/leveldb/db/version_set_test.cc +1 -1
- data/leveldb/db/write_batch.cc +2 -2
- data/leveldb/db/write_batch_internal.h +1 -1
- data/leveldb/db/write_batch_test.cc +1 -1
- 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/cache.h +1 -1
- data/leveldb/include/leveldb/comparator.h +1 -1
- data/leveldb/include/leveldb/db.h +1 -1
- data/leveldb/include/leveldb/env.h +1 -1
- data/leveldb/include/leveldb/iterator.h +1 -1
- data/leveldb/include/leveldb/options.h +1 -1
- data/leveldb/include/leveldb/slice.h +1 -1
- data/leveldb/include/leveldb/status.h +1 -1
- data/leveldb/include/leveldb/table.h +1 -1
- data/leveldb/include/leveldb/table_builder.h +1 -1
- data/leveldb/include/leveldb/write_batch.h +1 -1
- data/leveldb/port/atomic_pointer.h +2 -2
- data/leveldb/port/port_android.cc +2 -2
- data/leveldb/port/port_android.h +2 -2
- data/leveldb/port/port_example.h +2 -2
- data/leveldb/port/port_posix.cc +2 -2
- data/leveldb/port/port_posix.h +11 -3
- data/leveldb/table/block.cc +1 -1
- data/leveldb/table/block.h +1 -1
- data/leveldb/table/block_builder.cc +1 -1
- data/leveldb/table/block_builder.h +1 -1
- data/leveldb/table/format.cc +1 -1
- data/leveldb/table/format.h +1 -1
- data/leveldb/table/iterator.cc +2 -2
- data/leveldb/table/merger.cc +2 -2
- data/leveldb/table/merger.h +1 -1
- data/leveldb/table/table.cc +1 -1
- data/leveldb/table/table_builder.cc +1 -1
- data/leveldb/table/table_test.cc +3 -3
- data/leveldb/table/two_level_iterator.cc +2 -2
- data/leveldb/table/two_level_iterator.h +1 -1
- data/leveldb/util/arena.cc +1 -1
- data/leveldb/util/arena.h +1 -1
- data/leveldb/util/arena_test.cc +1 -1
- data/leveldb/util/cache.cc +1 -1
- data/leveldb/util/cache_test.cc +1 -1
- data/leveldb/util/coding.cc +1 -1
- data/leveldb/util/coding.h +1 -1
- data/leveldb/util/coding_test.cc +1 -1
- data/leveldb/util/comparator.cc +7 -4
- data/leveldb/util/crc32c.cc +2 -2
- data/leveldb/util/crc32c.h +2 -2
- data/leveldb/util/crc32c_test.cc +2 -2
- data/leveldb/util/env.cc +17 -3
- data/leveldb/util/env_posix.cc +2 -2
- data/leveldb/util/env_test.cc +13 -11
- data/leveldb/util/hash.cc +1 -1
- data/leveldb/util/histogram.cc +1 -1
- data/leveldb/util/histogram.h +1 -1
- data/leveldb/util/logging.cc +1 -1
- data/leveldb/util/logging.h +1 -1
- data/leveldb/util/mutexlock.h +1 -1
- data/leveldb/util/options.cc +1 -1
- data/leveldb/util/posix_logger.h +1 -1
- data/leveldb/util/random.h +1 -1
- data/leveldb/util/status.cc +1 -1
- data/leveldb/util/testharness.cc +2 -2
- data/leveldb/util/testharness.h +2 -2
- data/leveldb/util/testutil.cc +2 -2
- data/leveldb/util/testutil.h +2 -2
- data/lib/leveldb.rb +34 -2
- metadata +7 -13
- data/leveldb/port/port_chromium.cc +0 -80
- data/leveldb/port/port_chromium.h +0 -97
- data/leveldb/port/port_osx.cc +0 -50
- data/leveldb/port/port_osx.h +0 -125
- data/leveldb/port/sha1_portable.cc +0 -298
- data/leveldb/port/sha1_portable.h +0 -25
- data/leveldb/port/sha1_test.cc +0 -39
- data/leveldb/util/env_chromium.cc +0 -612
data/leveldb/db/dbformat.cc
CHANGED
@@ -73,9 +73,10 @@ void InternalKeyComparator::FindShortestSeparator(
|
|
73
73
|
Slice user_limit = ExtractUserKey(limit);
|
74
74
|
std::string tmp(user_start.data(), user_start.size());
|
75
75
|
user_comparator_->FindShortestSeparator(&tmp, user_limit);
|
76
|
-
if (
|
77
|
-
|
78
|
-
//
|
76
|
+
if (tmp.size() < user_start.size() &&
|
77
|
+
user_comparator_->Compare(user_start, tmp) < 0) {
|
78
|
+
// User key has become shorter physically, but larger logically.
|
79
|
+
// Tack on the earliest possible number to the shortened user key.
|
79
80
|
PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
|
80
81
|
assert(this->Compare(*start, tmp) < 0);
|
81
82
|
assert(this->Compare(tmp, limit) < 0);
|
@@ -87,9 +88,10 @@ void InternalKeyComparator::FindShortSuccessor(std::string* key) const {
|
|
87
88
|
Slice user_key = ExtractUserKey(*key);
|
88
89
|
std::string tmp(user_key.data(), user_key.size());
|
89
90
|
user_comparator_->FindShortSuccessor(&tmp);
|
90
|
-
if (
|
91
|
-
|
92
|
-
//
|
91
|
+
if (tmp.size() < user_key.size() &&
|
92
|
+
user_comparator_->Compare(user_key, tmp) < 0) {
|
93
|
+
// User key has become shorter physically, but larger logically.
|
94
|
+
// Tack on the earliest possible number to the shortened user key.
|
93
95
|
PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));
|
94
96
|
assert(this->Compare(*key, tmp) < 0);
|
95
97
|
key->swap(tmp);
|
@@ -115,4 +117,4 @@ LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
|
|
115
117
|
end_ = dst;
|
116
118
|
}
|
117
119
|
|
118
|
-
}
|
120
|
+
} // namespace leveldb
|
data/leveldb/db/dbformat.h
CHANGED
@@ -37,7 +37,7 @@ static const int kL0_StopWritesTrigger = 12;
|
|
37
37
|
// space if the same key space is being repeatedly overwritten.
|
38
38
|
static const int kMaxMemCompactLevel = 2;
|
39
39
|
|
40
|
-
}
|
40
|
+
} // namespace config
|
41
41
|
|
42
42
|
class InternalKey;
|
43
43
|
|
@@ -210,6 +210,6 @@ inline LookupKey::~LookupKey() {
|
|
210
210
|
if (start_ != space_) delete[] start_;
|
211
211
|
}
|
212
212
|
|
213
|
-
}
|
213
|
+
} // namespace leveldb
|
214
214
|
|
215
215
|
#endif // STORAGE_LEVELDB_DB_FORMAT_H_
|
data/leveldb/db/dbformat_test.cc
CHANGED
data/leveldb/db/filename.cc
CHANGED
@@ -11,6 +11,10 @@
|
|
11
11
|
|
12
12
|
namespace leveldb {
|
13
13
|
|
14
|
+
// A utility routine: write "data" to the named file and Sync() it.
|
15
|
+
extern Status WriteStringToFileSync(Env* env, const Slice& data,
|
16
|
+
const std::string& fname);
|
17
|
+
|
14
18
|
static std::string MakeFileName(const std::string& name, uint64_t number,
|
15
19
|
const char* suffix) {
|
16
20
|
char buf[100];
|
@@ -122,7 +126,7 @@ Status SetCurrentFile(Env* env, const std::string& dbname,
|
|
122
126
|
assert(contents.starts_with(dbname + "/"));
|
123
127
|
contents.remove_prefix(dbname.size() + 1);
|
124
128
|
std::string tmp = TempFileName(dbname, descriptor_number);
|
125
|
-
Status s =
|
129
|
+
Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp);
|
126
130
|
if (s.ok()) {
|
127
131
|
s = env->RenameFile(tmp, CurrentFileName(dbname));
|
128
132
|
}
|
@@ -132,4 +136,4 @@ Status SetCurrentFile(Env* env, const std::string& dbname,
|
|
132
136
|
return s;
|
133
137
|
}
|
134
138
|
|
135
|
-
}
|
139
|
+
} // namespace leveldb
|
data/leveldb/db/filename.h
CHANGED
data/leveldb/db/filename_test.cc
CHANGED
data/leveldb/db/log_format.h
CHANGED
@@ -29,7 +29,7 @@ static const int kBlockSize = 32768;
|
|
29
29
|
// Header is checksum (4 bytes), type (1 byte), length (2 bytes).
|
30
30
|
static const int kHeaderSize = 4 + 1 + 2;
|
31
31
|
|
32
|
-
}
|
33
|
-
}
|
32
|
+
} // namespace log
|
33
|
+
} // namespace leveldb
|
34
34
|
|
35
35
|
#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_
|
data/leveldb/db/log_reader.cc
CHANGED
data/leveldb/db/log_reader.h
CHANGED
data/leveldb/db/log_test.cc
CHANGED
data/leveldb/db/log_writer.cc
CHANGED
data/leveldb/db/log_writer.h
CHANGED
data/leveldb/db/memtable.cc
CHANGED
data/leveldb/db/memtable.h
CHANGED
data/leveldb/db/repair.cc
CHANGED
@@ -377,11 +377,11 @@ class Repairer {
|
|
377
377
|
fname.c_str(), s.ToString().c_str());
|
378
378
|
}
|
379
379
|
};
|
380
|
-
}
|
380
|
+
} // namespace
|
381
381
|
|
382
382
|
Status RepairDB(const std::string& dbname, const Options& options) {
|
383
383
|
Repairer repairer(dbname, options);
|
384
384
|
return repairer.Run();
|
385
385
|
}
|
386
386
|
|
387
|
-
}
|
387
|
+
} // namespace leveldb
|
data/leveldb/db/skiplist.h
CHANGED
data/leveldb/db/skiplist_test.cc
CHANGED
@@ -371,7 +371,7 @@ TEST(SkipTest, Concurrent3) { RunConcurrent(3); }
|
|
371
371
|
TEST(SkipTest, Concurrent4) { RunConcurrent(4); }
|
372
372
|
TEST(SkipTest, Concurrent5) { RunConcurrent(5); }
|
373
373
|
|
374
|
-
}
|
374
|
+
} // namespace leveldb
|
375
375
|
|
376
376
|
int main(int argc, char** argv) {
|
377
377
|
return leveldb::test::RunAllTests();
|
data/leveldb/db/snapshot.h
CHANGED
data/leveldb/db/table_cache.cc
CHANGED
data/leveldb/db/table_cache.h
CHANGED
data/leveldb/db/version_edit.cc
CHANGED
data/leveldb/db/version_edit.h
CHANGED
data/leveldb/db/version_set.cc
CHANGED
@@ -26,6 +26,11 @@ static const int kTargetFileSize = 2 * 1048576;
|
|
26
26
|
// stop building a single file in a level->level+1 compaction.
|
27
27
|
static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize;
|
28
28
|
|
29
|
+
// Maximum number of bytes in all compacted files. We avoid expanding
|
30
|
+
// the lower level file set of a compaction if it would make the
|
31
|
+
// total compaction cover more than this many bytes.
|
32
|
+
static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize;
|
33
|
+
|
29
34
|
static double MaxBytesForLevel(int level) {
|
30
35
|
// Note: the result for level zero is not really used since we set
|
31
36
|
// the level-0 compaction threshold based on number of files.
|
@@ -61,7 +66,7 @@ std::string IntSetToString(const std::set<uint64_t>& s) {
|
|
61
66
|
result += "}";
|
62
67
|
return result;
|
63
68
|
}
|
64
|
-
}
|
69
|
+
} // namespace
|
65
70
|
|
66
71
|
Version::~Version() {
|
67
72
|
assert(refs_ == 0);
|
@@ -253,7 +258,8 @@ void Version::AddIterators(const ReadOptions& options,
|
|
253
258
|
// If "*iter" points at a value or deletion for user_key, store
|
254
259
|
// either the value, or a NotFound error and return true.
|
255
260
|
// Else return false.
|
256
|
-
static bool GetValue(
|
261
|
+
static bool GetValue(const Comparator* cmp,
|
262
|
+
Iterator* iter, const Slice& user_key,
|
257
263
|
std::string* value,
|
258
264
|
Status* s) {
|
259
265
|
if (!iter->Valid()) {
|
@@ -264,7 +270,7 @@ static bool GetValue(Iterator* iter, const Slice& user_key,
|
|
264
270
|
*s = Status::Corruption("corrupted key for ", user_key);
|
265
271
|
return true;
|
266
272
|
}
|
267
|
-
if (parsed_key.user_key !=
|
273
|
+
if (cmp->Compare(parsed_key.user_key, user_key) != 0) {
|
268
274
|
return false;
|
269
275
|
}
|
270
276
|
switch (parsed_key.type) {
|
@@ -360,7 +366,7 @@ Status Version::Get(const ReadOptions& options,
|
|
360
366
|
f->number,
|
361
367
|
f->file_size);
|
362
368
|
iter->Seek(ikey);
|
363
|
-
const bool done = GetValue(iter, user_key, value, &s);
|
369
|
+
const bool done = GetValue(ucmp, iter, user_key, value, &s);
|
364
370
|
if (!iter->status().ok()) {
|
365
371
|
s = iter->status();
|
366
372
|
delete iter;
|
@@ -450,16 +456,29 @@ void Version::GetOverlappingInputs(
|
|
450
456
|
user_end = end->user_key();
|
451
457
|
}
|
452
458
|
const Comparator* user_cmp = vset_->icmp_.user_comparator();
|
453
|
-
for (size_t i = 0; i < files_[level].size();
|
454
|
-
FileMetaData* f = files_[level][i];
|
455
|
-
|
456
|
-
|
459
|
+
for (size_t i = 0; i < files_[level].size(); ) {
|
460
|
+
FileMetaData* f = files_[level][i++];
|
461
|
+
const Slice file_start = f->smallest.user_key();
|
462
|
+
const Slice file_limit = f->largest.user_key();
|
463
|
+
if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) {
|
457
464
|
// "f" is completely before specified range; skip it
|
458
|
-
} else if (end != NULL &&
|
459
|
-
user_cmp->Compare(f->smallest.user_key(), user_end) > 0) {
|
465
|
+
} else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) {
|
460
466
|
// "f" is completely after specified range; skip it
|
461
467
|
} else {
|
462
468
|
inputs->push_back(f);
|
469
|
+
if (level == 0) {
|
470
|
+
// Level-0 files may overlap each other. So check if the newly
|
471
|
+
// added file has expanded the range. If so, restart search.
|
472
|
+
if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) {
|
473
|
+
user_begin = file_start;
|
474
|
+
inputs->clear();
|
475
|
+
i = 0;
|
476
|
+
} else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) {
|
477
|
+
user_end = file_limit;
|
478
|
+
inputs->clear();
|
479
|
+
i = 0;
|
480
|
+
}
|
481
|
+
}
|
463
482
|
}
|
464
483
|
}
|
465
484
|
}
|
@@ -1209,7 +1228,11 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|
1209
1228
|
if (!c->inputs_[1].empty()) {
|
1210
1229
|
std::vector<FileMetaData*> expanded0;
|
1211
1230
|
current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0);
|
1212
|
-
|
1231
|
+
const int64_t inputs0_size = TotalFileSize(c->inputs_[0]);
|
1232
|
+
const int64_t inputs1_size = TotalFileSize(c->inputs_[1]);
|
1233
|
+
const int64_t expanded0_size = TotalFileSize(expanded0);
|
1234
|
+
if (expanded0.size() > c->inputs_[0].size() &&
|
1235
|
+
inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) {
|
1213
1236
|
InternalKey new_start, new_limit;
|
1214
1237
|
GetRange(expanded0, &new_start, &new_limit);
|
1215
1238
|
std::vector<FileMetaData*> expanded1;
|
@@ -1217,12 +1240,14 @@ void VersionSet::SetupOtherInputs(Compaction* c) {
|
|
1217
1240
|
&expanded1);
|
1218
1241
|
if (expanded1.size() == c->inputs_[1].size()) {
|
1219
1242
|
Log(options_->info_log,
|
1220
|
-
"Expanding@%d %d+%d to %d+%d\n",
|
1243
|
+
"Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n",
|
1221
1244
|
level,
|
1222
1245
|
int(c->inputs_[0].size()),
|
1223
1246
|
int(c->inputs_[1].size()),
|
1247
|
+
long(inputs0_size), long(inputs1_size),
|
1224
1248
|
int(expanded0.size()),
|
1225
|
-
int(expanded1.size())
|
1249
|
+
int(expanded1.size()),
|
1250
|
+
long(expanded0_size), long(inputs1_size));
|
1226
1251
|
smallest = new_start;
|
1227
1252
|
largest = new_limit;
|
1228
1253
|
c->inputs_[0] = expanded0;
|
@@ -1369,4 +1394,4 @@ void Compaction::ReleaseInputs() {
|
|
1369
1394
|
}
|
1370
1395
|
}
|
1371
1396
|
|
1372
|
-
}
|
1397
|
+
} // namespace leveldb
|
data/leveldb/db/version_set.h
CHANGED
data/leveldb/db/write_batch.cc
CHANGED
@@ -120,7 +120,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
|
120
120
|
sequence_++;
|
121
121
|
}
|
122
122
|
};
|
123
|
-
}
|
123
|
+
} // namespace
|
124
124
|
|
125
125
|
Status WriteBatchInternal::InsertInto(const WriteBatch* b,
|
126
126
|
MemTable* memtable) {
|
@@ -135,4 +135,4 @@ void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) {
|
|
135
135
|
b->rep_.assign(contents.data(), contents.size());
|
136
136
|
}
|
137
137
|
|
138
|
-
}
|
138
|
+
} // namespace leveldb
|
@@ -0,0 +1,374 @@
|
|
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 InMemoryEnv : public EnvWrapper {
|
225
|
+
public:
|
226
|
+
explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { }
|
227
|
+
|
228
|
+
virtual ~InMemoryEnv() {
|
229
|
+
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
|
230
|
+
i->second->Unref();
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
// Partial implementation of the Env interface.
|
235
|
+
virtual Status NewSequentialFile(const std::string& fname,
|
236
|
+
SequentialFile** result) {
|
237
|
+
MutexLock lock(&mutex_);
|
238
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
239
|
+
*result = NULL;
|
240
|
+
return Status::IOError(fname, "File not found");
|
241
|
+
}
|
242
|
+
|
243
|
+
*result = new SequentialFileImpl(file_map_[fname]);
|
244
|
+
return Status::OK();
|
245
|
+
}
|
246
|
+
|
247
|
+
virtual Status NewRandomAccessFile(const std::string& fname,
|
248
|
+
RandomAccessFile** result) {
|
249
|
+
MutexLock lock(&mutex_);
|
250
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
251
|
+
*result = NULL;
|
252
|
+
return Status::IOError(fname, "File not found");
|
253
|
+
}
|
254
|
+
|
255
|
+
*result = new RandomAccessFileImpl(file_map_[fname]);
|
256
|
+
return Status::OK();
|
257
|
+
}
|
258
|
+
|
259
|
+
virtual Status NewWritableFile(const std::string& fname,
|
260
|
+
WritableFile** result) {
|
261
|
+
MutexLock lock(&mutex_);
|
262
|
+
if (file_map_.find(fname) != file_map_.end()) {
|
263
|
+
DeleteFileInternal(fname);
|
264
|
+
}
|
265
|
+
|
266
|
+
FileState* file = new FileState();
|
267
|
+
file->Ref();
|
268
|
+
file_map_[fname] = file;
|
269
|
+
|
270
|
+
*result = new WritableFileImpl(file);
|
271
|
+
return Status::OK();
|
272
|
+
}
|
273
|
+
|
274
|
+
virtual bool FileExists(const std::string& fname) {
|
275
|
+
MutexLock lock(&mutex_);
|
276
|
+
return file_map_.find(fname) != file_map_.end();
|
277
|
+
}
|
278
|
+
|
279
|
+
virtual Status GetChildren(const std::string& dir,
|
280
|
+
std::vector<std::string>* result) {
|
281
|
+
MutexLock lock(&mutex_);
|
282
|
+
result->clear();
|
283
|
+
|
284
|
+
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){
|
285
|
+
const std::string& filename = i->first;
|
286
|
+
|
287
|
+
if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&
|
288
|
+
Slice(filename).starts_with(Slice(dir))) {
|
289
|
+
result->push_back(filename.substr(dir.size() + 1));
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
return Status::OK();
|
294
|
+
}
|
295
|
+
|
296
|
+
void DeleteFileInternal(const std::string& fname) {
|
297
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
298
|
+
return;
|
299
|
+
}
|
300
|
+
|
301
|
+
file_map_[fname]->Unref();
|
302
|
+
file_map_.erase(fname);
|
303
|
+
}
|
304
|
+
|
305
|
+
virtual Status DeleteFile(const std::string& fname) {
|
306
|
+
MutexLock lock(&mutex_);
|
307
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
308
|
+
return Status::IOError(fname, "File not found");
|
309
|
+
}
|
310
|
+
|
311
|
+
DeleteFileInternal(fname);
|
312
|
+
return Status::OK();
|
313
|
+
}
|
314
|
+
|
315
|
+
virtual Status CreateDir(const std::string& dirname) {
|
316
|
+
return Status::OK();
|
317
|
+
}
|
318
|
+
|
319
|
+
virtual Status DeleteDir(const std::string& dirname) {
|
320
|
+
return Status::OK();
|
321
|
+
}
|
322
|
+
|
323
|
+
virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) {
|
324
|
+
MutexLock lock(&mutex_);
|
325
|
+
if (file_map_.find(fname) == file_map_.end()) {
|
326
|
+
return Status::IOError(fname, "File not found");
|
327
|
+
}
|
328
|
+
|
329
|
+
*file_size = file_map_[fname]->Size();
|
330
|
+
return Status::OK();
|
331
|
+
}
|
332
|
+
|
333
|
+
virtual Status RenameFile(const std::string& src,
|
334
|
+
const std::string& target) {
|
335
|
+
MutexLock lock(&mutex_);
|
336
|
+
if (file_map_.find(src) == file_map_.end()) {
|
337
|
+
return Status::IOError(src, "File not found");
|
338
|
+
}
|
339
|
+
|
340
|
+
DeleteFileInternal(target);
|
341
|
+
file_map_[target] = file_map_[src];
|
342
|
+
file_map_.erase(src);
|
343
|
+
return Status::OK();
|
344
|
+
}
|
345
|
+
|
346
|
+
virtual Status LockFile(const std::string& fname, FileLock** lock) {
|
347
|
+
*lock = new FileLock;
|
348
|
+
return Status::OK();
|
349
|
+
}
|
350
|
+
|
351
|
+
virtual Status UnlockFile(FileLock* lock) {
|
352
|
+
delete lock;
|
353
|
+
return Status::OK();
|
354
|
+
}
|
355
|
+
|
356
|
+
virtual Status GetTestDirectory(std::string* path) {
|
357
|
+
*path = "/test";
|
358
|
+
return Status::OK();
|
359
|
+
}
|
360
|
+
|
361
|
+
private:
|
362
|
+
// Map from filenames to FileState objects, representing a simple file system.
|
363
|
+
typedef std::map<std::string, FileState*> FileSystem;
|
364
|
+
port::Mutex mutex_;
|
365
|
+
FileSystem file_map_; // Protected by mutex_.
|
366
|
+
};
|
367
|
+
|
368
|
+
} // namespace
|
369
|
+
|
370
|
+
Env* NewMemEnv(Env* base_env) {
|
371
|
+
return new InMemoryEnv(base_env);
|
372
|
+
}
|
373
|
+
|
374
|
+
} // namespace leveldb
|