leveldb-ruby 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.
Files changed (113) hide show
  1. data/README +17 -0
  2. data/ext/leveldb/extconf.rb +10 -0
  3. data/ext/leveldb/leveldb.cc +181 -0
  4. data/leveldb/Makefile +172 -0
  5. data/leveldb/db/builder.cc +90 -0
  6. data/leveldb/db/builder.h +36 -0
  7. data/leveldb/db/corruption_test.cc +354 -0
  8. data/leveldb/db/db_bench.cc +677 -0
  9. data/leveldb/db/db_impl.cc +1236 -0
  10. data/leveldb/db/db_impl.h +180 -0
  11. data/leveldb/db/db_iter.cc +298 -0
  12. data/leveldb/db/db_iter.h +26 -0
  13. data/leveldb/db/db_test.cc +1192 -0
  14. data/leveldb/db/dbformat.cc +87 -0
  15. data/leveldb/db/dbformat.h +165 -0
  16. data/leveldb/db/dbformat_test.cc +112 -0
  17. data/leveldb/db/filename.cc +135 -0
  18. data/leveldb/db/filename.h +80 -0
  19. data/leveldb/db/filename_test.cc +122 -0
  20. data/leveldb/db/log_format.h +35 -0
  21. data/leveldb/db/log_reader.cc +254 -0
  22. data/leveldb/db/log_reader.h +108 -0
  23. data/leveldb/db/log_test.cc +500 -0
  24. data/leveldb/db/log_writer.cc +103 -0
  25. data/leveldb/db/log_writer.h +48 -0
  26. data/leveldb/db/memtable.cc +108 -0
  27. data/leveldb/db/memtable.h +85 -0
  28. data/leveldb/db/repair.cc +384 -0
  29. data/leveldb/db/skiplist.h +378 -0
  30. data/leveldb/db/skiplist_test.cc +378 -0
  31. data/leveldb/db/snapshot.h +66 -0
  32. data/leveldb/db/table_cache.cc +95 -0
  33. data/leveldb/db/table_cache.h +50 -0
  34. data/leveldb/db/version_edit.cc +268 -0
  35. data/leveldb/db/version_edit.h +106 -0
  36. data/leveldb/db/version_edit_test.cc +46 -0
  37. data/leveldb/db/version_set.cc +1060 -0
  38. data/leveldb/db/version_set.h +306 -0
  39. data/leveldb/db/write_batch.cc +138 -0
  40. data/leveldb/db/write_batch_internal.h +45 -0
  41. data/leveldb/db/write_batch_test.cc +89 -0
  42. data/leveldb/include/leveldb/cache.h +99 -0
  43. data/leveldb/include/leveldb/comparator.h +63 -0
  44. data/leveldb/include/leveldb/db.h +148 -0
  45. data/leveldb/include/leveldb/env.h +302 -0
  46. data/leveldb/include/leveldb/iterator.h +100 -0
  47. data/leveldb/include/leveldb/options.h +198 -0
  48. data/leveldb/include/leveldb/slice.h +109 -0
  49. data/leveldb/include/leveldb/status.h +100 -0
  50. data/leveldb/include/leveldb/table.h +70 -0
  51. data/leveldb/include/leveldb/table_builder.h +91 -0
  52. data/leveldb/include/leveldb/write_batch.h +64 -0
  53. data/leveldb/port/port.h +23 -0
  54. data/leveldb/port/port_android.cc +64 -0
  55. data/leveldb/port/port_android.h +150 -0
  56. data/leveldb/port/port_chromium.cc +80 -0
  57. data/leveldb/port/port_chromium.h +97 -0
  58. data/leveldb/port/port_example.h +115 -0
  59. data/leveldb/port/port_osx.cc +50 -0
  60. data/leveldb/port/port_osx.h +125 -0
  61. data/leveldb/port/port_posix.cc +50 -0
  62. data/leveldb/port/port_posix.h +94 -0
  63. data/leveldb/port/sha1_portable.cc +298 -0
  64. data/leveldb/port/sha1_portable.h +25 -0
  65. data/leveldb/port/sha1_test.cc +39 -0
  66. data/leveldb/port/win/stdint.h +24 -0
  67. data/leveldb/table/block.cc +263 -0
  68. data/leveldb/table/block.h +43 -0
  69. data/leveldb/table/block_builder.cc +109 -0
  70. data/leveldb/table/block_builder.h +57 -0
  71. data/leveldb/table/format.cc +131 -0
  72. data/leveldb/table/format.h +103 -0
  73. data/leveldb/table/iterator.cc +67 -0
  74. data/leveldb/table/iterator_wrapper.h +63 -0
  75. data/leveldb/table/merger.cc +197 -0
  76. data/leveldb/table/merger.h +26 -0
  77. data/leveldb/table/table.cc +175 -0
  78. data/leveldb/table/table_builder.cc +227 -0
  79. data/leveldb/table/table_test.cc +845 -0
  80. data/leveldb/table/two_level_iterator.cc +182 -0
  81. data/leveldb/table/two_level_iterator.h +34 -0
  82. data/leveldb/util/arena.cc +68 -0
  83. data/leveldb/util/arena.h +68 -0
  84. data/leveldb/util/arena_test.cc +68 -0
  85. data/leveldb/util/cache.cc +255 -0
  86. data/leveldb/util/cache_test.cc +169 -0
  87. data/leveldb/util/coding.cc +194 -0
  88. data/leveldb/util/coding.h +104 -0
  89. data/leveldb/util/coding_test.cc +173 -0
  90. data/leveldb/util/comparator.cc +72 -0
  91. data/leveldb/util/crc32c.cc +332 -0
  92. data/leveldb/util/crc32c.h +45 -0
  93. data/leveldb/util/crc32c_test.cc +72 -0
  94. data/leveldb/util/env.cc +77 -0
  95. data/leveldb/util/env_chromium.cc +612 -0
  96. data/leveldb/util/env_posix.cc +606 -0
  97. data/leveldb/util/env_test.cc +102 -0
  98. data/leveldb/util/hash.cc +45 -0
  99. data/leveldb/util/hash.h +19 -0
  100. data/leveldb/util/histogram.cc +128 -0
  101. data/leveldb/util/histogram.h +41 -0
  102. data/leveldb/util/logging.cc +81 -0
  103. data/leveldb/util/logging.h +47 -0
  104. data/leveldb/util/mutexlock.h +39 -0
  105. data/leveldb/util/options.cc +28 -0
  106. data/leveldb/util/random.h +59 -0
  107. data/leveldb/util/status.cc +75 -0
  108. data/leveldb/util/testharness.cc +65 -0
  109. data/leveldb/util/testharness.h +129 -0
  110. data/leveldb/util/testutil.cc +51 -0
  111. data/leveldb/util/testutil.h +53 -0
  112. data/lib/leveldb.rb +36 -0
  113. metadata +183 -0
