leveldb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +95 -0
  4. data/ext/Rakefile +11 -0
  5. data/ext/leveldb/LICENSE +27 -0
  6. data/ext/leveldb/Makefile +206 -0
  7. data/ext/leveldb/build_config.mk +13 -0
  8. data/ext/leveldb/db/builder.cc +88 -0
  9. data/ext/leveldb/db/builder.h +34 -0
  10. data/ext/leveldb/db/c.cc +595 -0
  11. data/ext/leveldb/db/c_test.c +390 -0
  12. data/ext/leveldb/db/corruption_test.cc +359 -0
  13. data/ext/leveldb/db/db_bench.cc +979 -0
  14. data/ext/leveldb/db/db_impl.cc +1485 -0
  15. data/ext/leveldb/db/db_impl.h +203 -0
  16. data/ext/leveldb/db/db_iter.cc +299 -0
  17. data/ext/leveldb/db/db_iter.h +26 -0
  18. data/ext/leveldb/db/db_test.cc +2092 -0
  19. data/ext/leveldb/db/dbformat.cc +140 -0
  20. data/ext/leveldb/db/dbformat.h +227 -0
  21. data/ext/leveldb/db/dbformat_test.cc +112 -0
  22. data/ext/leveldb/db/filename.cc +139 -0
  23. data/ext/leveldb/db/filename.h +80 -0
  24. data/ext/leveldb/db/filename_test.cc +122 -0
  25. data/ext/leveldb/db/leveldb_main.cc +238 -0
  26. data/ext/leveldb/db/log_format.h +35 -0
  27. data/ext/leveldb/db/log_reader.cc +259 -0
  28. data/ext/leveldb/db/log_reader.h +108 -0
  29. data/ext/leveldb/db/log_test.cc +500 -0
  30. data/ext/leveldb/db/log_writer.cc +103 -0
  31. data/ext/leveldb/db/log_writer.h +48 -0
  32. data/ext/leveldb/db/memtable.cc +145 -0
  33. data/ext/leveldb/db/memtable.h +91 -0
  34. data/ext/leveldb/db/repair.cc +389 -0
  35. data/ext/leveldb/db/skiplist.h +379 -0
  36. data/ext/leveldb/db/skiplist_test.cc +378 -0
  37. data/ext/leveldb/db/snapshot.h +66 -0
  38. data/ext/leveldb/db/table_cache.cc +121 -0
  39. data/ext/leveldb/db/table_cache.h +61 -0
  40. data/ext/leveldb/db/version_edit.cc +266 -0
  41. data/ext/leveldb/db/version_edit.h +107 -0
  42. data/ext/leveldb/db/version_edit_test.cc +46 -0
  43. data/ext/leveldb/db/version_set.cc +1443 -0
  44. data/ext/leveldb/db/version_set.h +383 -0
  45. data/ext/leveldb/db/version_set_test.cc +179 -0
  46. data/ext/leveldb/db/write_batch.cc +147 -0
  47. data/ext/leveldb/db/write_batch_internal.h +49 -0
  48. data/ext/leveldb/db/write_batch_test.cc +120 -0
  49. data/ext/leveldb/doc/bench/db_bench_sqlite3.cc +718 -0
  50. data/ext/leveldb/doc/bench/db_bench_tree_db.cc +528 -0
  51. data/ext/leveldb/helpers/memenv/memenv.cc +384 -0
  52. data/ext/leveldb/helpers/memenv/memenv.h +20 -0
  53. data/ext/leveldb/helpers/memenv/memenv_test.cc +232 -0
  54. data/ext/leveldb/include/leveldb/c.h +291 -0
  55. data/ext/leveldb/include/leveldb/cache.h +99 -0
  56. data/ext/leveldb/include/leveldb/comparator.h +63 -0
  57. data/ext/leveldb/include/leveldb/db.h +161 -0
  58. data/ext/leveldb/include/leveldb/env.h +333 -0
  59. data/ext/leveldb/include/leveldb/filter_policy.h +70 -0
  60. data/ext/leveldb/include/leveldb/iterator.h +100 -0
  61. data/ext/leveldb/include/leveldb/options.h +195 -0
  62. data/ext/leveldb/include/leveldb/slice.h +109 -0
  63. data/ext/leveldb/include/leveldb/status.h +106 -0
  64. data/ext/leveldb/include/leveldb/table.h +85 -0
  65. data/ext/leveldb/include/leveldb/table_builder.h +92 -0
  66. data/ext/leveldb/include/leveldb/write_batch.h +64 -0
  67. data/ext/leveldb/issues/issue178_test.cc +92 -0
  68. data/ext/leveldb/port/atomic_pointer.h +224 -0
  69. data/ext/leveldb/port/port.h +19 -0
  70. data/ext/leveldb/port/port_example.h +135 -0
  71. data/ext/leveldb/port/port_posix.cc +54 -0
  72. data/ext/leveldb/port/port_posix.h +157 -0
  73. data/ext/leveldb/port/thread_annotations.h +59 -0
  74. data/ext/leveldb/port/win/stdint.h +24 -0
  75. data/ext/leveldb/table/block.cc +268 -0
  76. data/ext/leveldb/table/block.h +44 -0
  77. data/ext/leveldb/table/block_builder.cc +109 -0
  78. data/ext/leveldb/table/block_builder.h +57 -0
  79. data/ext/leveldb/table/filter_block.cc +111 -0
  80. data/ext/leveldb/table/filter_block.h +68 -0
  81. data/ext/leveldb/table/filter_block_test.cc +128 -0
  82. data/ext/leveldb/table/format.cc +145 -0
  83. data/ext/leveldb/table/format.h +108 -0
  84. data/ext/leveldb/table/iterator.cc +67 -0
  85. data/ext/leveldb/table/iterator_wrapper.h +63 -0
  86. data/ext/leveldb/table/merger.cc +197 -0
  87. data/ext/leveldb/table/merger.h +26 -0
  88. data/ext/leveldb/table/table.cc +275 -0
  89. data/ext/leveldb/table/table_builder.cc +270 -0
  90. data/ext/leveldb/table/table_test.cc +868 -0
  91. data/ext/leveldb/table/two_level_iterator.cc +182 -0
  92. data/ext/leveldb/table/two_level_iterator.h +34 -0
  93. data/ext/leveldb/util/arena.cc +68 -0
  94. data/ext/leveldb/util/arena.h +68 -0
  95. data/ext/leveldb/util/arena_test.cc +68 -0
  96. data/ext/leveldb/util/bloom.cc +95 -0
  97. data/ext/leveldb/util/bloom_test.cc +160 -0
  98. data/ext/leveldb/util/cache.cc +325 -0
  99. data/ext/leveldb/util/cache_test.cc +186 -0
  100. data/ext/leveldb/util/coding.cc +194 -0
  101. data/ext/leveldb/util/coding.h +104 -0
  102. data/ext/leveldb/util/coding_test.cc +196 -0
  103. data/ext/leveldb/util/comparator.cc +81 -0
  104. data/ext/leveldb/util/crc32c.cc +332 -0
  105. data/ext/leveldb/util/crc32c.h +45 -0
  106. data/ext/leveldb/util/crc32c_test.cc +72 -0
  107. data/ext/leveldb/util/env.cc +96 -0
  108. data/ext/leveldb/util/env_posix.cc +698 -0
  109. data/ext/leveldb/util/env_test.cc +104 -0
  110. data/ext/leveldb/util/filter_policy.cc +11 -0
  111. data/ext/leveldb/util/hash.cc +52 -0
  112. data/ext/leveldb/util/hash.h +19 -0
  113. data/ext/leveldb/util/histogram.cc +139 -0
  114. data/ext/leveldb/util/histogram.h +42 -0
  115. data/ext/leveldb/util/logging.cc +81 -0
  116. data/ext/leveldb/util/logging.h +47 -0
  117. data/ext/leveldb/util/mutexlock.h +41 -0
  118. data/ext/leveldb/util/options.cc +29 -0
  119. data/ext/leveldb/util/posix_logger.h +98 -0
  120. data/ext/leveldb/util/random.h +59 -0
  121. data/ext/leveldb/util/status.cc +75 -0
  122. data/ext/leveldb/util/testharness.cc +77 -0
  123. data/ext/leveldb/util/testharness.h +138 -0
  124. data/ext/leveldb/util/testutil.cc +51 -0
  125. data/ext/leveldb/util/testutil.h +53 -0
  126. data/lib/leveldb/version.rb +3 -0
  127. data/lib/leveldb.rb +1006 -0
  128. metadata +228 -0
