1use bumpalo::Bump;
29
30pub const DEFAULT_ARENA_CAPACITY: usize = 256 * 1024;
32
33#[derive(Debug)]
46pub struct FrameArena {
47 bump: Bump,
48}
49
50impl FrameArena {
51 pub fn new(capacity: usize) -> Self {
57 Self {
58 bump: Bump::with_capacity(capacity),
59 }
60 }
61
62 pub fn with_default_capacity() -> Self {
64 Self::new(DEFAULT_ARENA_CAPACITY)
65 }
66
67 pub fn reset(&mut self) {
73 self.bump.reset();
74 }
75
76 pub fn alloc_str(&self, s: &str) -> &str {
81 self.bump.alloc_str(s)
82 }
83
84 pub fn alloc_slice<T: Copy>(&self, slice: &[T]) -> &[T] {
89 self.bump.alloc_slice_copy(slice)
90 }
91
92 pub fn alloc_with<T, F: FnOnce() -> T>(&self, f: F) -> &mut T {
97 self.bump.alloc_with(f)
98 }
99
100 pub fn alloc<T>(&self, val: T) -> &mut T {
105 self.bump.alloc(val)
106 }
107
108 pub fn allocated_bytes(&self) -> usize {
110 self.bump.allocated_bytes()
111 }
112
113 pub fn allocated_bytes_including_metadata(&self) -> usize {
115 self.bump.allocated_bytes_including_metadata()
116 }
117
118 pub fn as_bump(&self) -> &Bump {
123 &self.bump
124 }
125}
126
127impl Default for FrameArena {
128 fn default() -> Self {
129 Self::with_default_capacity()
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn new_creates_arena_with_capacity() {
139 let arena = FrameArena::new(1024);
140 let _s = arena.alloc_str("hello");
142 }
143
144 #[test]
145 fn default_uses_256kb() {
146 let arena = FrameArena::default();
147 let _s = arena.alloc_str("test");
148 }
149
150 #[test]
151 fn alloc_str_returns_correct_content() {
152 let arena = FrameArena::new(4096);
153 let s = arena.alloc_str("hello, world!");
154 assert_eq!(s, "hello, world!");
155 }
156
157 #[test]
158 fn alloc_str_empty() {
159 let arena = FrameArena::new(4096);
160 let s = arena.alloc_str("");
161 assert_eq!(s, "");
162 }
163
164 #[test]
165 fn alloc_str_unicode() {
166 let arena = FrameArena::new(4096);
167 let s = arena.alloc_str("こんにちは 🎉");
168 assert_eq!(s, "こんにちは 🎉");
169 }
170
171 #[test]
172 fn alloc_slice_copies_correctly() {
173 let arena = FrameArena::new(4096);
174 let data = [1u32, 2, 3, 4, 5];
175 let slice = arena.alloc_slice(&data);
176 assert_eq!(slice, &[1, 2, 3, 4, 5]);
177 }
178
179 #[test]
180 fn alloc_slice_empty() {
181 let arena = FrameArena::new(4096);
182 let slice: &[u8] = arena.alloc_slice(&[]);
183 assert!(slice.is_empty());
184 }
185
186 #[test]
187 fn alloc_slice_u8() {
188 let arena = FrameArena::new(4096);
189 let data = b"ANSI escape";
190 let slice = arena.alloc_slice(data.as_slice());
191 assert_eq!(slice, b"ANSI escape");
192 }
193
194 #[test]
195 fn alloc_with_constructs_value() {
196 let arena = FrameArena::new(4096);
197 let val = arena.alloc_with(|| 42u64);
198 assert_eq!(*val, 42);
199 }
200
201 #[test]
202 fn alloc_returns_mutable_ref() {
203 let arena = FrameArena::new(4096);
204 let val = arena.alloc(100i32);
205 assert_eq!(*val, 100);
206 *val = 200;
207 assert_eq!(*val, 200);
208 }
209
210 #[test]
211 fn reset_allows_reuse() {
212 let mut arena = FrameArena::new(4096);
213 let _s1 = arena.alloc_str("first frame data");
214 let bytes_before = arena.allocated_bytes();
215 assert!(bytes_before > 0);
216
217 arena.reset();
218
219 let _s2 = arena.alloc_str("second frame data");
221 }
222
223 #[test]
224 fn multiple_allocations_coexist() {
225 let arena = FrameArena::new(4096);
226 let s1 = arena.alloc_str("hello");
227 let s2 = arena.alloc_str("world");
228 let slice = arena.alloc_slice(&[1u32, 2, 3]);
229 let val = arena.alloc(42u64);
230
231 assert_eq!(s1, "hello");
233 assert_eq!(s2, "world");
234 assert_eq!(slice, &[1, 2, 3]);
235 assert_eq!(*val, 42);
236 }
237
238 #[test]
239 fn arena_grows_beyond_initial_capacity() {
240 let arena = FrameArena::new(64); let large = "a]".repeat(100);
243 let s = arena.alloc_str(&large);
244 assert_eq!(s, large);
245 }
246
247 #[test]
248 fn allocated_bytes_tracks_usage() {
249 let arena = FrameArena::new(4096);
250 let initial = arena.allocated_bytes();
251 let _s = arena.alloc_str("some text for tracking");
252 assert!(arena.allocated_bytes() >= initial);
253 }
254
255 #[test]
256 fn as_bump_provides_access() {
257 let arena = FrameArena::new(4096);
258 let bump = arena.as_bump();
259 let val = bump.alloc(99u32);
261 assert_eq!(*val, 99);
262 }
263
264 #[test]
265 fn reset_then_heavy_reuse() {
266 let mut arena = FrameArena::new(4096);
267 for frame in 0..100 {
268 let s = arena.alloc_str(&format!("frame {frame}"));
269 assert!(s.starts_with("frame "));
270 let data: Vec<u32> = (0..50).collect();
271 let slice = arena.alloc_slice(&data);
272 assert_eq!(slice.len(), 50);
273 arena.reset();
274 }
275 }
276
277 #[test]
278 fn debug_impl() {
279 let arena = FrameArena::new(1024);
280 let debug = format!("{arena:?}");
281 assert!(debug.contains("FrameArena"));
282 }
283}