From 4a8b2df1084abb0d238ad5a40a1287c45d72b0b1 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 29 May 2022 19:56:49 +0100 Subject: [PATCH] Util: Add a non-dynamic arena allocator based on DynUniformArenaAllocator --- src/core/arena_alloc.hpp | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/core/arena_alloc.hpp diff --git a/src/core/arena_alloc.hpp b/src/core/arena_alloc.hpp new file mode 100644 index 0000000000..0fd19aef97 --- /dev/null +++ b/src/core/arena_alloc.hpp @@ -0,0 +1,86 @@ +/* + * 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 arena_alloc.hpp Arena allocator. */ + +#ifndef ARENA_ALLOC_HPP +#define ARENA_ALLOC_HPP + +#include + +/** + * Custom arena allocator for uniform-size allocations. + */ +template +class UniformArenaAllocator { + static_assert(SIZE >= sizeof(void *)); + + std::vector used_blocks; + + void *current_block = nullptr; + void *last_freed = nullptr; + size_t next_position = 0; + + void NewBlock() + { + current_block = malloc(SIZE * N_PER_CHUNK); + assert(current_block != nullptr); + next_position = 0; + used_blocks.push_back(current_block); + } + + public: + UniformArenaAllocator() = default; + UniformArenaAllocator(const UniformArenaAllocator &other) = delete; + UniformArenaAllocator& operator=(const UniformArenaAllocator &other) = delete; + + ~UniformArenaAllocator() + { + 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(); + } + + void *Allocate() { + if (last_freed) { + void *ptr = last_freed; + last_freed = *reinterpret_cast(ptr); + return ptr; + } else { + if (current_block == nullptr || next_position == N_PER_CHUNK) { + NewBlock(); + } + void *out = reinterpret_cast(current_block) + (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; + } +}; + +#endif /* ARENA_ALLOC_HPP */