Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
memory.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright 2026 Johnson Ogundeji
3#pragma once
4
13
14#include <cstdint>
15#include <cstdlib>
16#include <cstring>
17#include <memory>
18#include <new>
19#include <vector>
20
21namespace signet::forge {
22
33class Arena {
34public:
37 explicit Arena(size_t block_size = 64 * 1024)
38 : block_size_(block_size) {}
39
40 ~Arena() = default;
41
52 void* allocate(size_t size, size_t alignment = 8) {
53 if (size == 0) return nullptr;
54
55 // Try to fit in the current (last) block
56 if (!blocks_.empty()) {
57 Block& current = blocks_.back();
58 void* ptr = try_allocate_from(current, size, alignment);
59 if (ptr) {
60 total_used_ += size;
61 return ptr;
62 }
63 }
64
65 // Current block cannot satisfy — allocate a new block
66 if (size > SIZE_MAX - alignment) return nullptr; // overflow guard
67 size_t new_block_size = (size + alignment > block_size_)
68 ? (size + alignment)
69 : block_size_;
70 allocate_block(new_block_size);
71
72 Block& fresh = blocks_.back();
73 void* ptr = try_allocate_from(fresh, size, alignment);
74 // A fresh block is guaranteed to fit (we sized it above)
75 if (ptr) {
76 total_used_ += size;
77 }
78 return ptr;
79 }
80
92 void* allocate_zeroed(size_t size, size_t alignment = alignof(std::max_align_t)) {
93 void* ptr = allocate(size, alignment);
94 if (ptr) std::memset(ptr, 0, size);
95 return ptr;
96 }
97
106 template <typename T>
107 T* allocate_array(size_t count) {
108 if (count == 0) return nullptr;
109 if (count > SIZE_MAX / sizeof(T)) return nullptr; // overflow guard
110 void* raw = allocate(count * sizeof(T), alignof(T));
111 return static_cast<T*>(raw);
112 }
113
119 const uint8_t* copy(const uint8_t* data, size_t size) {
120 if (size == 0 || data == nullptr) return nullptr;
121 void* dst = allocate(size, 1);
122 if (!dst) return nullptr; // allocation failed — propagate null
123 std::memcpy(dst, data, size);
124 return static_cast<const uint8_t*>(dst);
125 }
126
131 const char* copy_string(const std::string& str) {
132 size_t len = str.size() + 1; // include null terminator
133 void* dst = allocate(len, 1);
134 if (!dst) return nullptr; // allocation failed — propagate null
135 std::memcpy(dst, str.c_str(), len);
136 return static_cast<const char*>(dst);
137 }
138
141 [[nodiscard]] size_t bytes_used() const { return total_used_; }
142
147 void reset() {
148 for (auto& block : blocks_) {
149 block.used = 0;
150 }
151 total_used_ = 0;
152 }
153
154 Arena(const Arena&) = delete;
155 Arena& operator=(const Arena&) = delete;
156
157 Arena(Arena&&) noexcept = default;
158 Arena& operator=(Arena&&) noexcept = default;
159
160private:
162 struct Block {
163 std::unique_ptr<uint8_t[]> data;
164 size_t size;
165 size_t used;
166 };
167
168 std::vector<Block> blocks_;
169 size_t block_size_;
170 size_t total_used_ = 0;
171
173 static void* try_allocate_from(Block& block, size_t size, size_t alignment) {
174 // Round up 'used' to the next multiple of alignment
175 size_t aligned_offset = align_up(block.used, alignment);
176 if (aligned_offset + size > block.size) {
177 return nullptr;
178 }
179 void* ptr = block.data.get() + aligned_offset;
180 block.used = aligned_offset + size;
181 return ptr;
182 }
183
186 void allocate_block(size_t size) {
187 try {
188 Block block;
189 block.data = std::make_unique<uint8_t[]>(size);
190 block.size = size;
191 block.used = 0;
192 blocks_.push_back(std::move(block));
193 } catch (const std::bad_alloc&) {
194 // Push an empty block so callers see a block with size 0
195 Block empty;
196 empty.size = 0;
197 empty.used = 0;
198 blocks_.push_back(std::move(empty));
199 }
200 }
201
203 static size_t align_up(size_t offset, size_t alignment) {
204 size_t mask = alignment - 1;
205 // Overflow guard: if offset + mask would wrap, return SIZE_MAX (CWE-190)
206 if (offset > SIZE_MAX - mask) return SIZE_MAX;
207 return (offset + mask) & ~mask;
208 }
209};
210
211} // namespace signet::forge
Bump-pointer arena allocator for batch Parquet reads.
Definition memory.hpp:33
Arena & operator=(const Arena &)=delete
Non-copyable.
void reset()
Reset the arena, reusing all memory blocks without freeing them.
Definition memory.hpp:147
Arena(const Arena &)=delete
Non-copyable.
Arena(Arena &&) noexcept=default
Move-constructible.
T * allocate_array(size_t count)
Allocate a typed array of count elements from the arena.
Definition memory.hpp:107
const char * copy_string(const std::string &str)
Copy a string into the arena (including null terminator).
Definition memory.hpp:131
size_t bytes_used() const
Total bytes allocated (excluding alignment padding).
Definition memory.hpp:141
void * allocate_zeroed(size_t size, size_t alignment=alignof(std::max_align_t))
Allocate zero-initialized memory from the arena.
Definition memory.hpp:92
Arena(size_t block_size=64 *1024)
Construct an arena with the given default block size.
Definition memory.hpp:37
void * allocate(size_t size, size_t alignment=8)
Allocate aligned memory from the arena.
Definition memory.hpp:52
const uint8_t * copy(const uint8_t *data, size_t size)
Copy raw bytes into the arena and return a pointer to the copy.
Definition memory.hpp:119