format.cc
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::EncodeTo(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::DecodeFrom(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", file->GetName()); 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", file->GetName()); 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", file->GetName()); 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", file->GetName()); 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", file->GetName()); 136 } 137 138 return Status::OK(); 139 } 140 141 } // namespace leveldb