@@ -0,0 +1,108 @@
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_DB_LOG_READER_H_
6
+ #define STORAGE_LEVELDB_DB_LOG_READER_H_
7
+
8
+ #include <stdint.h>
9
+
10
+ #include "db/log_format.h"
11
+ #include "leveldb/slice.h"
12
+ #include "leveldb/status.h"
13
+
14
+ namespace leveldb {
15
+
16
+ class SequentialFile;
17
+
18
+ namespace log {
19
+
20
+ class Reader {
21
+ public:
22
+ // Interface for reporting errors.
23
+ class Reporter {
24
+ public:
25
+ virtual ~Reporter();
26
+
27
+ // Some corruption was detected. "size" is the approximate number
28
+ // of bytes dropped due to the corruption.
29
+ virtual void Corruption(size_t bytes, const Status& status) = 0;
30
+ };
31
+
32
+ // Create a reader that will return log records from "*file".
33
+ // "*file" must remain live while this Reader is in use.
34
+ //
35
+ // If "reporter" is non-NULL, it is notified whenever some data is
36
+ // dropped due to a detected corruption. "*reporter" must remain
37
+ // live while this Reader is in use.
38
+ //
39
+ // If "checksum" is true, verify checksums if available.
40
+ //
41
+ // The Reader will start reading at the first record located at physical
42
+ // position >= initial_offset within the file.
43
+ Reader(SequentialFile* file, Reporter* reporter, bool checksum,
44
+ uint64_t initial_offset);
45
+
46
+ ~Reader();
47
+
48
+ // Read the next record into *record. Returns true if read
49
+ // successfully, false if we hit end of the input. May use
50
+ // "*scratch" as temporary storage. The contents filled in *record
51
+ // will only be valid until the next mutating operation on this
52
+ // reader or the next mutation to *scratch.
53
+ bool ReadRecord(Slice* record, std::string* scratch);
54
+
55
+ // Returns the physical offset of the last record returned by ReadRecord.
56
+ //
57
+ // Undefined before the first call to ReadRecord.
58
+ uint64_t LastRecordOffset();
59
+
60
+ private:
61
+ SequentialFile* const file_;
62
+ Reporter* const reporter_;
63
+ bool const checksum_;
64
+ char* const backing_store_;
65
+ Slice buffer_;
66
+ bool eof_; // Last Read() indicated EOF by returning < kBlockSize
67
+
68
+ // Offset of the last record returned by ReadRecord.
69
+ uint64_t last_record_offset_;
70
+ // Offset of the first location past the end of buffer_.
71
+ uint64_t end_of_buffer_offset_;
72
+
73
+ // Offset at which to start looking for the first record to return
74
+ uint64_t const initial_offset_;
75
+
76
+ // Extend record types with the following special values
77
+ enum {
78
+ kEof = kMaxRecordType + 1,
79
+ // Returned whenever we find an invalid physical record.
80
+ // Currently there are three situations in which this happens:
81
+ // * The record has an invalid CRC (ReadPhysicalRecord reports a drop)
82
+ // * The record is a 0-length record (No drop is reported)
83
+ // * The record is below constructor's initial_offset (No drop is reported)
84
+ kBadRecord = kMaxRecordType + 2
85
+ };
86
+
87
+ // Skips all blocks that are completely before "initial_offset_".
88
+ //
89
+ // Returns true on success. Handles reporting.
90
+ bool SkipToInitialBlock();
91
+
92
+ // Return type, or one of the preceding special values
93
+ unsigned int ReadPhysicalRecord(Slice* result);
94
+
95
+ // Reports dropped bytes to the reporter.
96
+ // buffer_ must be updated to remove the dropped bytes prior to invocation.
97
+ void ReportCorruption(size_t bytes, const char* reason);
98
+ void ReportDrop(size_t bytes, const Status& reason);
99
+
100
+ // No copying allowed
101
+ Reader(const Reader&);
102
+ void operator=(const Reader&);
103
+ };
104
+
105
+ }
106
+ }
107
+
108
+ #endif // STORAGE_LEVELDB_DB_LOG_READER_H_
@@ -0,0 +1,500 @@
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
+ #include "db/log_writer.h"
7
+ #include "leveldb/env.h"
8
+ #include "util/coding.h"
9
+ #include "util/crc32c.h"
10
+ #include "util/random.h"
11
+ #include "util/testharness.h"
12
+
13
+ namespace leveldb {
14
+ namespace log {
15
+
16
+ // Construct a string of the specified length made out of the supplied
17
+ // partial string.
18
+ static std::string BigString(const std::string& partial_string, size_t n) {
19
+ std::string result;
20
+ while (result.size() < n) {
21
+ result.append(partial_string);
22
+ }
23
+ result.resize(n);
24
+ return result;
25
+ }
26
+
27
+ // Construct a string from a number
28
+ static std::string NumberString(int n) {
29
+ char buf[50];
30
+ snprintf(buf, sizeof(buf), "%d.", n);
31
+ return std::string(buf);
32
+ }
33
+
34
+ // Return a skewed potentially long string
35
+ static std::string RandomSkewedString(int i, Random* rnd) {
36
+ return BigString(NumberString(i), rnd->Skewed(17));
37
+ }
38
+
39
+ class LogTest {
40
+ private:
41
+ class StringDest : public WritableFile {
42
+ public:
43
+ std::string contents_;
44
+
45
+ virtual Status Close() { return Status::OK(); }
46
+ virtual Status Flush() { return Status::OK(); }
47
+ virtual Status Sync() { return Status::OK(); }
48
+ virtual Status Append(const Slice& slice) {
49
+ contents_.append(slice.data(), slice.size());
50
+ return Status::OK();
51
+ }
52
+ };
53
+
54
+ class StringSource : public SequentialFile {
55
+ public:
56
+ Slice contents_;
57
+ bool force_error_;
58
+ bool returned_partial_;
59
+ StringSource() : force_error_(false), returned_partial_(false) { }
60
+
61
+ virtual Status Read(size_t n, Slice* result, char* scratch) {
62
+ ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";
63
+
64
+ if (force_error_) {
65
+ force_error_ = false;
66
+ returned_partial_ = true;
67
+ return Status::Corruption("read error");
68
+ }
69
+
70
+ if (contents_.size() < n) {
71
+ n = contents_.size();
72
+ returned_partial_ = true;
73
+ }
74
+ *result = Slice(contents_.data(), n);
75
+ contents_.remove_prefix(n);
76
+ return Status::OK();
77
+ }
78
+
79
+ virtual Status Skip(uint64_t n) {
80
+ if (n > contents_.size()) {
81
+ contents_.clear();
82
+ return Status::NotFound("in-memory file skipepd past end");
83
+ }
84
+
85
+ contents_.remove_prefix(n);
86
+
87
+ return Status::OK();
88
+ }
89
+ };
90
+
91
+ class ReportCollector : public Reader::Reporter {
92
+ public:
93
+ size_t dropped_bytes_;
94
+ std::string message_;
95
+
96
+ ReportCollector() : dropped_bytes_(0) { }
97
+ virtual void Corruption(size_t bytes, const Status& status) {
98
+ dropped_bytes_ += bytes;
99
+ message_.append(status.ToString());
100
+ }
101
+ };
102
+
103
+ StringDest dest_;
104
+ StringSource source_;
105
+ ReportCollector report_;
106
+ bool reading_;
107
+ Writer writer_;
108
+ Reader reader_;
109
+
110
+ // Record metadata for testing initial offset functionality
111
+ static size_t initial_offset_record_sizes_[];
112
+ static uint64_t initial_offset_last_record_offsets_[];
113
+
114
+ public:
115
+ LogTest() : reading_(false),
116
+ writer_(&dest_),
117
+ reader_(&source_, &report_, true/*checksum*/,
118
+ 0/*initial_offset*/) {
119
+ }
120
+
121
+ void Write(const std::string& msg) {
122
+ ASSERT_TRUE(!reading_) << "Write() after starting to read";
123
+ writer_.AddRecord(Slice(msg));
124
+ }
125
+
126
+ size_t WrittenBytes() const {
127
+ return dest_.contents_.size();
128
+ }
129
+
130
+ std::string Read() {
131
+ if (!reading_) {
132
+ reading_ = true;
133
+ source_.contents_ = Slice(dest_.contents_);
134
+ }
135
+ std::string scratch;
136
+ Slice record;
137
+ if (reader_.ReadRecord(&record, &scratch)) {
138
+ return record.ToString();
139
+ } else {
140
+ return "EOF";
141
+ }
142
+ }
143
+
144
+ void IncrementByte(int offset, int delta) {
145
+ dest_.contents_[offset] += delta;
146
+ }
147
+
148
+ void SetByte(int offset, char new_byte) {
149
+ dest_.contents_[offset] = new_byte;
150
+ }
151
+
152
+ void ShrinkSize(int bytes) {
153
+ dest_.contents_.resize(dest_.contents_.size() - bytes);
154
+ }
155
+
156
+ void FixChecksum(int header_offset, int len) {
157
+ // Compute crc of type/len/data
158
+ uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len);
159
+ crc = crc32c::Mask(crc);
160
+ EncodeFixed32(&dest_.contents_[header_offset], crc);
161
+ }
162
+
163
+ void ForceError() {
164
+ source_.force_error_ = true;
165
+ }
166
+
167
+ size_t DroppedBytes() const {
168
+ return report_.dropped_bytes_;
169
+ }
170
+
171
+ std::string ReportMessage() const {
172
+ return report_.message_;
173
+ }
174
+
175
+ // Returns OK iff recorded error message contains "msg"
176
+ std::string MatchError(const std::string& msg) const {
177
+ if (report_.message_.find(msg) == std::string::npos) {
178
+ return report_.message_;
179
+ } else {
180
+ return "OK";
181
+ }
182
+ }
183
+
184
+ void WriteInitialOffsetLog() {
185
+ for (int i = 0; i < 4; i++) {
186
+ std::string record(initial_offset_record_sizes_[i],
187
+ static_cast<char>('a' + i));
188
+ Write(record);
189
+ }
190
+ }
191
+
192
+ void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) {
193
+ WriteInitialOffsetLog();
194
+ reading_ = true;
195
+ source_.contents_ = Slice(dest_.contents_);
196
+ Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
197
+ WrittenBytes() + offset_past_end);
198
+ Slice record;
199
+ std::string scratch;
200
+ ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch));
201
+ delete offset_reader;
202
+ }
203
+
204
+ void CheckInitialOffsetRecord(uint64_t initial_offset,
205
+ int expected_record_offset) {
206
+ WriteInitialOffsetLog();
207
+ reading_ = true;
208
+ source_.contents_ = Slice(dest_.contents_);
209
+ Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,
210
+ initial_offset);
211
+ Slice record;
212
+ std::string scratch;
213
+ ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));
214
+ ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],
215
+ record.size());
216
+ ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],
217
+ offset_reader->LastRecordOffset());
218
+ ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);
219
+ delete offset_reader;
220
+ }
221
+
222
+ };
223
+
224
+ size_t LogTest::initial_offset_record_sizes_[] =
225
+ {10000, // Two sizable records in first block
226
+ 10000,
227
+ 2 * log::kBlockSize - 1000, // Span three blocks
228
+ 1};
229
+
230
+ uint64_t LogTest::initial_offset_last_record_offsets_[] =
231
+ {0,
232
+ kHeaderSize + 10000,
233
+ 2 * (kHeaderSize + 10000),
234
+ 2 * (kHeaderSize + 10000) +
235
+ (2 * log::kBlockSize - 1000) + 3 * kHeaderSize};
236
+
237
+
238
+ TEST(LogTest, Empty) {
239
+ ASSERT_EQ("EOF", Read());
240
+ }
241
+
242
+ TEST(LogTest, ReadWrite) {
243
+ Write("foo");
244
+ Write("bar");
245
+ Write("");
246
+ Write("xxxx");
247
+ ASSERT_EQ("foo", Read());
248
+ ASSERT_EQ("bar", Read());
249
+ ASSERT_EQ("", Read());
250
+ ASSERT_EQ("xxxx", Read());
251
+ ASSERT_EQ("EOF", Read());
252
+ ASSERT_EQ("EOF", Read()); // Make sure reads at eof work
253
+ }
254
+
255
+ TEST(LogTest, ManyBlocks) {
256
+ for (int i = 0; i < 100000; i++) {
257
+ Write(NumberString(i));
258
+ }
259
+ for (int i = 0; i < 100000; i++) {
260
+ ASSERT_EQ(NumberString(i), Read());
261
+ }
262
+ ASSERT_EQ("EOF", Read());
263
+ }
264
+
265
+ TEST(LogTest, Fragmentation) {
266
+ Write("small");
267
+ Write(BigString("medium", 50000));
268
+ Write(BigString("large", 100000));
269
+ ASSERT_EQ("small", Read());
270
+ ASSERT_EQ(BigString("medium", 50000), Read());
271
+ ASSERT_EQ(BigString("large", 100000), Read());
272
+ ASSERT_EQ("EOF", Read());
273
+ }
274
+
275
+ TEST(LogTest, MarginalTrailer) {
276
+ // Make a trailer that is exactly the same length as an empty record.
277
+ const int n = kBlockSize - 2*kHeaderSize;
278
+ Write(BigString("foo", n));
279
+ ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());
280
+ Write("");
281
+ Write("bar");
282
+ ASSERT_EQ(BigString("foo", n), Read());
283
+ ASSERT_EQ("", Read());
284
+ ASSERT_EQ("bar", Read());
285
+ ASSERT_EQ("EOF", Read());
286
+ }
287
+
288
+ TEST(LogTest, MarginalTrailer2) {
289
+ // Make a trailer that is exactly the same length as an empty record.
290
+ const int n = kBlockSize - 2*kHeaderSize;
291
+ Write(BigString("foo", n));
292
+ ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());
293
+ Write("bar");
294
+ ASSERT_EQ(BigString("foo", n), Read());
295
+ ASSERT_EQ("bar", Read());
296
+ ASSERT_EQ("EOF", Read());
297
+ ASSERT_EQ(0, DroppedBytes());
298
+ ASSERT_EQ("", ReportMessage());
299
+ }
300
+
301
+ TEST(LogTest, ShortTrailer) {
302
+ const int n = kBlockSize - 2*kHeaderSize + 4;
303
+ Write(BigString("foo", n));
304
+ ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
305
+ Write("");
306
+ Write("bar");
307
+ ASSERT_EQ(BigString("foo", n), Read());
308
+ ASSERT_EQ("", Read());
309
+ ASSERT_EQ("bar", Read());
310
+ ASSERT_EQ("EOF", Read());
311
+ }
312
+
313
+ TEST(LogTest, AlignedEof) {
314
+ const int n = kBlockSize - 2*kHeaderSize + 4;
315
+ Write(BigString("foo", n));
316
+ ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());
317
+ ASSERT_EQ(BigString("foo", n), Read());
318
+ ASSERT_EQ("EOF", Read());
319
+ }
320
+
321
+ TEST(LogTest, RandomRead) {
322
+ const int N = 500;
323
+ Random write_rnd(301);
324
+ for (int i = 0; i < N; i++) {
325
+ Write(RandomSkewedString(i, &write_rnd));
326
+ }
327
+ Random read_rnd(301);
328
+ for (int i = 0; i < N; i++) {
329
+ ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read());
330
+ }
331
+ ASSERT_EQ("EOF", Read());
332
+ }
333
+
334
+ // Tests of all the error paths in log_reader.cc follow:
335
+
336
+ TEST(LogTest, ReadError) {
337
+ Write("foo");
338
+ ForceError();
339
+ ASSERT_EQ("EOF", Read());
340
+ ASSERT_EQ(kBlockSize, DroppedBytes());
341
+ ASSERT_EQ("OK", MatchError("read error"));
342
+ }
343
+
344
+ TEST(LogTest, BadRecordType) {
345
+ Write("foo");
346
+ // Type is stored in header[6]
347
+ IncrementByte(6, 100);
348
+ FixChecksum(0, 3);
349
+ ASSERT_EQ("EOF", Read());
350
+ ASSERT_EQ(3, DroppedBytes());
351
+ ASSERT_EQ("OK", MatchError("unknown record type"));
352
+ }
353
+
354
+ TEST(LogTest, TruncatedTrailingRecord) {
355
+ Write("foo");
356
+ ShrinkSize(4); // Drop all payload as well as a header byte
357
+ ASSERT_EQ("EOF", Read());
358
+ ASSERT_EQ(kHeaderSize - 1, DroppedBytes());
359
+ ASSERT_EQ("OK", MatchError("truncated record at end of file"));
360
+ }
361
+
362
+ TEST(LogTest, BadLength) {
363
+ Write("foo");
364
+ ShrinkSize(1);
365
+ ASSERT_EQ("EOF", Read());
366
+ ASSERT_EQ(kHeaderSize + 2, DroppedBytes());
367
+ ASSERT_EQ("OK", MatchError("bad record length"));
368
+ }
369
+
370
+ TEST(LogTest, ChecksumMismatch) {
371
+ Write("foo");
372
+ IncrementByte(0, 10);
373
+ ASSERT_EQ("EOF", Read());
374
+ ASSERT_EQ(10, DroppedBytes());
375
+ ASSERT_EQ("OK", MatchError("checksum mismatch"));
376
+ }
377
+
378
+ TEST(LogTest, UnexpectedMiddleType) {
379
+ Write("foo");
380
+ SetByte(6, kMiddleType);
381
+ FixChecksum(0, 3);
382
+ ASSERT_EQ("EOF", Read());
383
+ ASSERT_EQ(3, DroppedBytes());
384
+ ASSERT_EQ("OK", MatchError("missing start"));
385
+ }
386
+
387
+ TEST(LogTest, UnexpectedLastType) {
388
+ Write("foo");
389
+ SetByte(6, kLastType);
390
+ FixChecksum(0, 3);
391
+ ASSERT_EQ("EOF", Read());
392
+ ASSERT_EQ(3, DroppedBytes());
393
+ ASSERT_EQ("OK", MatchError("missing start"));
394
+ }
395
+
396
+ TEST(LogTest, UnexpectedFullType) {
397
+ Write("foo");
398
+ Write("bar");
399
+ SetByte(6, kFirstType);
400
+ FixChecksum(0, 3);
401
+ ASSERT_EQ("bar", Read());
402
+ ASSERT_EQ("EOF", Read());
403
+ ASSERT_EQ(3, DroppedBytes());
404
+ ASSERT_EQ("OK", MatchError("partial record without end"));
405
+ }
406
+
407
+ TEST(LogTest, UnexpectedFirstType) {
408
+ Write("foo");
409
+ Write(BigString("bar", 100000));
410
+ SetByte(6, kFirstType);
411
+ FixChecksum(0, 3);
412
+ ASSERT_EQ(BigString("bar", 100000), Read());
413
+ ASSERT_EQ("EOF", Read());
414
+ ASSERT_EQ(3, DroppedBytes());
415
+ ASSERT_EQ("OK", MatchError("partial record without end"));
416
+ }
417
+
418
+ TEST(LogTest, ErrorJoinsRecords) {
419
+ // Consider two fragmented records:
420
+ // first(R1) last(R1) first(R2) last(R2)
421
+ // where the middle two fragments disappear. We do not want
422
+ // first(R1),last(R2) to get joined and returned as a valid record.
423
+
424
+ // Write records that span two blocks
425
+ Write(BigString("foo", kBlockSize));
426
+ Write(BigString("bar", kBlockSize));
427
+ Write("correct");
428
+
429
+ // Wipe the middle block
430
+ for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) {
431
+ SetByte(offset, 'x');
432
+ }
433
+
434
+ ASSERT_EQ("correct", Read());
435
+ ASSERT_EQ("EOF", Read());
436
+ const int dropped = DroppedBytes();
437
+ ASSERT_LE(dropped, 2*kBlockSize + 100);
438
+ ASSERT_GE(dropped, 2*kBlockSize);
439
+ }
440
+
441
+ TEST(LogTest, ReadStart) {
442
+ CheckInitialOffsetRecord(0, 0);
443
+ }
444
+
445
+ TEST(LogTest, ReadSecondOneOff) {
446
+ CheckInitialOffsetRecord(1, 1);
447
+ }
448
+
449
+ TEST(LogTest, ReadSecondTenThousand) {
450
+ CheckInitialOffsetRecord(10000, 1);
451
+ }
452
+
453
+ TEST(LogTest, ReadSecondStart) {
454
+ CheckInitialOffsetRecord(10007, 1);
455
+ }
456
+
457
+ TEST(LogTest, ReadThirdOneOff) {
458
+ CheckInitialOffsetRecord(10008, 2);
459
+ }
460
+
461
+ TEST(LogTest, ReadThirdStart) {
462
+ CheckInitialOffsetRecord(20014, 2);
463
+ }
464
+
465
+ TEST(LogTest, ReadFourthOneOff) {
466
+ CheckInitialOffsetRecord(20015, 3);
467
+ }
468
+
469
+ TEST(LogTest, ReadFourthFirstBlockTrailer) {
470
+ CheckInitialOffsetRecord(log::kBlockSize - 4, 3);
471
+ }
472
+
473
+ TEST(LogTest, ReadFourthMiddleBlock) {
474
+ CheckInitialOffsetRecord(log::kBlockSize + 1, 3);
475
+ }
476
+
477
+ TEST(LogTest, ReadFourthLastBlock) {
478
+ CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3);
479
+ }
480
+
481
+ TEST(LogTest, ReadFourthStart) {
482
+ CheckInitialOffsetRecord(
483
+ 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,
484
+ 3);
485
+ }
486
+
487
+ TEST(LogTest, ReadEnd) {
488
+ CheckOffsetPastEndReturnsNoRecords(0);
489
+ }
490
+
491
+ TEST(LogTest, ReadPastEnd) {
492
+ CheckOffsetPastEndReturnsNoRecords(5);
493
+ }
494
+
495
+ }
496
+ }
497
+
498
+ int main(int argc, char** argv) {
499
+ return leveldb::test::RunAllTests();
500
+ }