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