25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

224 satır
6.5 KiB

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