pbr-cpp-memory-pool 1.1.2
Fixed-block-size O(1) memory pool — C++17 with an ANSI C public surface
Loading...
Searching...
No Matches
pool_allocator.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 Daniel Polo
3
4#ifndef IT_D4NP_MEMORYPOOL_POOL_ALLOCATOR_HPP_
5#define IT_D4NP_MEMORYPOOL_POOL_ALLOCATOR_HPP_
6
28
29#include <cstddef>
30#include <limits>
31#include <new>
32#include <type_traits>
33
34namespace it::d4np::memorypool {
35
69template <typename T>
71public:
72 using value_type = T;
73
74 // Propagation traits — ADR-0018 §4. All three `false`: memory routing
75 // is tied to (n, T) and to the pool a pointer came from, never to
76 // allocator-identity propagation across container copy / move / swap.
77 using propagate_on_container_copy_assignment = std::false_type;
78 using propagate_on_container_move_assignment = std::false_type;
79 using propagate_on_container_swap = std::false_type;
80 // Stateful: two adapters are interchangeable only if they share a pool.
81 using is_always_equal = std::false_type;
82
88 explicit PoolAllocator(Pool& pool) noexcept : pool_(&pool) {}
89
97 template <typename U>
98 PoolAllocator(const PoolAllocator<U>& other) noexcept : pool_(other.pool_) {}
99
108 [[nodiscard]] T* allocate(std::size_t n) {
109 if (routes_to_pool(n)) {
110 void* const block = pool_->try_allocate();
111 if (block == nullptr) {
112 throw std::bad_alloc{};
113 }
114 return static_cast<T*>(block);
115 }
116 return allocate_fallback(n);
117 }
118
124 void deallocate(T* ptr, std::size_t n) noexcept {
125 if (routes_to_pool(n)) {
126 pool_->deallocate(ptr);
127 return;
128 }
129 deallocate_fallback(ptr);
130 }
131
133 template <typename U>
134 [[nodiscard]] bool operator==(const PoolAllocator<U>& rhs) const noexcept {
135 return pool_ == rhs.pool_;
136 }
137
139 template <typename U>
140 [[nodiscard]] bool operator!=(const PoolAllocator<U>& rhs) const noexcept {
141 return !(*this == rhs);
142 }
143
144private:
145 // Rebound specialisations are mutual friends so the converting ctor and
146 // operator== can read each other's private back-reference.
147 template <typename U>
148 friend class PoolAllocator;
149
151 [[nodiscard]] bool routes_to_pool(std::size_t n) const noexcept {
152 return n == 1U && sizeof(T) <= pool_->block_size() && alignof(T) <= alignof(std::max_align_t);
153 }
154
156 [[nodiscard]] static T* allocate_fallback(std::size_t n) {
157 if (n > (std::numeric_limits<std::size_t>::max() / sizeof(T))) {
158 // n * sizeof(T) would overflow size_t — ADR-0009 §3 guard,
159 // applied here to the fallback path.
160 throw std::bad_alloc{};
161 }
162 const std::size_t bytes = n * sizeof(T);
163 // Over-aligned ::operator new so the fallback honours alignof(T)
164 // even for over-aligned T (the case routes_to_pool excludes from
165 // the pool). cppcoreguidelines-owning-memory misreads the returned
166 // storage as a leaked resource; deallocate_fallback closes it.
167 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
168 return static_cast<T*>(::operator new(bytes, std::align_val_t{alignof(T)}));
169 }
170
172 static void deallocate_fallback(T* ptr) noexcept {
173 // Matching aligned ::operator delete for the over-aligned new
174 // above — C++17 [expr.delete] requires the same alignment argument.
175 // The T* -> void* conversion to the deallocation function is
176 // implicit and unambiguous, so no explicit cast is needed.
177 // NOLINTNEXTLINE(cppcoreguidelines-owning-memory)
178 ::operator delete(ptr, std::align_val_t{alignof(T)});
179 }
180
181 Pool* pool_;
182};
183
184} // namespace it::d4np::memorypool
185
186#endif // IT_D4NP_MEMORYPOOL_POOL_ALLOCATOR_HPP_
STL-compatible allocator vending storage from a Pool.
void deallocate(T *ptr, std::size_t n) noexcept
Return storage obtained from allocate.
T * allocate(std::size_t n)
Allocate storage for n objects of T (ADR-0018 §2).
bool operator==(const PoolAllocator< U > &rhs) const noexcept
Two adapters are equal iff they reference the same pool.
bool operator!=(const PoolAllocator< U > &rhs) const noexcept
Negation of operator==.
PoolAllocator(Pool &pool) noexcept
Bind the adapter to pool.
PoolAllocator(const PoolAllocator< U > &other) noexcept
Rebinding converting constructor — required by the Cpp17Allocator requirements so a container can bui...
Owning, non-copyable, move-only wrapper around a memory_pool_t*.
void deallocate(void *block) noexcept
Return a previously allocated block to the pool in O(1).
std::size_t block_size() const noexcept
Report the configured per-block size in bytes (ADR-0018 §3).
void * try_allocate() noexcept
Allocate one block in O(1) — non-throwing verb (ADR-0016 §2).
C++17 RAII wrapper around the C memory pool.