@@ -0,0 +1,122 @@
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/filename.h"
6
+
7
+ #include "db/dbformat.h"
8
+ #include "port/port.h"
9
+ #include "util/logging.h"
10
+ #include "util/testharness.h"
11
+
12
+ namespace leveldb {
13
+
14
+ class FileNameTest { };
15
+
16
+ TEST(FileNameTest, Parse) {
17
+ Slice db;
18
+ FileType type;
19
+ uint64_t number;
20
+
21
+ // Successful parses
22
+ static struct {
23
+ const char* fname;
24
+ uint64_t number;
25
+ FileType type;
26
+ } cases[] = {
27
+ { "100.log", 100, kLogFile },
28
+ { "0.log", 0, kLogFile },
29
+ { "0.sst", 0, kTableFile },
30
+ { "CURRENT", 0, kCurrentFile },
31
+ { "LOCK", 0, kDBLockFile },
32
+ { "MANIFEST-2", 2, kDescriptorFile },
33
+ { "MANIFEST-7", 7, kDescriptorFile },
34
+ { "LOG", 0, kInfoLogFile },
35
+ { "LOG.old", 0, kInfoLogFile },
36
+ { "18446744073709551615.log", 18446744073709551615ull, kLogFile },
37
+ };
38
+ for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
39
+ std::string f = cases[i].fname;
40
+ ASSERT_TRUE(ParseFileName(f, &number, &type)) << f;
41
+ ASSERT_EQ(cases[i].type, type) << f;
42
+ ASSERT_EQ(cases[i].number, number) << f;
43
+ }
44
+
45
+ // Errors
46
+ static const char* errors[] = {
47
+ "",
48
+ "foo",
49
+ "foo-dx-100.log",
50
+ ".log",
51
+ "",
52
+ "manifest",
53
+ "CURREN",
54
+ "CURRENTX",
55
+ "MANIFES",
56
+ "MANIFEST",
57
+ "MANIFEST-",
58
+ "XMANIFEST-3",
59
+ "MANIFEST-3x",
60
+ "LOC",
61
+ "LOCKx",
62
+ "LO",
63
+ "LOGx",
64
+ "18446744073709551616.log",
65
+ "184467440737095516150.log",
66
+ "100",
67
+ "100.",
68
+ "100.lop"
69
+ };
70
+ for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {
71
+ std::string f = errors[i];
72
+ ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;
73
+ }
74
+ }
75
+
76
+ TEST(FileNameTest, Construction) {
77
+ uint64_t number;
78
+ FileType type;
79
+ std::string fname;
80
+
81
+ fname = CurrentFileName("foo");
82
+ ASSERT_EQ("foo/", std::string(fname.data(), 4));
83
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
84
+ ASSERT_EQ(0, number);
85
+ ASSERT_EQ(kCurrentFile, type);
86
+
87
+ fname = LockFileName("foo");
88
+ ASSERT_EQ("foo/", std::string(fname.data(), 4));
89
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
90
+ ASSERT_EQ(0, number);
91
+ ASSERT_EQ(kDBLockFile, type);
92
+
93
+ fname = LogFileName("foo", 192);
94
+ ASSERT_EQ("foo/", std::string(fname.data(), 4));
95
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
96
+ ASSERT_EQ(192, number);
97
+ ASSERT_EQ(kLogFile, type);
98
+
99
+ fname = TableFileName("bar", 200);
100
+ ASSERT_EQ("bar/", std::string(fname.data(), 4));
101
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
102
+ ASSERT_EQ(200, number);
103
+ ASSERT_EQ(kTableFile, type);
104
+
105
+ fname = DescriptorFileName("bar", 100);
106
+ ASSERT_EQ("bar/", std::string(fname.data(), 4));
107
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
108
+ ASSERT_EQ(100, number);
109
+ ASSERT_EQ(kDescriptorFile, type);
110
+
111
+ fname = TempFileName("tmp", 999);
112
+ ASSERT_EQ("tmp/", std::string(fname.data(), 4));
113
+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));
114
+ ASSERT_EQ(999, number);
115
+ ASSERT_EQ(kTempFile, type);
116
+ }
117
+
118
+ } // namespace leveldb
119
+
120
+ int main(int argc, char** argv) {
121
+ return leveldb::test::RunAllTests();
122
+ }
@@ -0,0 +1,238 @@
1
+ // Copyright (c) 2012 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 <stdio.h>
6
+ #include "db/dbformat.h"
7
+ #include "db/filename.h"
8
+ #include "db/log_reader.h"
9
+ #include "db/version_edit.h"
10
+ #include "db/write_batch_internal.h"
11
+ #include "leveldb/env.h"
12
+ #include "leveldb/iterator.h"
13
+ #include "leveldb/options.h"
14
+ #include "leveldb/status.h"
15
+ #include "leveldb/table.h"
16
+ #include "leveldb/write_batch.h"
17
+ #include "util/logging.h"
18
+
19
+ namespace leveldb {
20
+
21
+ namespace {
22
+
23
+ bool GuessType(const std::string& fname, FileType* type) {
24
+ size_t pos = fname.rfind('/');
25
+ std::string basename;
26
+ if (pos == std::string::npos) {
27
+ basename = fname;
28
+ } else {
29
+ basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1);
30
+ }
31
+ uint64_t ignored;
32
+ return ParseFileName(basename, &ignored, type);
33
+ }
34
+
35
+ // Notified when log reader encounters corruption.
36
+ class CorruptionReporter : public log::Reader::Reporter {
37
+ public:
38
+ virtual void Corruption(size_t bytes, const Status& status) {
39
+ printf("corruption: %d bytes; %s\n",
40
+ static_cast<int>(bytes),
41
+ status.ToString().c_str());
42
+ }
43
+ };
44
+
45
+ // Print contents of a log file. (*func)() is called on every record.
46
+ bool PrintLogContents(Env* env, const std::string& fname,
47
+ void (*func)(Slice)) {
48
+ SequentialFile* file;
49
+ Status s = env->NewSequentialFile(fname, &file);
50
+ if (!s.ok()) {
51
+ fprintf(stderr, "%s\n", s.ToString().c_str());
52
+ return false;
53
+ }
54
+ CorruptionReporter reporter;
55
+ log::Reader reader(file, &reporter, true, 0);
56
+ Slice record;
57
+ std::string scratch;
58
+ while (reader.ReadRecord(&record, &scratch)) {
59
+ printf("--- offset %llu; ",
60
+ static_cast<unsigned long long>(reader.LastRecordOffset()));
61
+ (*func)(record);
62
+ }
63
+ delete file;
64
+ return true;
65
+ }
66
+
67
+ // Called on every item found in a WriteBatch.
68
+ class WriteBatchItemPrinter : public WriteBatch::Handler {
69
+ public:
70
+ uint64_t offset_;
71
+ uint64_t sequence_;
72
+
73
+ virtual void Put(const Slice& key, const Slice& value) {
74
+ printf(" put '%s' '%s'\n",
75
+ EscapeString(key).c_str(),
76
+ EscapeString(value).c_str());
77
+ }
78
+ virtual void Delete(const Slice& key) {
79
+ printf(" del '%s'\n",
80
+ EscapeString(key).c_str());
81
+ }
82
+ };
83
+
84
+
85
+ // Called on every log record (each one of which is a WriteBatch)
86
+ // found in a kLogFile.
87
+ static void WriteBatchPrinter(Slice record) {
88
+ if (record.size() < 12) {
89
+ printf("log record length %d is too small\n",
90
+ static_cast<int>(record.size()));
91
+ return;
92
+ }
93
+ WriteBatch batch;
94
+ WriteBatchInternal::SetContents(&batch, record);
95
+ printf("sequence %llu\n",
96
+ static_cast<unsigned long long>(WriteBatchInternal::Sequence(&batch)));
97
+ WriteBatchItemPrinter batch_item_printer;
98
+ Status s = batch.Iterate(&batch_item_printer);
99
+ if (!s.ok()) {
100
+ printf(" error: %s\n", s.ToString().c_str());
101
+ }
102
+ }
103
+
104
+ bool DumpLog(Env* env, const std::string& fname) {
105
+ return PrintLogContents(env, fname, WriteBatchPrinter);
106
+ }
107
+
108
+ // Called on every log record (each one of which is a WriteBatch)
109
+ // found in a kDescriptorFile.
110
+ static void VersionEditPrinter(Slice record) {
111
+ VersionEdit edit;
112
+ Status s = edit.DecodeFrom(record);
113
+ if (!s.ok()) {
114
+ printf("%s\n", s.ToString().c_str());
115
+ return;
116
+ }
117
+ printf("%s", edit.DebugString().c_str());
118
+ }
119
+
120
+ bool DumpDescriptor(Env* env, const std::string& fname) {
121
+ return PrintLogContents(env, fname, VersionEditPrinter);
122
+ }
123
+
124
+ bool DumpTable(Env* env, const std::string& fname) {
125
+ uint64_t file_size;
126
+ RandomAccessFile* file = NULL;
127
+ Table* table = NULL;
128
+ Status s = env->GetFileSize(fname, &file_size);
129
+ if (s.ok()) {
130
+ s = env->NewRandomAccessFile(fname, &file);
131
+ }
132
+ if (s.ok()) {
133
+ // We use the default comparator, which may or may not match the
134
+ // comparator used in this database. However this should not cause
135
+ // problems since we only use Table operations that do not require
136
+ // any comparisons. In particular, we do not call Seek or Prev.
137
+ s = Table::Open(Options(), file, file_size, &table);
138
+ }
139
+ if (!s.ok()) {
140
+ fprintf(stderr, "%s\n", s.ToString().c_str());
141
+ delete table;
142
+ delete file;
143
+ return false;
144
+ }
145
+
146
+ ReadOptions ro;
147
+ ro.fill_cache = false;
148
+ Iterator* iter = table->NewIterator(ro);
149
+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
150
+ ParsedInternalKey key;
151
+ if (!ParseInternalKey(iter->key(), &key)) {
152
+ printf("badkey '%s' => '%s'\n",
153
+ EscapeString(iter->key()).c_str(),
154
+ EscapeString(iter->value()).c_str());
155
+ } else {
156
+ char kbuf[20];
157
+ const char* type;
158
+ if (key.type == kTypeDeletion) {
159
+ type = "del";
160
+ } else if (key.type == kTypeValue) {
161
+ type = "val";
162
+ } else {
163
+ snprintf(kbuf, sizeof(kbuf), "%d", static_cast<int>(key.type));
164
+ type = kbuf;
165
+ }
166
+ printf("'%s' @ %8llu : %s => '%s'\n",
167
+ EscapeString(key.user_key).c_str(),
168
+ static_cast<unsigned long long>(key.sequence),
169
+ type,
170
+ EscapeString(iter->value()).c_str());
171
+ }
172
+ }
173
+ s = iter->status();
174
+ if (!s.ok()) {
175
+ printf("iterator error: %s\n", s.ToString().c_str());
176
+ }
177
+
178
+ delete iter;
179
+ delete table;
180
+ delete file;
181
+ return true;
182
+ }
183
+
184
+ bool DumpFile(Env* env, const std::string& fname) {
185
+ FileType ftype;
186
+ if (!GuessType(fname, &ftype)) {
187
+ fprintf(stderr, "%s: unknown file type\n", fname.c_str());
188
+ return false;
189
+ }
190
+ switch (ftype) {
191
+ case kLogFile: return DumpLog(env, fname);
192
+ case kDescriptorFile: return DumpDescriptor(env, fname);
193
+ case kTableFile: return DumpTable(env, fname);
194
+
195
+ default: {
196
+ fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str());
197
+ break;
198
+ }
199
+ }
200
+ return false;
201
+ }
202
+
203
+ bool HandleDumpCommand(Env* env, char** files, int num) {
204
+ bool ok = true;
205
+ for (int i = 0; i < num; i++) {
206
+ ok &= DumpFile(env, files[i]);
207
+ }
208
+ return ok;
209
+ }
210
+
211
+ }
212
+ } // namespace leveldb
213
+
214
+ static void Usage() {
215
+ fprintf(
216
+ stderr,
217
+ "Usage: leveldbutil command...\n"
218
+ " dump files... -- dump contents of specified files\n"
219
+ );
220
+ }
221
+
222
+ int main(int argc, char** argv) {
223
+ leveldb::Env* env = leveldb::Env::Default();
224
+ bool ok = true;
225
+ if (argc < 2) {
226
+ Usage();
227
+ ok = false;
228
+ } else {
229
+ std::string command = argv[1];
230
+ if (command == "dump") {
231
+ ok = leveldb::HandleDumpCommand(env, argv+2, argc-2);
232
+ } else {
233
+ Usage();
234
+ ok = false;
235
+ }
236
+ }
237
+ return (ok ? 0 : 1);
238
+ }
@@ -0,0 +1,35 @@
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
+ // Log format information shared by reader and writer.
6
+ // See ../doc/log_format.txt for more detail.
7
+
8
+ #ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_
9
+ #define STORAGE_LEVELDB_DB_LOG_FORMAT_H_
10
+
11
+ namespace leveldb {
12
+ namespace log {
13
+
14
+ enum RecordType {
15
+ // Zero is reserved for preallocated files
16
+ kZeroType = 0,
17
+
18
+ kFullType = 1,
19
+
20
+ // For fragments
21
+ kFirstType = 2,
22
+ kMiddleType = 3,
23
+ kLastType = 4
24
+ };
25
+ static const int kMaxRecordType = kLastType;
26
+
27
+ static const int kBlockSize = 32768;
28
+
29
+ // Header is checksum (4 bytes), type (1 byte), length (2 bytes).
30
+ static const int kHeaderSize = 4 + 1 + 2;
31
+
32
+ } // namespace log
33
+ } // namespace leveldb
34
+
35
+ #endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_
@@ -0,0 +1,259 @@
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/log_reader.h"
6
+
7
+ #include <stdio.h>
8
+ #include "leveldb/env.h"
9
+ #include "util/coding.h"
10
+ #include "util/crc32c.h"
11
+
12
+ namespace leveldb {
13
+ namespace log {
14
+
15
+ Reader::Reporter::~Reporter() {
16
+ }
17
+
18
+ Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,
19
+ uint64_t initial_offset)
20
+ : file_(file),
21
+ reporter_(reporter),
22
+ checksum_(checksum),
23
+ backing_store_(new char[kBlockSize]),
24
+ buffer_(),
25
+ eof_(false),
26
+ last_record_offset_(0),
27
+ end_of_buffer_offset_(0),
28
+ initial_offset_(initial_offset) {
29
+ }
30
+
31
+ Reader::~Reader() {
32
+ delete[] backing_store_;
33
+ }
34
+
35
+ bool Reader::SkipToInitialBlock() {
36
+ size_t offset_in_block = initial_offset_ % kBlockSize;
37
+ uint64_t block_start_location = initial_offset_ - offset_in_block;
38
+
39
+ // Don't search a block if we'd be in the trailer
40
+ if (offset_in_block > kBlockSize - 6) {
41
+ offset_in_block = 0;
42
+ block_start_location += kBlockSize;
43
+ }
44
+
45
+ end_of_buffer_offset_ = block_start_location;
46
+
47
+ // Skip to start of first block that can contain the initial record
48
+ if (block_start_location > 0) {
49
+ Status skip_status = file_->Skip(block_start_location);
50
+ if (!skip_status.ok()) {
51
+ ReportDrop(block_start_location, skip_status);
52
+ return false;
53
+ }
54
+ }
55
+
56
+ return true;
57
+ }
58
+
59
+ bool Reader::ReadRecord(Slice* record, std::string* scratch) {
60
+ if (last_record_offset_ < initial_offset_) {
61
+ if (!SkipToInitialBlock()) {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ scratch->clear();
67
+ record->clear();
68
+ bool in_fragmented_record = false;
69
+ // Record offset of the logical record that we're reading
70
+ // 0 is a dummy value to make compilers happy
71
+ uint64_t prospective_record_offset = 0;
72
+
73
+ Slice fragment;
74
+ while (true) {
75
+ uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();
76
+ const unsigned int record_type = ReadPhysicalRecord(&fragment);
77
+ switch (record_type) {
78
+ case kFullType:
79
+ if (in_fragmented_record) {
80
+ // Handle bug in earlier versions of log::Writer where
81
+ // it could emit an empty kFirstType record at the tail end
82
+ // of a block followed by a kFullType or kFirstType record
83
+ // at the beginning of the next block.
84
+ if (scratch->empty()) {
85
+ in_fragmented_record = false;
86
+ } else {
87
+ ReportCorruption(scratch->size(), "partial record without end(1)");
88
+ }
89
+ }
90
+ prospective_record_offset = physical_record_offset;
91
+ scratch->clear();
92
+ *record = fragment;
93
+ last_record_offset_ = prospective_record_offset;
94
+ return true;
95
+
96
+ case kFirstType:
97
+ if (in_fragmented_record) {
98
+ // Handle bug in earlier versions of log::Writer where
99
+ // it could emit an empty kFirstType record at the tail end
100
+ // of a block followed by a kFullType or kFirstType record
101
+ // at the beginning of the next block.
102
+ if (scratch->empty()) {
103
+ in_fragmented_record = false;
104
+ } else {
105
+ ReportCorruption(scratch->size(), "partial record without end(2)");
106
+ }
107
+ }
108
+ prospective_record_offset = physical_record_offset;
109
+ scratch->assign(fragment.data(), fragment.size());
110
+ in_fragmented_record = true;
111
+ break;
112
+
113
+ case kMiddleType:
114
+ if (!in_fragmented_record) {
115
+ ReportCorruption(fragment.size(),
116
+ "missing start of fragmented record(1)");
117
+ } else {
118
+ scratch->append(fragment.data(), fragment.size());
119
+ }
120
+ break;
121
+
122
+ case kLastType:
123
+ if (!in_fragmented_record) {
124
+ ReportCorruption(fragment.size(),
125
+ "missing start of fragmented record(2)");
126
+ } else {
127
+ scratch->append(fragment.data(), fragment.size());
128
+ *record = Slice(*scratch);
129
+ last_record_offset_ = prospective_record_offset;
130
+ return true;
131
+ }
132
+ break;
133
+
134
+ case kEof:
135
+ if (in_fragmented_record) {
136
+ ReportCorruption(scratch->size(), "partial record without end(3)");
137
+ scratch->clear();
138
+ }
139
+ return false;
140
+
141
+ case kBadRecord:
142
+ if (in_fragmented_record) {
143
+ ReportCorruption(scratch->size(), "error in middle of record");
144
+ in_fragmented_record = false;
145
+ scratch->clear();
146
+ }
147
+ break;
148
+
149
+ default: {
150
+ char buf[40];
151
+ snprintf(buf, sizeof(buf), "unknown record type %u", record_type);
152
+ ReportCorruption(
153
+ (fragment.size() + (in_fragmented_record ? scratch->size() : 0)),
154
+ buf);
155
+ in_fragmented_record = false;
156
+ scratch->clear();
157
+ break;
158
+ }
159
+ }
160
+ }
161
+ return false;
162
+ }
163
+
164
+ uint64_t Reader::LastRecordOffset() {
165
+ return last_record_offset_;
166
+ }
167
+
168
+ void Reader::ReportCorruption(size_t bytes, const char* reason) {
169
+ ReportDrop(bytes, Status::Corruption(reason));
170
+ }
171
+
172
+ void Reader::ReportDrop(size_t bytes, const Status& reason) {
173
+ if (reporter_ != NULL &&
174
+ end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {
175
+ reporter_->Corruption(bytes, reason);
176
+ }
177
+ }
178
+
179
+ unsigned int Reader::ReadPhysicalRecord(Slice* result) {
180
+ while (true) {
181
+ if (buffer_.size() < kHeaderSize) {
182
+ if (!eof_) {
183
+ // Last read was a full read, so this is a trailer to skip
184
+ buffer_.clear();
185
+ Status status = file_->Read(kBlockSize, &buffer_, backing_store_);
186
+ end_of_buffer_offset_ += buffer_.size();
187
+ if (!status.ok()) {
188
+ buffer_.clear();
189
+ ReportDrop(kBlockSize, status);
190
+ eof_ = true;
191
+ return kEof;
192
+ } else if (buffer_.size() < kBlockSize) {
193
+ eof_ = true;
194
+ }
195
+ continue;
196
+ } else if (buffer_.size() == 0) {
197
+ // End of file
198
+ return kEof;
199
+ } else {
200
+ size_t drop_size = buffer_.size();
201
+ buffer_.clear();
202
+ ReportCorruption(drop_size, "truncated record at end of file");
203
+ return kEof;
204
+ }
205
+ }
206
+
207
+ // Parse the header
208
+ const char* header = buffer_.data();
209
+ const uint32_t a = static_cast<uint32_t>(header[4]) & 0xff;
210
+ const uint32_t b = static_cast<uint32_t>(header[5]) & 0xff;
211
+ const unsigned int type = header[6];
212
+ const uint32_t length = a | (b << 8);
213
+ if (kHeaderSize + length > buffer_.size()) {
214
+ size_t drop_size = buffer_.size();
215
+ buffer_.clear();
216
+ ReportCorruption(drop_size, "bad record length");
217
+ return kBadRecord;
218
+ }
219
+
220
+ if (type == kZeroType && length == 0) {
221
+ // Skip zero length record without reporting any drops since
222
+ // such records are produced by the mmap based writing code in
223
+ // env_posix.cc that preallocates file regions.
224
+ buffer_.clear();
225
+ return kBadRecord;
226
+ }
227
+
228
+ // Check crc
229
+ if (checksum_) {
230
+ uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header));
231
+ uint32_t actual_crc = crc32c::Value(header + 6, 1 + length);
232
+ if (actual_crc != expected_crc) {
233
+ // Drop the rest of the buffer since "length" itself may have
234
+ // been corrupted and if we trust it, we could find some
235
+ // fragment of a real log record that just happens to look
236
+ // like a valid log record.
237
+ size_t drop_size = buffer_.size();
238
+ buffer_.clear();
239
+ ReportCorruption(drop_size, "checksum mismatch");
240
+ return kBadRecord;
241
+ }
242
+ }
243
+
244
+ buffer_.remove_prefix(kHeaderSize + length);
245
+
246
+ // Skip physical record that started before initial_offset_
247
+ if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length <
248
+ initial_offset_) {
249
+ result->clear();
250
+ return kBadRecord;
251
+ }
252
+
253
+ *result = Slice(header + kHeaderSize, length);
254
+ return type;
255
+ }
256
+ }
257
+
258
+ } // namespace log
259
+ } // namespace leveldb