1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib
#ifndef FOONATHAN_MEMORY_STATIC_ALLOCATOR_HPP_INCLUDED
#define FOONATHAN_MEMORY_STATIC_ALLOCATOR_HPP_INCLUDED
/// \file
/// Allocators using a static, fixed-sized storage.
#include <type_traits>
#include "detail/align.hpp"
#include "detail/assert.hpp"
#include "detail/memory_stack.hpp"
#include "detail/utility.hpp"
#include "config.hpp"
#if FOONATHAN_MEMORY_EXTERN_TEMPLATE
#include "allocator_traits.hpp"
#endif
namespace foonathan
{
namespace memory
{
/// Storage for a \ref static_allocator.
/// Its constructor will take a reference to it and use it for its allocation.
/// The storage type is simply a \c char array aligned for maximum alignment.
/// \note It is not allowed to access the memory of the storage.
/// \ingroup allocator
template <std::size_t Size>
struct static_allocator_storage
{
alignas(detail::max_alignment) char storage[Size];
};
static_assert(sizeof(static_allocator_storage<1024>) == 1024, "");
static_assert(alignof(static_allocator_storage<1024>) == detail::max_alignment, "");
struct allocator_info;
/// A stateful \concept{concept_rawallocator,RawAllocator} that uses a fixed sized storage for the allocations.
/// It works on a \ref static_allocator_storage and uses its memory for all allocations.
/// Deallocations are not supported, memory cannot be marked as freed.<br>
/// \note It is not allowed to share an \ref static_allocator_storage between multiple \ref static_allocator objects.
/// \ingroup allocator
class static_allocator
{
public:
using is_stateful = std::true_type;
/// \effects Creates it by passing it a \ref static_allocator_storage by reference.
/// It will take the address of the storage and use its memory for the allocation.
/// \requires The storage object must live as long as the allocator object.
/// It must not be shared between multiple allocators,
/// i.e. the object must not have been passed to a constructor before.
template <std::size_t Size>
static_allocator(static_allocator_storage<Size>& storage) noexcept
: stack_(&storage), end_(stack_.top() + Size)
{
}
/// \effects A \concept{concept_rawallocator,RawAllocator} allocation function.
/// It uses the specified \ref static_allocator_storage.
/// \returns A pointer to a \concept{concept_node,node}, it will never be \c nullptr.
/// \throws An exception of type \ref out_of_memory or whatever is thrown by its handler if the storage is exhausted.
void* allocate_node(std::size_t size, std::size_t alignment);
/// \effects A \concept{concept_rawallocator,RawAllocator} deallocation function.
/// It does nothing, deallocation is not supported by this allocator.
void deallocate_node(void*, std::size_t, std::size_t) noexcept {}
/// \returns The maximum node size which is the capacity remaining inside the \ref static_allocator_storage.
std::size_t max_node_size() const noexcept
{
return static_cast<std::size_t>(end_ - stack_.top());
}
/// \returns The maximum possible value since there is no alignment restriction
/// (except indirectly through the size of the \ref static_allocator_storage).
std::size_t max_alignment() const noexcept
{
return std::size_t(-1);
}
private:
allocator_info info() const noexcept;
detail::fixed_memory_stack stack_;
const char* end_;
};
#if FOONATHAN_MEMORY_EXTERN_TEMPLATE
extern template class allocator_traits<static_allocator>;
#endif
struct memory_block;
/// A \concept{concept_blockallocator,BlockAllocator} that allocates the blocks from a fixed size storage.
/// It works on a \ref static_allocator_storage and uses it for all allocations,
/// deallocations are only allowed in reversed order which is guaranteed by \ref memory_arena.
/// \note It is not allowed to share an \ref static_allocator_storage between multiple \ref static_allocator objects.
/// \ingroup allocator
class static_block_allocator
{
public:
/// \effects Creates it by passing it the block size and a \ref static_allocator_storage by reference.
/// It will take the address of the storage and use it to allocate \c block_size'd blocks.
/// \requires The storage object must live as long as the allocator object.
/// It must not be shared between multiple allocators,
/// i.e. the object must not have been passed to a constructor before.
/// The size of the \ref static_allocator_storage must be a multiple of the (non-null) block size.
template <std::size_t Size>
static_block_allocator(std::size_t block_size,
static_allocator_storage<Size>& storage) noexcept
: cur_(static_cast<char*>(static_cast<void*>(&storage))),
end_(cur_ + Size),
block_size_(block_size)
{
FOONATHAN_MEMORY_ASSERT(block_size <= Size);
FOONATHAN_MEMORY_ASSERT(Size % block_size == 0u);
}
~static_block_allocator() noexcept = default;
/// @{
/// \effects Moves the block allocator, it transfers ownership over the \ref static_allocator_storage.
/// This does not invalidate any memory blocks.
static_block_allocator(static_block_allocator&& other) noexcept
: cur_(other.cur_), end_(other.end_), block_size_(other.block_size_)
{
other.cur_ = other.end_ = nullptr;
other.block_size_ = 0;
}
static_block_allocator& operator=(static_block_allocator&& other) noexcept
{
static_block_allocator tmp(detail::move(other));
swap(*this, tmp);
return *this;
}
/// @}
/// \effects Swaps the ownership over the \ref static_allocator_storage.
/// This does not invalidate any memory blocks.
friend void swap(static_block_allocator& a, static_block_allocator& b) noexcept
{
detail::adl_swap(a.cur_, b.cur_);
detail::adl_swap(a.end_, b.end_);
detail::adl_swap(a.block_size_, b.block_size_);
}
/// \effects Allocates a new block by returning the \ref next_block_size() bytes.
/// \returns The new memory block.
memory_block allocate_block();
/// \effects Deallocates the last memory block by marking the block as free again.
/// This block will be returned again by the next call to \ref allocate_block().
/// \requires \c block must be the current top block of the memory,
/// this is guaranteed by \ref memory_arena.
void deallocate_block(memory_block block) noexcept;
/// \returns The next block size, this is the size passed to the constructor.
std::size_t next_block_size() const noexcept
{
return block_size_;
}
private:
allocator_info info() const noexcept;
char * cur_, *end_;
std::size_t block_size_;
};
} // namespace memory
} // namespace foonathan
#endif //FOONATHAN_MEMORY_STATIC_ALLOCATOR_HPP_INCLUDED
|