filiptepper-leveldb-ruby 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/LICENSE +24 -0
  2. data/README +72 -0
  3. data/ext/leveldb/extconf.rb +14 -0
  4. data/ext/leveldb/leveldb.cc +530 -0
  5. data/ext/leveldb/platform.rb +83 -0
  6. data/leveldb/Makefile +191 -0
  7. data/leveldb/build_detect_platform +160 -0
  8. data/leveldb/db/builder.cc +88 -0
  9. data/leveldb/db/builder.h +34 -0
  10. data/leveldb/db/c.cc +581 -0
  11. data/leveldb/db/corruption_test.cc +359 -0
  12. data/leveldb/db/db_bench.cc +970 -0
  13. data/leveldb/db/db_impl.cc +1448 -0
  14. data/leveldb/db/db_impl.h +194 -0
  15. data/leveldb/db/db_iter.cc +299 -0
  16. data/leveldb/db/db_iter.h +26 -0
  17. data/leveldb/db/db_test.cc +1901 -0
  18. data/leveldb/db/dbformat.cc +140 -0
  19. data/leveldb/db/dbformat.h +227 -0
  20. data/leveldb/db/dbformat_test.cc +112 -0
  21. data/leveldb/db/filename.cc +139 -0
  22. data/leveldb/db/filename.h +80 -0
  23. data/leveldb/db/filename_test.cc +122 -0
  24. data/leveldb/db/log_format.h +35 -0
  25. data/leveldb/db/log_reader.cc +259 -0
  26. data/leveldb/db/log_reader.h +108 -0
  27. data/leveldb/db/log_test.cc +500 -0
  28. data/leveldb/db/log_writer.cc +103 -0
  29. data/leveldb/db/log_writer.h +48 -0
  30. data/leveldb/db/memtable.cc +145 -0
  31. data/leveldb/db/memtable.h +91 -0
  32. data/leveldb/db/repair.cc +389 -0
  33. data/leveldb/db/skiplist.h +379 -0
  34. data/leveldb/db/skiplist_test.cc +378 -0
  35. data/leveldb/db/snapshot.h +66 -0
  36. data/leveldb/db/table_cache.cc +121 -0
  37. data/leveldb/db/table_cache.h +61 -0
  38. data/leveldb/db/version_edit.cc +266 -0
  39. data/leveldb/db/version_edit.h +107 -0
  40. data/leveldb/db/version_edit_test.cc +46 -0
  41. data/leveldb/db/version_set.cc +1402 -0
  42. data/leveldb/db/version_set.h +370 -0
  43. data/leveldb/db/version_set_test.cc +179 -0
  44. data/leveldb/db/write_batch.cc +147 -0
  45. data/leveldb/db/write_batch_internal.h +49 -0
  46. data/leveldb/db/write_batch_test.cc +120 -0
  47. data/leveldb/helpers/memenv/memenv.cc +374 -0
  48. data/leveldb/helpers/memenv/memenv.h +20 -0
  49. data/leveldb/helpers/memenv/memenv_test.cc +232 -0
  50. data/leveldb/include/leveldb/c.h +275 -0
  51. data/leveldb/include/leveldb/cache.h +99 -0
  52. data/leveldb/include/leveldb/comparator.h +63 -0
  53. data/leveldb/include/leveldb/db.h +161 -0
  54. data/leveldb/include/leveldb/env.h +323 -0
  55. data/leveldb/include/leveldb/filter_policy.h +70 -0
  56. data/leveldb/include/leveldb/iterator.h +100 -0
  57. data/leveldb/include/leveldb/options.h +195 -0
  58. data/leveldb/include/leveldb/slice.h +109 -0
  59. data/leveldb/include/leveldb/status.h +106 -0
  60. data/leveldb/include/leveldb/table.h +85 -0
  61. data/leveldb/include/leveldb/table_builder.h +92 -0
  62. data/leveldb/include/leveldb/write_batch.h +64 -0
  63. data/leveldb/port/atomic_pointer.h +144 -0
  64. data/leveldb/port/port.h +21 -0
  65. data/leveldb/port/port_android.cc +64 -0
  66. data/leveldb/port/port_android.h +159 -0
  67. data/leveldb/port/port_example.h +125 -0
  68. data/leveldb/port/port_posix.cc +50 -0
  69. data/leveldb/port/port_posix.h +129 -0
  70. data/leveldb/port/win/stdint.h +24 -0
  71. data/leveldb/table/block.cc +267 -0
  72. data/leveldb/table/block.h +44 -0
  73. data/leveldb/table/block_builder.cc +109 -0
  74. data/leveldb/table/block_builder.h +57 -0
  75. data/leveldb/table/filter_block.cc +111 -0
  76. data/leveldb/table/filter_block.h +68 -0
  77. data/leveldb/table/filter_block_test.cc +128 -0
  78. data/leveldb/table/format.cc +145 -0
  79. data/leveldb/table/format.h +108 -0
  80. data/leveldb/table/iterator.cc +67 -0
  81. data/leveldb/table/iterator_wrapper.h +63 -0
  82. data/leveldb/table/merger.cc +197 -0
  83. data/leveldb/table/merger.h +26 -0
  84. data/leveldb/table/table.cc +276 -0
  85. data/leveldb/table/table_builder.cc +270 -0
  86. data/leveldb/table/table_test.cc +838 -0
  87. data/leveldb/table/two_level_iterator.cc +182 -0
  88. data/leveldb/table/two_level_iterator.h +34 -0
  89. data/leveldb/util/arena.cc +68 -0
  90. data/leveldb/util/arena.h +68 -0
  91. data/leveldb/util/arena_test.cc +68 -0
  92. data/leveldb/util/bloom.cc +95 -0
  93. data/leveldb/util/bloom_test.cc +159 -0
  94. data/leveldb/util/cache.cc +328 -0
  95. data/leveldb/util/cache_test.cc +186 -0
  96. data/leveldb/util/coding.cc +194 -0
  97. data/leveldb/util/coding.h +104 -0
  98. data/leveldb/util/coding_test.cc +173 -0
  99. data/leveldb/util/comparator.cc +76 -0
  100. data/leveldb/util/crc32c.cc +332 -0
  101. data/leveldb/util/crc32c.h +45 -0
  102. data/leveldb/util/crc32c_test.cc +72 -0
  103. data/leveldb/util/env.cc +96 -0
  104. data/leveldb/util/env_posix.cc +609 -0
  105. data/leveldb/util/env_test.cc +104 -0
  106. data/leveldb/util/filter_policy.cc +11 -0
  107. data/leveldb/util/hash.cc +45 -0
  108. data/leveldb/util/hash.h +19 -0
  109. data/leveldb/util/histogram.cc +139 -0
  110. data/leveldb/util/histogram.h +42 -0
  111. data/leveldb/util/logging.cc +81 -0
  112. data/leveldb/util/logging.h +47 -0
  113. data/leveldb/util/mutexlock.h +39 -0
  114. data/leveldb/util/options.cc +29 -0
  115. data/leveldb/util/posix_logger.h +98 -0
  116. data/leveldb/util/random.h +59 -0
  117. data/leveldb/util/status.cc +75 -0
  118. data/leveldb/util/testharness.cc +77 -0
  119. data/leveldb/util/testharness.h +138 -0
  120. data/leveldb/util/testutil.cc +51 -0
  121. data/leveldb/util/testutil.h +53 -0
  122. data/lib/leveldb.rb +76 -0
  123. metadata +175 -0
