/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file dyn_arena_alloc.hpp Dynamic chunk-size arena allocator. */ #ifndef DYN_ARENA_ALLOC_HPP #define DYN_ARENA_ALLOC_HPP #include /** * Custom arena allocator for uniform-size allocations of a variable size. * The allocation and chunk sizes may only be changed when the arena is empty. */ class DynUniformArenaAllocator { std::vector used_blocks; void *current_block = nullptr; void *last_freed = nullptr; size_t next_position = 0; size_t item_size = 0; size_t items_per_chunk = 0; void NewBlock() { current_block = malloc(item_size * items_per_chunk); assert(current_block != nullptr); next_position = 0; used_blocks.push_back(current_block); } public: DynUniformArenaAllocator() = default; DynUniformArenaAllocator(const DynUniformArenaAllocator &other) = delete; DynUniformArenaAllocator& operator=(const DynUniformArenaAllocator &other) = delete; ~DynUniformArenaAllocator() { EmptyArena(); } void EmptyArena() { current_block = nullptr; last_freed = nullptr; next_position = 0; for (void *block : used_blocks) { free(block); } used_blocks.clear(); } void ResetArena() { EmptyArena(); item_size = 0; items_per_chunk = 0; } void *Allocate() { assert(item_size != 0); if (last_freed) { void *ptr = last_freed; last_freed = *reinterpret_cast(ptr); return ptr; } else { if (current_block == nullptr || next_position == items_per_chunk) { NewBlock(); } void *out = reinterpret_cast(current_block) + (item_size * next_position); next_position++; return out; } } void Free(void *ptr) { if (!ptr) return; assert(current_block != nullptr); *reinterpret_cast(ptr) = last_freed; last_freed = ptr; } void SetParameters(size_t item_size, size_t items_per_chunk) { if (item_size < sizeof(void *)) item_size = sizeof(void *); if (this->item_size == item_size && this->items_per_chunk == items_per_chunk) return; assert(current_block == nullptr); this->item_size = item_size; this->items_per_chunk = items_per_chunk; } }; #endif /* DYN_ARENA_ALLOC_HPP */