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.
5
#include "leveldb/table_builder.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"
19
struct TableBuilder::Rep {
21
Options index_block_options;
25
BlockBuilder data_block;
26
BlockBuilder index_block;
29
bool closed; // Either Finish() or Abandon() has been called.
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
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
43
std::string compressed_output;
45
Rep(const Options& opt, WritableFile* f)
47
index_block_options(opt),
51
index_block(&index_block_options),
54
pending_index_entry(false) {
55
index_block_options.block_restart_interval = 1;
59
TableBuilder::TableBuilder(const Options& options, WritableFile* file)
60
: rep_(new Rep(options, file)) {
63
TableBuilder::~TableBuilder() {
64
assert(rep_->closed); // Catch errors where caller forgot to call Finish()
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");
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;
84
void TableBuilder::Add(const Slice& key, const Slice& value) {
88
if (r->num_entries > 0) {
89
assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0);
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;
101
r->last_key.assign(key.data(), key.size());
103
r->data_block.Add(key, value);
105
const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();
106
if (estimated_block_size >= r->options.block_size) {
111
void TableBuilder::Flush() {
115
if (r->data_block.empty()) return;
116
assert(!r->pending_index_entry);
117
WriteBlock(&r->data_block, &r->pending_handle);
119
r->pending_index_entry = true;
120
r->status = r->file->Flush();
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]
131
Slice raw = block->Finish();
133
Slice block_contents;
134
CompressionType type = r->options.compression;
135
// TODO(postrelease): Support more compression options: zlib?
138
block_contents = raw;
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;
147
// Snappy not supported, or compressed less than 12.5%, so just
148
// store uncompressed form
149
block_contents = raw;
150
type = kNoCompression;
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];
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;
169
r->compressed_output.clear();
173
Status TableBuilder::status() const {
177
Status TableBuilder::Finish() {
182
BlockHandle metaindex_block_handle;
183
BlockHandle index_block_handle;
185
BlockBuilder meta_index_block(&r->options);
186
// TODO(postrelease): Add stats and other meta blocks
187
WriteBlock(&meta_index_block, &metaindex_block_handle);
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;
197
WriteBlock(&r->index_block, &index_block_handle);
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();
213
void TableBuilder::Abandon() {
219
uint64_t TableBuilder::NumEntries() const {
220
return rep_->num_entries;
223
uint64_t TableBuilder::FileSize() const {
227
} // namespace leveldb