use alloc_crate::alloc::{Layout, alloc, alloc_zeroed};
use alloc_crate::boxed::Box;
use alloc_crate::vec::Vec;
use crate::error::{Error, ErrorKind, Result};
pub struct Arena {
pub(crate) buf: Vec<u8>,
}
impl Arena {
pub fn try_new(bytes: u64) -> Result<Self> {
Ok(Self {
buf: try_zeroed(bytes)?,
})
}
pub fn capacity(&self) -> u64 {
self.buf.capacity() as u64
}
pub(crate) const fn empty() -> Self {
Self { buf: Vec::new() }
}
}
impl core::fmt::Debug for Arena {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Arena")
.field("capacity", &self.capacity())
.finish()
}
}
#[cold]
fn alloc_failed(bytes: u64) -> Error {
Error::new(ErrorKind::AllocationFailed { bytes })
}
pub(crate) fn try_zeroed(bytes: u64) -> Result<Vec<u8>> {
let len = usize::try_from(bytes).map_err(|_| alloc_failed(bytes))?;
if len == 0 {
return Ok(Vec::new());
}
let layout = Layout::array::<u8>(len).map_err(|_| alloc_failed(bytes))?;
let ptr = unsafe { alloc_zeroed(layout) };
if ptr.is_null() {
return Err(alloc_failed(bytes));
}
Ok(unsafe { Vec::from_raw_parts(ptr, len, len) })
}
pub(crate) fn fit(buf: &mut Vec<u8>, len: usize) -> Result<()> {
if buf.capacity() >= len {
if buf.len() >= len {
buf.truncate(len);
} else {
buf.resize(len, 0);
}
return Ok(());
}
let fresh = try_zeroed(len as u64)?;
*buf = fresh;
Ok(())
}
pub(crate) fn try_box<T>(value: T) -> Result<Box<T>> {
let layout = Layout::new::<T>();
if layout.size() == 0 {
return Ok(Box::new(value));
}
let ptr = unsafe { alloc(layout) }.cast::<T>();
if ptr.is_null() {
return Err(alloc_failed(layout.size() as u64));
}
unsafe {
ptr.write(value);
Ok(Box::from_raw(ptr))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_refused_size_is_an_error_not_an_abort() {
let e = Arena::try_new(u64::MAX).unwrap_err();
assert_eq!(e.kind, ErrorKind::AllocationFailed { bytes: u64::MAX });
let e = Arena::try_new(isize::MAX as u64 + 1).unwrap_err();
assert!(matches!(e.kind, ErrorKind::AllocationFailed { .. }));
}
#[test]
fn arenas_start_zeroed_and_fit_reuses_capacity() {
let arena = Arena::try_new(4096).unwrap();
assert_eq!(arena.capacity(), 4096);
assert!(arena.buf.iter().all(|&b| b == 0));
let mut buf = arena.buf;
let addr = buf.as_ptr();
fit(&mut buf, 1000).unwrap();
assert_eq!((buf.len(), buf.as_ptr()), (1000, addr));
fit(&mut buf, 4096).unwrap();
assert_eq!((buf.len(), buf.as_ptr()), (4096, addr));
fit(&mut buf, 8192).unwrap();
assert_eq!(buf.len(), 8192);
assert!(Arena::try_new(0).unwrap().capacity() == 0);
}
}