cadmpeg_core/decode/
arena.rs1use std::cell::RefCell;
5use std::ptr::NonNull;
6
7use super::context::DecodeContext;
8use crate::CodecError;
9
10#[derive(Debug, Default)]
12pub struct DecodeArena {
13 buffers: RefCell<Vec<OwnedBuffer>>,
14}
15
16impl DecodeArena {
17 pub fn new() -> Self {
19 Self::default()
20 }
21
22 pub fn alloc(&self, ctx: &DecodeContext<'_>, bytes: Box<[u8]>) -> Result<&[u8], CodecError> {
26 let mut buffers = self.buffers.borrow_mut();
27 ctx.reserve_vec(&mut buffers, 1, "arena registry")?;
28 let buffer = OwnedBuffer(NonNull::from(Box::leak(bytes)));
29 let pointer = buffer.0;
30 buffers.push(buffer);
31 Ok(unsafe { pointer.as_ref() })
34 }
35}
36
37#[derive(Debug)]
39struct OwnedBuffer(NonNull<[u8]>);
40
41unsafe impl Send for OwnedBuffer {}
45
46impl Drop for OwnedBuffer {
47 fn drop(&mut self) {
48 unsafe { drop(Box::from_raw(self.0.as_ptr())) };
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::DecodeArena;
57 use crate::decode::{DecodeContext, DecodePolicy};
58
59 fn buffer(index: usize) -> Box<[u8]> {
60 let len = (index % 7) + 1;
61 vec![u8::try_from(index).expect("test index fits u8"); len].into_boxed_slice()
62 }
63
64 fn check(index: usize, slice: &[u8]) {
65 assert_eq!(slice.len(), (index % 7) + 1, "length of buffer {index}");
66 assert!(
67 slice
68 .iter()
69 .all(|&byte| byte == u8::try_from(index).expect("test index fits u8")),
70 "contents of buffer {index}",
71 );
72 }
73
74 #[test]
75 fn empty_buffer_borrows_survive_later_allocations() {
76 let arena = DecodeArena::new();
77 let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::default())
78 .expect("empty root");
79 let empty = arena
80 .alloc(&ctx, Vec::new().into_boxed_slice())
81 .expect("admitted registry");
82 let present = arena
83 .alloc(&ctx, vec![7].into_boxed_slice())
84 .expect("admitted registry");
85 assert!(empty.is_empty());
86 assert_eq!(present, &[7]);
87 }
88
89 #[test]
90 fn an_owned_arena_can_move_between_threads() {
91 let arena = DecodeArena::new();
92 {
93 let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::default())
94 .expect("empty root");
95 arena
96 .alloc(&ctx, vec![7].into_boxed_slice())
97 .expect("admitted registry");
98 }
99 let arena = std::thread::spawn(move || {
100 {
101 let (ctx, _) =
102 DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::default())
103 .expect("empty root");
104 assert_eq!(
105 arena
106 .alloc(&ctx, vec![9].into_boxed_slice())
107 .expect("admitted registry"),
108 &[9]
109 );
110 }
111 arena
112 })
113 .join()
114 .expect("the receiving thread retains and reads the arena");
115 let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::default())
116 .expect("empty root");
117 assert_eq!(
118 arena
119 .alloc(&ctx, vec![11].into_boxed_slice())
120 .expect("admitted registry"),
121 &[11]
122 );
123 }
124
125 #[test]
128 fn interleaved_alloc_and_read_across_many_buffers() {
129 let arena = DecodeArena::new();
130 let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &DecodePolicy::default())
131 .expect("empty root");
132 let mut borrows: Vec<&[u8]> = Vec::new();
133 for index in 0..256usize {
134 borrows.push(arena.alloc(&ctx, buffer(index)).expect("admitted registry"));
135 for (i, slice) in borrows.iter().enumerate() {
136 check(i, slice);
137 }
138 }
139 }
140}