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 "leveldb/table_builder.h" 6 7 #include <assert.h> 8 #include <stdio.h> 9 #include "leveldb/comparator.h" 10 #include "leveldb/env.h" 11 #include "table/block_builder.h" 12 #include "table/format.h" 13 #include "util/coding.h" 14 #include "util/crc32c.h" 15 #include "util/logging.h" 16 17 namespace leveldb { 18 19 struct TableBuilder::Rep { 20 Options options; 21 Options index_block_options; 22 WritableFile* file; 23 uint64_t offset; 24 Status status; 25 BlockBuilder data_block; 26 BlockBuilder index_block; 27 std::string last_key; 28 int64_t num_entries; 29 bool closed; // Either Finish() or Abandon() has been called. 30 31 // We do not emit the index entry for a block until we have seen the 32 // first key for the next data block. This allows us to use shorter 33 // keys in the index block. For example, consider a block boundary 34 // between the keys "the quick brown fox" and "the who". We can use 35 // "the r" as the key for the index block entry since it is >= all 36 // entries in the first block and < all entries in subsequent 37 // blocks. 38 // 39 // Invariant: r->pending_index_entry is true only if data_block is empty. 40 bool pending_index_entry; 41 BlockHandle pending_handle; // Handle to add to index block 42 43 std::string compressed_output; 44 45 Rep(const Options& opt, WritableFile* f) 46 : options(opt), 47 index_block_options(opt), 48 file(f), 49 offset(0), 50 data_block(&options), 51 index_block(&index_block_options), 52 num_entries(0), 53 closed(false), 54 pending_index_entry(false) { 55 index_block_options.block_restart_interval = 1; 56 } 57 }; 58 59 TableBuilder::TableBuilder(const Options& options, WritableFile* file) 60 : rep_(new Rep(options, file)) { 61 } 62 63 TableBuilder::~TableBuilder() { 64 assert(rep_->closed); // Catch errors where caller forgot to call Finish() 65 delete rep_; 66 } 67 68 Status TableBuilder::ChangeOptions(const Options& options) { 69 // Note: if more fields are added to Options, update 70 // this function to catch changes that should not be allowed to 71 // change in the middle of building a Table. 72 if (options.comparator != rep_->options.comparator) { 73 return Status::InvalidArgument("changing comparator while building table"); 74 } 75 76 // Note that any live BlockBuilders point to rep_->options and therefore 77 // will automatically pick up the updated options. 78 rep_->options = options; 79 rep_->index_block_options = options; 80 rep_->index_block_options.block_restart_interval = 1; 81 return Status::OK(); 82 } 83 84 void TableBuilder::Add(const Slice& key, const Slice& value) { 85 Rep* r = rep_; 86 assert(!r->closed); 87 if (!ok()) return; 88 if (r->num_entries > 0) { 89 assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0); 90 } 91 92 if (r->pending_index_entry) { 93 assert(r->data_block.empty()); 94 r->options.comparator->FindShortestSeparator(&r->last_key, key); 95 std::string handle_encoding; 96 r->pending_handle.EncodeTo(&handle_encoding); 97 r->index_block.Add(r->last_key, Slice(handle_encoding)); 98 r->pending_index_entry = false; 99 } 100 101 r->last_key.assign(key.data(), key.size()); 102 r->num_entries++; 103 r->data_block.Add(key, value); 104 105 const size_t estimated_block_size = r->data_block.CurrentSizeEstimate(); 106 if (estimated_block_size >= r->options.block_size) { 107 Flush(); 108 } 109 } 110 111 void TableBuilder::Flush() { 112 Rep* r = rep_; 113 assert(!r->closed); 114 if (!ok()) return; 115 if (r->data_block.empty()) return; 116 assert(!r->pending_index_entry); 117 WriteBlock(&r->data_block, &r->pending_handle); 118 if (ok()) { 119 r->pending_index_entry = true; 120 r->status = r->file->Flush(); 121 } 122 } 123 124 void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) { 125 // File format contains a sequence of blocks where each block has: 126 // block_data: uint8[n] 127 // type: uint8 128 // crc: uint32 129 assert(ok()); 130 Rep* r = rep_; 131 Slice raw = block->Finish(); 132 133 Slice block_contents; 134 CompressionType type = r->options.compression; 135 // TODO(postrelease): Support more compression options: zlib? 136 switch (type) { 137 case kNoCompression: 138 block_contents = raw; 139 break; 140 141 case kSnappyCompression: { 142 std::string* compressed = &r->compressed_output; 143 if (port::Snappy_Compress(raw.data(), raw.size(), compressed) && 144 compressed->size() < raw.size() - (raw.size() / 8u)) { 145 block_contents = *compressed; 146 } else { 147 // Snappy not supported, or compressed less than 12.5%, so just 148 // store uncompressed form 149 block_contents = raw; 150 type = kNoCompression; 151 } 152 break; 153 } 154 } 155 handle->set_offset(r->offset); 156 handle->set_size(block_contents.size()); 157 r->status = r->file->Append(block_contents); 158 if (r->status.ok()) { 159 char trailer[kBlockTrailerSize]; 160 trailer[0] = type; 161 uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size()); 162 crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type 163 EncodeFixed32(trailer+1, crc32c::Mask(crc)); 164 r->status = r->file->Append(Slice(trailer, kBlockTrailerSize)); 165 if (r->status.ok()) { 166 r->offset += block_contents.size() + kBlockTrailerSize; 167 } 168 } 169 r->compressed_output.clear(); 170 block->Reset(); 171 } 172 173 Status TableBuilder::status() const { 174 return rep_->status; 175 } 176 177 Status TableBuilder::Finish() { 178 Rep* r = rep_; 179 Flush(); 180 assert(!r->closed); 181 r->closed = true; 182 BlockHandle metaindex_block_handle; 183 BlockHandle index_block_handle; 184 if (ok()) { 185 BlockBuilder meta_index_block(&r->options); 186 // TODO(postrelease): Add stats and other meta blocks 187 WriteBlock(&meta_index_block, &metaindex_block_handle); 188 } 189 if (ok()) { 190 if (r->pending_index_entry) { 191 r->options.comparator->FindShortSuccessor(&r->last_key); 192 std::string handle_encoding; 193 r->pending_handle.EncodeTo(&handle_encoding); 194 r->index_block.Add(r->last_key, Slice(handle_encoding)); 195 r->pending_index_entry = false; 196 } 197 WriteBlock(&r->index_block, &index_block_handle); 198 } 199 if (ok()) { 200 Footer footer; 201 footer.set_metaindex_handle(metaindex_block_handle); 202 footer.set_index_handle(index_block_handle); 203 std::string footer_encoding; 204 footer.EncodeTo(&footer_encoding); 205 r->status = r->file->Append(footer_encoding); 206 if (r->status.ok()) { 207 r->offset += footer_encoding.size(); 208 } 209 } 210 return r->status; 211 } 212 213 void TableBuilder::Abandon() { 214 Rep* r = rep_; 215 assert(!r->closed); 216 r->closed = true; 217 } 218 219 uint64_t TableBuilder::NumEntries() const { 220 return rep_->num_entries; 221 } 222 223 uint64_t TableBuilder::FileSize() const { 224 return rep_->offset; 225 } 226 227 } 228