1#include "duckdb/storage/meta_block_writer.hpp"
2
3#include <cstring>
4
5namespace duckdb {
6
7MetaBlockWriter::MetaBlockWriter(BlockManager &block_manager, block_id_t initial_block_id)
8 : block_manager(block_manager) {
9 if (initial_block_id == INVALID_BLOCK) {
10 initial_block_id = MetaBlockWriter::GetNextBlockId();
11 }
12 block = block_manager.CreateBlock(block_id: initial_block_id, source_buffer: nullptr);
13 Store<block_id_t>(val: -1, ptr: block->buffer);
14 offset = sizeof(block_id_t);
15}
16
17MetaBlockWriter::~MetaBlockWriter() {
18 // If there's an exception during checkpoint, this can get destroyed without
19 // flushing the data...which is fine, because none of the unwritten data
20 // will be referenced.
21 //
22 // Otherwise, we should have explicitly flushed (and thereby nulled the block).
23 D_ASSERT(!block || Exception::UncaughtException());
24}
25
26block_id_t MetaBlockWriter::GetNextBlockId() {
27 return block_manager.GetFreeBlockId();
28}
29
30BlockPointer MetaBlockWriter::GetBlockPointer() {
31 BlockPointer pointer;
32 pointer.block_id = block->id;
33 pointer.offset = offset;
34 return pointer;
35}
36
37void MetaBlockWriter::Flush() {
38 if (offset < block->size) {
39 // clear remaining bytes of block (if any)
40 memset(s: block->buffer + offset, c: 0, n: block->size - offset);
41 }
42 AdvanceBlock();
43 block = nullptr;
44}
45
46void MetaBlockWriter::AdvanceBlock() {
47 written_blocks.insert(x: block->id);
48 if (offset > sizeof(block_id_t)) {
49 block_manager.Write(block&: *block);
50 offset = sizeof(block_id_t);
51 }
52}
53
54void MetaBlockWriter::WriteData(const_data_ptr_t buffer, idx_t write_size) {
55 while (offset + write_size > block->size) {
56 // we need to make a new block
57 // first copy what we can
58 D_ASSERT(offset <= block->size);
59 idx_t copy_amount = block->size - offset;
60 if (copy_amount > 0) {
61 memcpy(dest: block->buffer + offset, src: buffer, n: copy_amount);
62 buffer += copy_amount;
63 offset += copy_amount;
64 write_size -= copy_amount;
65 }
66 // now we need to get a new block id
67 block_id_t new_block_id = GetNextBlockId();
68 // write the block id of the new block to the start of the current block
69 Store<block_id_t>(val: new_block_id, ptr: block->buffer);
70 // first flush the old block
71 AdvanceBlock();
72 // now update the block id of the block
73 block->id = new_block_id;
74 Store<block_id_t>(val: -1, ptr: block->buffer);
75 }
76 memcpy(dest: block->buffer + offset, src: buffer, n: write_size);
77 offset += write_size;
78}
79
80} // namespace duckdb
81