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