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 "table/format.h" |
6 | |
7 | #include "leveldb/env.h" |
8 | #include "port/port.h" |
9 | #include "table/block.h" |
10 | #include "util/coding.h" |
11 | #include "util/crc32c.h" |
12 | |
13 | namespace leveldb { |
14 | |
15 | void BlockHandle::EncodeTo(std::string* dst) const { |
16 | // Sanity check that all fields have been set |
17 | assert(offset_ != ~static_cast<uint64_t>(0)); |
18 | assert(size_ != ~static_cast<uint64_t>(0)); |
19 | PutVarint64(dst, offset_); |
20 | PutVarint64(dst, size_); |
21 | } |
22 | |
23 | Status BlockHandle::DecodeFrom(Slice* input) { |
24 | if (GetVarint64(input, &offset_) && GetVarint64(input, &size_)) { |
25 | return Status::OK(); |
26 | } else { |
27 | return Status::Corruption("bad block handle" ); |
28 | } |
29 | } |
30 | |
31 | void Footer::(std::string* dst) const { |
32 | const size_t original_size = dst->size(); |
33 | metaindex_handle_.EncodeTo(dst); |
34 | index_handle_.EncodeTo(dst); |
35 | dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding |
36 | PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber & 0xffffffffu)); |
37 | PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber >> 32)); |
38 | assert(dst->size() == original_size + kEncodedLength); |
39 | (void)original_size; // Disable unused variable warning. |
40 | } |
41 | |
42 | Status Footer::(Slice* input) { |
43 | const char* magic_ptr = input->data() + kEncodedLength - 8; |
44 | const uint32_t magic_lo = DecodeFixed32(magic_ptr); |
45 | const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4); |
46 | const uint64_t magic = ((static_cast<uint64_t>(magic_hi) << 32) | |
47 | (static_cast<uint64_t>(magic_lo))); |
48 | if (magic != kTableMagicNumber) { |
49 | return Status::Corruption("not an sstable (bad magic number)" ); |
50 | } |
51 | |
52 | Status result = metaindex_handle_.DecodeFrom(input); |
53 | if (result.ok()) { |
54 | result = index_handle_.DecodeFrom(input); |
55 | } |
56 | if (result.ok()) { |
57 | // We skip over any leftover data (just padding for now) in "input" |
58 | const char* end = magic_ptr + 8; |
59 | *input = Slice(end, input->data() + input->size() - end); |
60 | } |
61 | return result; |
62 | } |
63 | |
64 | Status ReadBlock(RandomAccessFile* file, const ReadOptions& options, |
65 | const BlockHandle& handle, BlockContents* result) { |
66 | result->data = Slice(); |
67 | result->cachable = false; |
68 | result->heap_allocated = false; |
69 | |
70 | // Read the block contents as well as the type/crc footer. |
71 | // See table_builder.cc for the code that built this structure. |
72 | size_t n = static_cast<size_t>(handle.size()); |
73 | char* buf = new char[n + kBlockTrailerSize]; |
74 | Slice contents; |
75 | Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf); |
76 | if (!s.ok()) { |
77 | delete[] buf; |
78 | return s; |
79 | } |
80 | if (contents.size() != n + kBlockTrailerSize) { |
81 | delete[] buf; |
82 | return Status::Corruption("truncated block read" ); |
83 | } |
84 | |
85 | // Check the crc of the type and the block contents |
86 | const char* data = contents.data(); // Pointer to where Read put the data |
87 | if (options.verify_checksums) { |
88 | const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1)); |
89 | const uint32_t actual = crc32c::Value(data, n + 1); |
90 | if (actual != crc) { |
91 | delete[] buf; |
92 | s = Status::Corruption("block checksum mismatch" ); |
93 | return s; |
94 | } |
95 | } |
96 | |
97 | switch (data[n]) { |
98 | case kNoCompression: |
99 | if (data != buf) { |
100 | // File implementation gave us pointer to some other data. |
101 | // Use it directly under the assumption that it will be live |
102 | // while the file is open. |
103 | delete[] buf; |
104 | result->data = Slice(data, n); |
105 | result->heap_allocated = false; |
106 | result->cachable = false; // Do not double-cache |
107 | } else { |
108 | result->data = Slice(buf, n); |
109 | result->heap_allocated = true; |
110 | result->cachable = true; |
111 | } |
112 | |
113 | // Ok |
114 | break; |
115 | case kSnappyCompression: { |
116 | size_t ulength = 0; |
117 | if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) { |
118 | delete[] buf; |
119 | return Status::Corruption("corrupted compressed block contents" ); |
120 | } |
121 | char* ubuf = new char[ulength]; |
122 | if (!port::Snappy_Uncompress(data, n, ubuf)) { |
123 | delete[] buf; |
124 | delete[] ubuf; |
125 | return Status::Corruption("corrupted compressed block contents" ); |
126 | } |
127 | delete[] buf; |
128 | result->data = Slice(ubuf, ulength); |
129 | result->heap_allocated = true; |
130 | result->cachable = true; |
131 | break; |
132 | } |
133 | default: |
134 | delete[] buf; |
135 | return Status::Corruption("bad block type" ); |
136 | } |
137 | |
138 | return Status::OK(); |
139 | } |
140 | |
141 | } // namespace leveldb |
142 | |