#ifndef PROC_NODES_LINEAR_ALLOCATOR_H
#define PROC_NODES_LINEAR_ALLOCATOR_H
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <godot_cpp/templates/vector.hpp>
namespace godot {
// Fast bump allocator. All allocations freed at once via reset().
// Used for temporary data during graph evaluation.
class LinearAllocator {
static constexpr size_t BLOCK_SIZE = 64 * 1024; // 64KB blocks
struct Block {
uint8_t *data;
size_t capacity;
};
Vector<Block> blocks;
int current_block = -1;
size_t current_offset = 0;
void grow() {
Block b;
b.capacity = BLOCK_SIZE;
b.data = static_cast<uint8_t *>(memalloc(BLOCK_SIZE));
blocks.push_back(b);
current_block = blocks.size() - 1;
current_offset = 0;
}
public:
LinearAllocator() = default;
~LinearAllocator() {
for (int i = 0; i < blocks.size(); i++) {
memfree(blocks[i].data);
}
}
void *alloc(size_t p_size, size_t p_align = 16) {
if (p_size == 0) {
return nullptr;
}
// Handle oversized allocations
if (p_size > BLOCK_SIZE / 2) {
Block b;
b.capacity = p_size;
b.data = static_cast<uint8_t *>(memalloc(p_size));
blocks.push_back(b);
return b.data;
}
if (current_block < 0) {
grow();
}
// Align offset
size_t aligned = (current_offset + p_align - 1) & ~(p_align - 1);
if (aligned + p_size > blocks[current_block].capacity) {
grow();
aligned = 0;
}
void *ptr = blocks[current_block].data + aligned;
current_offset = aligned + p_size;
return ptr;
}
template <typename T, typename... Args>
T *construct(Args &&...args) {
void *mem = alloc(sizeof(T), alignof(T));
return new (mem) T(static_cast<Args &&>(args)...);
}
// Free ALL allocations at once
void reset() {
current_block = blocks.size() > 0 ? 0 : -1;
current_offset = 0;
}
// Disallow copy
LinearAllocator(const LinearAllocator &) = delete;
LinearAllocator &operator=(const LinearAllocator &) = delete;
};
} // namespace godot
#endif // PROC_NODES_LINEAR_ALLOCATOR_H