graphos_common/memory/
bump.rs1use std::cell::Cell;
8use std::ptr::NonNull;
9
10pub struct BumpAllocator {
17 inner: bumpalo::Bump,
19 allocation_count: Cell<usize>,
21}
22
23impl BumpAllocator {
24 #[must_use]
26 pub fn new() -> Self {
27 Self {
28 inner: bumpalo::Bump::new(),
29 allocation_count: Cell::new(0),
30 }
31 }
32
33 #[must_use]
35 pub fn with_capacity(capacity: usize) -> Self {
36 Self {
37 inner: bumpalo::Bump::with_capacity(capacity),
38 allocation_count: Cell::new(0),
39 }
40 }
41
42 #[inline]
44 pub fn alloc<T>(&self, value: T) -> &mut T {
45 self.allocation_count.set(self.allocation_count.get() + 1);
46 self.inner.alloc(value)
47 }
48
49 #[inline]
51 pub fn alloc_slice_copy<T: Copy>(&self, values: &[T]) -> &mut [T] {
52 self.allocation_count.set(self.allocation_count.get() + 1);
53 self.inner.alloc_slice_copy(values)
54 }
55
56 #[inline]
58 pub fn alloc_slice_clone<T: Clone>(&self, values: &[T]) -> &mut [T] {
59 self.allocation_count.set(self.allocation_count.get() + 1);
60 self.inner.alloc_slice_clone(values)
61 }
62
63 #[inline]
65 pub fn alloc_str(&self, s: &str) -> &mut str {
66 self.allocation_count.set(self.allocation_count.get() + 1);
67 self.inner.alloc_str(s)
68 }
69
70 #[inline]
72 pub fn alloc_layout(&self, layout: std::alloc::Layout) -> NonNull<u8> {
73 self.allocation_count.set(self.allocation_count.get() + 1);
74 self.inner.alloc_layout(layout)
75 }
76
77 #[inline]
81 pub fn reset(&mut self) {
82 self.inner.reset();
83 self.allocation_count.set(0);
84 }
85
86 #[must_use]
88 #[inline]
89 pub fn allocated_bytes(&self) -> usize {
90 self.inner.allocated_bytes()
91 }
92
93 #[must_use]
95 #[inline]
96 pub fn allocation_count(&self) -> usize {
97 self.allocation_count.get()
98 }
99
100 #[must_use]
102 #[inline]
103 pub fn chunk_count(&mut self) -> usize {
104 self.inner.iter_allocated_chunks().count()
105 }
106}
107
108impl Default for BumpAllocator {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114pub struct ScopedBump<'a> {
118 bump: &'a mut BumpAllocator,
119 start_bytes: usize,
120}
121
122impl<'a> ScopedBump<'a> {
123 pub fn new(bump: &'a mut BumpAllocator) -> Self {
125 let start_bytes = bump.allocated_bytes();
126 Self { bump, start_bytes }
127 }
128
129 #[inline]
131 pub fn alloc<T>(&self, value: T) -> &mut T {
132 self.bump.alloc(value)
133 }
134
135 #[must_use]
137 pub fn scope_allocated_bytes(&self) -> usize {
138 self.bump.allocated_bytes() - self.start_bytes
139 }
140}
141
142impl Drop for ScopedBump<'_> {
143 fn drop(&mut self) {
144 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_bump_basic_allocation() {
156 let bump = BumpAllocator::new();
157
158 let a = bump.alloc(42u64);
159 assert_eq!(*a, 42);
160
161 let b = bump.alloc(String::from("hello"));
162 assert_eq!(b.as_str(), "hello");
163 }
164
165 #[test]
166 fn test_bump_slice_allocation() {
167 let bump = BumpAllocator::new();
168
169 let slice = bump.alloc_slice_copy(&[1, 2, 3, 4, 5]);
170 assert_eq!(slice, &[1, 2, 3, 4, 5]);
171
172 slice[0] = 10;
173 assert_eq!(slice[0], 10);
174 }
175
176 #[test]
177 fn test_bump_string_allocation() {
178 let bump = BumpAllocator::new();
179
180 let s = bump.alloc_str("hello world");
181 assert_eq!(s, "hello world");
182 }
183
184 #[test]
185 fn test_bump_reset() {
186 let mut bump = BumpAllocator::new();
187
188 for _ in 0..100 {
189 bump.alloc(42u64);
190 }
191
192 let bytes_before = bump.allocated_bytes();
193 assert!(bytes_before > 0);
194 assert_eq!(bump.allocation_count(), 100);
195
196 bump.reset();
197
198 assert_eq!(bump.allocation_count(), 0);
200 }
201
202 #[test]
203 fn test_bump_with_capacity() {
204 let bump = BumpAllocator::with_capacity(1024);
205 assert_eq!(bump.allocation_count(), 0);
206
207 for _ in 0..10 {
209 bump.alloc(42u64);
210 }
211
212 assert_eq!(bump.allocation_count(), 10);
213 }
214
215 #[test]
216 fn test_scoped_bump() {
217 let mut bump = BumpAllocator::new();
218
219 bump.alloc(1u64);
220 let outer_allocs = bump.allocation_count();
221
222 {
223 let scope = ScopedBump::new(&mut bump);
224 scope.alloc(2u64);
225 scope.alloc(3u64);
226
227 }
231
232 assert_eq!(bump.allocation_count(), outer_allocs + 2);
234 }
235
236 #[test]
237 fn test_bump_many_small_allocations() {
238 let bump = BumpAllocator::new();
239
240 for i in 0u64..10_000u64 {
241 let v = bump.alloc(i);
242 assert_eq!(*v, i);
243 }
244
245 assert_eq!(bump.allocation_count(), 10_000);
246 }
247}