| 1 | #include "duckdb/common/file_buffer.hpp" |
| 2 | |
| 3 | #include "duckdb/common/allocator.hpp" |
| 4 | #include "duckdb/common/checksum.hpp" |
| 5 | #include "duckdb/common/exception.hpp" |
| 6 | #include "duckdb/common/file_system.hpp" |
| 7 | #include "duckdb/common/helper.hpp" |
| 8 | |
| 9 | #include <cstring> |
| 10 | |
| 11 | namespace duckdb { |
| 12 | |
| 13 | FileBuffer::FileBuffer(Allocator &allocator, FileBufferType type, uint64_t user_size) |
| 14 | : allocator(allocator), type(type) { |
| 15 | Init(); |
| 16 | if (user_size) { |
| 17 | Resize(user_size); |
| 18 | } |
| 19 | } |
| 20 | |
| 21 | void FileBuffer::Init() { |
| 22 | buffer = nullptr; |
| 23 | size = 0; |
| 24 | internal_buffer = nullptr; |
| 25 | internal_size = 0; |
| 26 | } |
| 27 | |
| 28 | FileBuffer::FileBuffer(FileBuffer &source, FileBufferType type_p) : allocator(source.allocator), type(type_p) { |
| 29 | // take over the structures of the source buffer |
| 30 | buffer = source.buffer; |
| 31 | size = source.size; |
| 32 | internal_buffer = source.internal_buffer; |
| 33 | internal_size = source.internal_size; |
| 34 | |
| 35 | source.Init(); |
| 36 | } |
| 37 | |
| 38 | FileBuffer::~FileBuffer() { |
| 39 | if (!internal_buffer) { |
| 40 | return; |
| 41 | } |
| 42 | allocator.FreeData(pointer: internal_buffer, size: internal_size); |
| 43 | } |
| 44 | |
| 45 | void FileBuffer::ReallocBuffer(size_t new_size) { |
| 46 | data_ptr_t new_buffer; |
| 47 | if (internal_buffer) { |
| 48 | new_buffer = allocator.ReallocateData(pointer: internal_buffer, old_size: internal_size, size: new_size); |
| 49 | } else { |
| 50 | new_buffer = allocator.AllocateData(size: new_size); |
| 51 | } |
| 52 | if (!new_buffer) { |
| 53 | throw std::bad_alloc(); |
| 54 | } |
| 55 | internal_buffer = new_buffer; |
| 56 | internal_size = new_size; |
| 57 | // Caller must update these. |
| 58 | buffer = nullptr; |
| 59 | size = 0; |
| 60 | } |
| 61 | |
| 62 | FileBuffer::MemoryRequirement FileBuffer::CalculateMemory(uint64_t user_size) { |
| 63 | FileBuffer::MemoryRequirement result; |
| 64 | |
| 65 | if (type == FileBufferType::TINY_BUFFER) { |
| 66 | // We never do IO on tiny buffers, so there's no need to add a header or sector-align. |
| 67 | result.header_size = 0; |
| 68 | result.alloc_size = user_size; |
| 69 | } else { |
| 70 | result.header_size = Storage::BLOCK_HEADER_SIZE; |
| 71 | result.alloc_size = AlignValue<uint32_t, Storage::SECTOR_SIZE>(n: result.header_size + user_size); |
| 72 | } |
| 73 | return result; |
| 74 | } |
| 75 | |
| 76 | void FileBuffer::Resize(uint64_t new_size) { |
| 77 | auto req = CalculateMemory(user_size: new_size); |
| 78 | ReallocBuffer(new_size: req.alloc_size); |
| 79 | |
| 80 | if (new_size > 0) { |
| 81 | buffer = internal_buffer + req.header_size; |
| 82 | size = internal_size - req.header_size; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | void FileBuffer::Read(FileHandle &handle, uint64_t location) { |
| 87 | D_ASSERT(type != FileBufferType::TINY_BUFFER); |
| 88 | handle.Read(buffer: internal_buffer, nr_bytes: internal_size, location); |
| 89 | } |
| 90 | |
| 91 | void FileBuffer::Write(FileHandle &handle, uint64_t location) { |
| 92 | D_ASSERT(type != FileBufferType::TINY_BUFFER); |
| 93 | handle.Write(buffer: internal_buffer, nr_bytes: internal_size, location); |
| 94 | } |
| 95 | |
| 96 | void FileBuffer::Clear() { |
| 97 | memset(s: internal_buffer, c: 0, n: internal_size); |
| 98 | } |
| 99 | |
| 100 | void FileBuffer::Initialize(DebugInitialize initialize) { |
| 101 | if (initialize == DebugInitialize::NO_INITIALIZE) { |
| 102 | return; |
| 103 | } |
| 104 | uint8_t value = initialize == DebugInitialize::DEBUG_ZERO_INITIALIZE ? 0 : 0xFF; |
| 105 | memset(s: internal_buffer, c: value, n: internal_size); |
| 106 | } |
| 107 | |
| 108 | } // namespace duckdb |
| 109 | |