use std::cell::RefCell;
use std::ptr::NonNull;
#[derive(Debug, Default)]
pub struct DecodeArena {
buffers: RefCell<Vec<OwnedBuffer>>,
}
impl DecodeArena {
pub fn new() -> Self {
Self::default()
}
pub fn alloc(&self, bytes: Box<[u8]>) -> &[u8] {
let mut buffers = self.buffers.borrow_mut();
let buffer = OwnedBuffer(NonNull::from(Box::leak(bytes)));
let pointer = buffer.0;
buffers.push(buffer);
unsafe { pointer.as_ref() }
}
}
#[derive(Debug)]
struct OwnedBuffer(NonNull<[u8]>);
unsafe impl Send for OwnedBuffer {}
impl Drop for OwnedBuffer {
fn drop(&mut self) {
unsafe { drop(Box::from_raw(self.0.as_ptr())) };
}
}
#[cfg(test)]
mod tests {
use super::DecodeArena;
fn buffer(index: usize) -> Box<[u8]> {
let len = (index % 7) + 1;
vec![u8::try_from(index).expect("test index fits u8"); len].into_boxed_slice()
}
fn check(index: usize, slice: &[u8]) {
assert_eq!(slice.len(), (index % 7) + 1, "length of buffer {index}");
assert!(
slice
.iter()
.all(|&byte| byte == u8::try_from(index).expect("test index fits u8")),
"contents of buffer {index}",
);
}
#[test]
fn empty_buffer_borrows_survive_later_allocations() {
let arena = DecodeArena::new();
let empty = arena.alloc(Vec::new().into_boxed_slice());
let present = arena.alloc(vec![7].into_boxed_slice());
assert!(empty.is_empty());
assert_eq!(present, &[7]);
}
#[test]
fn an_owned_arena_can_move_between_threads() {
let arena = DecodeArena::new();
{
arena.alloc(vec![7].into_boxed_slice());
}
let arena = std::thread::spawn(move || {
{
assert_eq!(arena.alloc(vec![9].into_boxed_slice()), &[9]);
}
arena
})
.join()
.expect("the receiving thread retains and reads the arena");
assert_eq!(arena.alloc(vec![11].into_boxed_slice()), &[11]);
}
#[test]
fn interleaved_alloc_and_read_across_many_buffers() {
let arena = DecodeArena::new();
let mut borrows: Vec<&[u8]> = Vec::new();
for index in 0..256usize {
borrows.push(arena.alloc(buffer(index)));
for (i, slice) in borrows.iter().enumerate() {
check(i, slice);
}
}
}
}