use alloc::vec::Vec;
#[derive(Debug, Default, Clone)]
pub struct ByteArena {
data: Vec<u8>,
cursor: usize,
}
impl ByteArena {
#[must_use]
pub const fn new() -> Self {
Self {
data: Vec::new(),
cursor: 0,
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: alloc::vec![0; capacity],
cursor: 0,
}
}
pub fn append(&mut self, bytes: &[u8]) -> u32 {
let offset = self.cursor;
let end = offset + bytes.len();
assert!(
end <= u32::MAX as usize,
"byte arena exceeded u32 offset space"
);
if end > self.data.len() {
self.data.resize(end.next_power_of_two(), 0);
}
self.data[offset..end].copy_from_slice(bytes);
self.cursor = end;
offset as u32
}
#[must_use]
pub fn read(&self, offset: u32, len: u32) -> &[u8] {
let start = offset as usize;
&self.data[start..start + len as usize]
}
pub fn reset(&mut self) {
self.cursor = 0;
}
#[must_use]
pub fn bytes_used(&self) -> usize {
self.cursor
}
#[must_use]
pub fn capacity(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.cursor == 0
}
}
#[cfg(test)]
mod tests {
use super::ByteArena;
#[test]
fn append_returns_offsets_and_reads_back_each_payload() {
let mut arena = ByteArena::new();
let first = arena.append(b"hello");
let second = arena.append(b"world!");
assert_eq!(first, 0);
assert_eq!(second, 5);
assert_eq!(arena.read(first, 5), b"hello");
assert_eq!(arena.read(second, 6), b"world!");
assert_eq!(arena.bytes_used(), 11);
}
#[test]
fn grows_power_of_two_and_preserves_prior_payloads() {
let mut arena = ByteArena::with_capacity(4);
let a = arena.append(b"ab");
let b = arena.append(b"cdef"); assert_eq!(arena.read(a, 2), b"ab");
assert_eq!(arena.read(b, 4), b"cdef");
assert!(arena.capacity().is_power_of_two());
assert!(arena.capacity() >= 6);
}
#[test]
fn reset_rewinds_cursor_and_keeps_capacity() {
let mut arena = ByteArena::new();
arena.append(b"some bytes");
let cap = arena.capacity();
arena.reset();
assert!(arena.is_empty());
assert_eq!(arena.bytes_used(), 0);
assert_eq!(arena.capacity(), cap, "reset retains the allocation");
assert_eq!(arena.append(b"x"), 0);
}
}