TPDE
Loading...
Searching...
No Matches
BumpAllocator.hpp
1// SPDX-FileCopyrightText: 2025 Contributors to TPDE <https://tpde.org>
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3#pragma once
4
5#include "tpde/util/AddressSanitizer.hpp"
6#include "tpde/util/SmallVector.hpp"
7#include "tpde/util/misc.hpp"
8
9#include <algorithm>
10#include <cstddef>
11#include <new>
12
13namespace tpde::util {
14
15template <size_t SlabSize = 4096>
16class BumpAllocator {
17 uintptr_t cur = 0;
18 uintptr_t end = 0;
19 SmallVector<void *> slabs;
20 SmallVector<std::pair<void *, size_t>, 0> large_slabs;
21
22 static constexpr auto SLAB_ALIGNMENT =
23 std::align_val_t(alignof(std::max_align_t));
24
25 /// Slab size for the i-th slab.
26 static constexpr size_t slab_size(size_t i) {
27 // Double slab size every 8 slabs.
28 return SlabSize << (i / 8);
29 }
30
31public:
32 BumpAllocator() = default;
33 ~BumpAllocator() noexcept {
34 deallocate_slabs();
35 deallocate_large_slabs();
36 }
37
38 BumpAllocator(const BumpAllocator &) = delete;
39
40 BumpAllocator &operator=(const BumpAllocator &) = delete;
41
42 /// Deallocate all but the first slab and reset current pointer to beginning.
43 void reset() noexcept {
44 if (!slabs.empty()) {
45 deallocate_slabs(1);
46 slabs.resize(1);
47 util::poison_memory_region(slabs[0], slab_size(0));
48 cur = reinterpret_cast<uintptr_t>(slabs[0]);
49 end = cur + slab_size(0);
50 }
51 deallocate_large_slabs();
52 }
53
54 void *allocate(size_t size, size_t align) noexcept {
55 assert(size > 0 && "cannot perform zero-sized allocation");
56 uintptr_t aligned = align_up(cur, align);
57 uintptr_t alloc_end = aligned + size;
58 if (alloc_end <= end) [[likely]] {
59 cur = alloc_end;
60 util::unpoison_memory_region(reinterpret_cast<void *>(aligned), size);
61 return reinterpret_cast<void *>(aligned);
62 }
63 return allocate_slab(size, align);
64 }
65
66 void *allocate_slab(size_t size, [[maybe_unused]] size_t align) noexcept {
67 assert(align <= alignof(std::max_align_t) && "alignment type unsupported");
68 size_t slab_sz = slab_size(slabs.size());
69 if (size > slab_sz) [[unlikely]] {
70 void *slab = allocate_mem(size, SLAB_ALIGNMENT);
71 large_slabs.emplace_back(slab, size);
72 return slab;
73 }
74
75 void *slab = allocate_mem(slab_sz, SLAB_ALIGNMENT);
76 util::poison_memory_region(slab, slab_sz);
77 slabs.push_back(slab);
78 cur = reinterpret_cast<uintptr_t>(slab) + size;
79 end = reinterpret_cast<uintptr_t>(slab) + slab_sz;
80 util::unpoison_memory_region(slab, size);
81 return slab;
82 }
83
84private:
85 void *allocate_mem(size_t size, std::align_val_t align) noexcept {
86 return ::operator new(size, align, std::nothrow);
87 }
88
89 void deallocate_mem(void *ptr,
90 [[maybe_unused]] size_t size,
91 std::align_val_t align) noexcept {
92#ifdef __cpp_sized_deallocation
93 ::operator delete(ptr, size, align);
94#else
95 ::operator delete(ptr, align);
96#endif
97 }
98
99 void deallocate_slabs(size_t skip = 0) noexcept {
100 for (size_t i = skip; i < slabs.size(); ++i) {
101 deallocate_mem(slabs[i], slab_size(i), SLAB_ALIGNMENT);
102 }
103 }
104
105 void deallocate_large_slabs() noexcept {
106 for (auto [slab, size] : large_slabs) {
107 deallocate_mem(slab, size, SLAB_ALIGNMENT);
108 }
109 }
110};
111
112template <typename T>
113struct BumpAllocatorDeleter {
114 constexpr BumpAllocatorDeleter() noexcept = default;
115 void operator()(T *ptr) const { std::destroy_at(ptr); }
116};
117
118template <typename T>
119using BumpAllocUniquePtr = std::unique_ptr<T, BumpAllocatorDeleter<T>>;
120
121} // namespace tpde::util
122
123template <size_t SlabSize>
124void *operator new(size_t s, tpde::util::BumpAllocator<SlabSize> &a) noexcept {
125 return a.allocate(s, std::min(s, alignof(std::max_align_t)));
126}
127
128template <size_t SlabSize>
129void *operator new[](size_t s,
130 tpde::util::BumpAllocator<SlabSize> &a) noexcept {
131 return a.allocate(s, std::min(s, alignof(std::max_align_t)));
132}
133
134template <size_t SlabSize>
135void operator delete(void *, tpde::util::BumpAllocator<SlabSize> &) noexcept {}
136
137template <size_t SlabSize>
138void operator delete[](void *, tpde::util::BumpAllocator<SlabSize> &) noexcept {
139}