cadmpeg_core/decode/arena.rs
1// SPDX-License-Identifier: Apache-2.0
2//! Append-only byte store with stable slice addresses.
3
4use std::cell::RefCell;
5
6/// Owns stable byte buffers allocated during a decode.
7#[derive(Debug, Default)]
8pub struct DecodeArena {
9 buffers: RefCell<Vec<Box<[u8]>>>,
10}
11
12impl DecodeArena {
13 /// Creates an empty arena.
14 pub fn new() -> Self {
15 Self::default()
16 }
17
18 /// Stores `bytes` and returns a borrow valid for the arena's lifetime.
19 ///
20 /// The returned slice is stable: later `alloc` calls never invalidate it.
21 pub fn alloc(&self, bytes: Box<[u8]>) -> &[u8] {
22 let mut buffers = self.buffers.borrow_mut();
23 buffers.push(bytes);
24 let slice: &[u8] = buffers.last().expect("a buffer was just pushed").as_ref();
25 // SAFETY: three legs keep the returned `&[u8]` valid:
26 // 1. Each buffer is a `Box<[u8]>`; its heap address is stable when
27 // the outer `Vec` reallocates (only the box pointers move).
28 // 2. `DecodeArena` is `!Sync` (`RefCell`), so no concurrent mutation
29 // can invalidate or alias the stored bytes across threads.
30 // 3. The returned slice is tied to `&self`; it cannot outlive the
31 // arena that owns the boxes.
32 // The bytes are never mutated after being stored (the arena exposes
33 // no mutation), so aliasing `&[u8]` borrows do not conflict.
34 unsafe { std::slice::from_raw_parts(slice.as_ptr(), slice.len()) }
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::DecodeArena;
41
42 fn buffer(index: usize) -> Box<[u8]> {
43 let len = (index % 7) + 1;
44 vec![index as u8; len].into_boxed_slice()
45 }
46
47 fn check(index: usize, slice: &[u8]) {
48 assert_eq!(slice.len(), (index % 7) + 1, "length of buffer {index}");
49 assert!(
50 slice.iter().all(|&byte| byte == index as u8),
51 "contents of buffer {index}",
52 );
53 }
54
55 /// Alloc while earlier borrows stay live: each iteration allocates, then
56 /// re-reads every prior slice.
57 #[test]
58 fn interleaved_alloc_and_read_across_many_buffers() {
59 let arena = DecodeArena::new();
60 let mut borrows: Vec<&[u8]> = Vec::new();
61 for index in 0..256usize {
62 borrows.push(arena.alloc(buffer(index)));
63 for (i, slice) in borrows.iter().enumerate() {
64 check(i, slice);
65 }
66 }
67 }
68}