@@ -0,0 +1,139 @@
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 <ctype.h>
6
+ #include <stdio.h>
7
+ #include "db/filename.h"
8
+ #include "db/dbformat.h"
9
+ #include "leveldb/env.h"
10
+ #include "util/logging.h"
11
+
12
+ namespace leveldb {
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
+
18
+ static std::string MakeFileName(const std::string& name, uint64_t number,
19
+ const char* suffix) {
20
+ char buf[100];
21
+ snprintf(buf, sizeof(buf), "/%06llu.%s",
22
+ static_cast<unsigned long long>(number),
23
+ suffix);
24
+ return name + buf;
25
+ }
26
+
27
+ std::string LogFileName(const std::string& name, uint64_t number) {
28
+ assert(number > 0);
29
+ return MakeFileName(name, number, "log");
30
+ }
31
+
32
+ std::string TableFileName(const std::string& name, uint64_t number) {
33
+ assert(number > 0);
34
+ return MakeFileName(name, number, "sst");
35
+ }
36
+
37
+ std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
38
+ assert(number > 0);
39
+ char buf[100];
40
+ snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",
41
+ static_cast<unsigned long long>(number));
42
+ return dbname + buf;
43
+ }
44
+
45
+ std::string CurrentFileName(const std::string& dbname) {
46
+ return dbname + "/CURRENT";
47
+ }
48
+
49
+ std::string LockFileName(const std::string& dbname) {
50
+ return dbname + "/LOCK";
51
+ }
52
+
53
+ std::string TempFileName(const std::string& dbname, uint64_t number) {
54
+ assert(number > 0);
55
+ return MakeFileName(dbname, number, "dbtmp");
56
+ }
57
+
58
+ std::string InfoLogFileName(const std::string& dbname) {
59
+ return dbname + "/LOG";
60
+ }
61
+
62
+ // Return the name of the old info log file for "dbname".
63
+ std::string OldInfoLogFileName(const std::string& dbname) {
64
+ return dbname + "/LOG.old";
65
+ }
66
+
67
+
68
+ // Owned filenames have the form:
69
+ // dbname/CURRENT
70
+ // dbname/LOCK
71
+ // dbname/LOG
72
+ // dbname/LOG.old
73
+ // dbname/MANIFEST-[0-9]+
74
+ // dbname/[0-9]+.(log|sst)
75
+ bool ParseFileName(const std::string& fname,
76
+ uint64_t* number,
77
+ FileType* type) {
78
+ Slice rest(fname);
79
+ if (rest == "CURRENT") {
80
+ *number = 0;
81
+ *type = kCurrentFile;
82
+ } else if (rest == "LOCK") {
83
+ *number = 0;
84
+ *type = kDBLockFile;
85
+ } else if (rest == "LOG" || rest == "LOG.old") {
86
+ *number = 0;
87
+ *type = kInfoLogFile;
88
+ } else if (rest.starts_with("MANIFEST-")) {
89
+ rest.remove_prefix(strlen("MANIFEST-"));
90
+ uint64_t num;
91
+ if (!ConsumeDecimalNumber(&rest, &num)) {
92
+ return false;
93
+ }
94
+ if (!rest.empty()) {
95
+ return false;
96
+ }
97
+ *type = kDescriptorFile;
98
+ *number = num;
99
+ } else {
100
+ // Avoid strtoull() to keep filename format independent of the
101
+ // current locale
102
+ uint64_t num;
103
+ if (!ConsumeDecimalNumber(&rest, &num)) {
104
+ return false;
105
+ }
106
+ Slice suffix = rest;
107
+ if (suffix == Slice(".log")) {
108
+ *type = kLogFile;
109
+ } else if (suffix == Slice(".sst")) {
110
+ *type = kTableFile;
111
+ } else if (suffix == Slice(".dbtmp")) {
112
+ *type = kTempFile;
113
+ } else {
114
+ return false;
115
+ }
116
+ *number = num;
117
+ }
118
+ return true;
119
+ }
120
+
121
+ Status SetCurrentFile(Env* env, const std::string& dbname,
122
+ uint64_t descriptor_number) {
123
+ // Remove leading "dbname/" and add newline to manifest file name
124
+ std::string manifest = DescriptorFileName(dbname, descriptor_number);
125
+ Slice contents = manifest;
126
+ assert(contents.starts_with(dbname + "/"));
127
+ contents.remove_prefix(dbname.size() + 1);
128
+ std::string tmp = TempFileName(dbname, descriptor_number);
129
+ Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp);
130
+ if (s.ok()) {
131
+ s = env->RenameFile(tmp, CurrentFileName(dbname));
132
+ }
133
+ if (!s.ok()) {
134
+ env->DeleteFile(tmp);
135
+ }
136
+ return s;
137
+ }
138
+
139
+ } // namespace leveldb
@@ -0,0 +1,80 @@
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
+ // File names used by DB code
6
+
7
+ #ifndef STORAGE_LEVELDB_DB_FILENAME_H_
8
+ #define STORAGE_LEVELDB_DB_FILENAME_H_
9
+
10
+ #include <stdint.h>
11
+ #include <string>
12
+ #include "leveldb/slice.h"
13
+ #include "leveldb/status.h"
14
+ #include "port/port.h"
15
+
16
+ namespace leveldb {
17
+
18
+ class Env;
19
+
20
+ enum FileType {
21
+ kLogFile,
22
+ kDBLockFile,
23
+ kTableFile,
24
+ kDescriptorFile,
25
+ kCurrentFile,
26
+ kTempFile,
27
+ kInfoLogFile // Either the current one, or an old one
28
+ };
29
+
30
+ // Return the name of the log file with the specified number
31
+ // in the db named by "dbname". The result will be prefixed with
32
+ // "dbname".
33
+ extern std::string LogFileName(const std::string& dbname, uint64_t number);
34
+
35
+ // Return the name of the sstable with the specified number
36
+ // in the db named by "dbname". The result will be prefixed with
37
+ // "dbname".
38
+ extern std::string TableFileName(const std::string& dbname, uint64_t number);
39
+
40
+ // Return the name of the descriptor file for the db named by
41
+ // "dbname" and the specified incarnation number. The result will be
42
+ // prefixed with "dbname".
43
+ extern std::string DescriptorFileName(const std::string& dbname,
44
+ uint64_t number);
45
+
46
+ // Return the name of the current file. This file contains the name
47
+ // of the current manifest file. The result will be prefixed with
48
+ // "dbname".
49
+ extern std::string CurrentFileName(const std::string& dbname);
50
+
51
+ // Return the name of the lock file for the db named by
52
+ // "dbname". The result will be prefixed with "dbname".
53
+ extern std::string LockFileName(const std::string& dbname);
54
+
55
+ // Return the name of a temporary file owned by the db named "dbname".
56
+ // The result will be prefixed with "dbname".
57
+ extern std::string TempFileName(const std::string& dbname, uint64_t number);
58
+
59
+ // Return the name of the info log file for "dbname".
60
+ extern std::string InfoLogFileName(const std::string& dbname);
61
+
62
+ // Return the name of the old info log file for "dbname".
63
+ extern std::string OldInfoLogFileName(const std::string& dbname);
64
+
65
+ // If filename is a leveldb file, store the type of the file in *type.
66
+ // The number encoded in the filename is stored in *number. If the
67
+ // filename was successfully parsed, returns true. Else return false.
68
+ extern bool ParseFileName(const std::string& filename,
69
+ uint64_t* number,
70
+ FileType* type);
71
+
72
+ // Make the CURRENT file point to the descriptor file with the
73
+ // specified number.
74
+ extern Status SetCurrentFile(Env* env, const std::string& dbname,
75
+ uint64_t descriptor_number);
76
+
77
+
78
+ } // namespace leveldb
79
+
80
+ #endif // STORAGE_LEVELDB_DB_FILENAME_H_
@@ -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,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