Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
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 be3// found in the LICENSE file. See the AUTHORS file for names of contributors.5#include "leveldb/table_builder.h"7#include <assert.h>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"19namespace leveldb {21struct TableBuilder::Rep {22Rep(const Options& opt, WritableFile* f)23: options(opt),24index_block_options(opt),25file(f),26offset(0),27data_block(&options),28index_block(&index_block_options),29num_entries(0),30closed(false),31filter_block(opt.filter_policy == nullptr32? nullptr33: new FilterBlockBuilder(opt.filter_policy)),34pending_index_entry(false) {35index_block_options.block_restart_interval = 1;36}38Options options;39Options index_block_options;40WritableFile* file;41uint64_t offset;42Status status;43BlockBuilder data_block;44BlockBuilder index_block;45std::string last_key;46int64_t num_entries;47bool closed; // Either Finish() or Abandon() has been called.48FilterBlockBuilder* filter_block;50// We do not emit the index entry for a block until we have seen the51// first key for the next data block. This allows us to use shorter52// keys in the index block. For example, consider a block boundary53// between the keys "the quick brown fox" and "the who". We can use54// "the r" as the key for the index block entry since it is >= all55// entries in the first block and < all entries in subsequent56// blocks.57//58// Invariant: r->pending_index_entry is true only if data_block is empty.59bool pending_index_entry;60BlockHandle pending_handle; // Handle to add to index block62std::string compressed_output;63};65TableBuilder::TableBuilder(const Options& options, WritableFile* file)66: rep_(new Rep(options, file)) {67if (rep_->filter_block != nullptr) {68rep_->filter_block->StartBlock(0);69}70}72TableBuilder::~TableBuilder() {73assert(rep_->closed); // Catch errors where caller forgot to call Finish()74delete rep_->filter_block;75delete rep_;76}78Status TableBuilder::ChangeOptions(const Options& options) {79// Note: if more fields are added to Options, update80// this function to catch changes that should not be allowed to81// change in the middle of building a Table.82if (options.comparator != rep_->options.comparator) {83return Status::InvalidArgument("changing comparator while building table");84}86// Note that any live BlockBuilders point to rep_->options and therefore87// will automatically pick up the updated options.88rep_->options = options;89rep_->index_block_options = options;90rep_->index_block_options.block_restart_interval = 1;91return Status::OK();92}94void TableBuilder::Add(const Slice& key, const Slice& value) {95Rep* r = rep_;96assert(!r->closed);97if (!ok()) return;98if (r->num_entries > 0) {99assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0);100}102if (r->pending_index_entry) {103assert(r->data_block.empty());104r->options.comparator->FindShortestSeparator(&r->last_key, key);105std::string handle_encoding;106r->pending_handle.EncodeTo(&handle_encoding);107r->index_block.Add(r->last_key, Slice(handle_encoding));108r->pending_index_entry = false;109}111if (r->filter_block != nullptr) {112r->filter_block->AddKey(key);113}115r->last_key.assign(key.data(), key.size());116r->num_entries++;117r->data_block.Add(key, value);119const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();120if (estimated_block_size >= r->options.block_size) {121Flush();122}123}125void TableBuilder::Flush() {126Rep* r = rep_;127assert(!r->closed);128if (!ok()) return;129if (r->data_block.empty()) return;130assert(!r->pending_index_entry);131WriteBlock(&r->data_block, &r->pending_handle);132if (ok()) {133r->pending_index_entry = true;134r->status = r->file->Flush();135}136if (r->filter_block != nullptr) {137r->filter_block->StartBlock(r->offset);138}139}141void 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: uint8145// crc: uint32146assert(ok());147Rep* r = rep_;148Slice raw = block->Finish();150Slice block_contents;151CompressionType type = r->options.compression;152// TODO(postrelease): Support more compression options: zlib?153switch (type) {154case kNoCompression:155block_contents = raw;156break;158case kSnappyCompression: {159std::string* compressed = &r->compressed_output;160if (port::Snappy_Compress(raw.data(), raw.size(), compressed) &&161compressed->size() < raw.size() - (raw.size() / 8u)) {162block_contents = *compressed;163} else {164// Snappy not supported, or compressed less than 12.5%, so just165// store uncompressed form166block_contents = raw;167type = kNoCompression;168}169break;170}171}172WriteRawBlock(block_contents, type, handle);173r->compressed_output.clear();174block->Reset();175}177void TableBuilder::WriteRawBlock(const Slice& block_contents,178CompressionType type, BlockHandle* handle) {179Rep* r = rep_;180handle->set_offset(r->offset);181handle->set_size(block_contents.size());182r->status = r->file->Append(block_contents);183if (r->status.ok()) {184char trailer[kBlockTrailerSize];185trailer[0] = type;186uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size());187crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type188EncodeFixed32(trailer + 1, crc32c::Mask(crc));189r->status = r->file->Append(Slice(trailer, kBlockTrailerSize));190if (r->status.ok()) {191r->offset += block_contents.size() + kBlockTrailerSize;192}193}194}196Status TableBuilder::status() const { return rep_->status; }198Status TableBuilder::Finish() {199Rep* r = rep_;200Flush();201assert(!r->closed);202r->closed = true;204BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle;206// Write filter block207if (ok() && r->filter_block != nullptr) {208WriteRawBlock(r->filter_block->Finish(), kNoCompression,209&filter_block_handle);210}212// Write metaindex block213if (ok()) {214BlockBuilder meta_index_block(&r->options);215if (r->filter_block != nullptr) {216// Add mapping from "filter.Name" to location of filter data217std::string key = "filter.";218key.append(r->options.filter_policy->Name());219std::string handle_encoding;220filter_block_handle.EncodeTo(&handle_encoding);221meta_index_block.Add(key, handle_encoding);222}224// TODO(postrelease): Add stats and other meta blocks225WriteBlock(&meta_index_block, &metaindex_block_handle);226}228// Write index block229if (ok()) {230if (r->pending_index_entry) {231r->options.comparator->FindShortSuccessor(&r->last_key);232std::string handle_encoding;233r->pending_handle.EncodeTo(&handle_encoding);234r->index_block.Add(r->last_key, Slice(handle_encoding));235r->pending_index_entry = false;236}237WriteBlock(&r->index_block, &index_block_handle);238}240// Write footer241if (ok()) {242Footer footer;243footer.set_metaindex_handle(metaindex_block_handle);244footer.set_index_handle(index_block_handle);245std::string footer_encoding;246footer.EncodeTo(&footer_encoding);247r->status = r->file->Append(footer_encoding);248if (r->status.ok()) {249r->offset += footer_encoding.size();250}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.