#![doc = include_str!("README.md")]
use std::cell::UnsafeCell;
use std::mem;
use std::ptr;
pub struct Arena {
buffer: Box<[u8]>,
offset: UnsafeCell<usize>,
}
impl Arena {
pub fn new(capacity_bytes: usize) -> Self {
let buffer = vec![0u8; capacity_bytes].into_boxed_slice();
Self {
buffer,
offset: UnsafeCell::new(0),
}
}
#[allow(clippy::mut_from_ref)]
pub fn alloc<T>(&self, value: T) -> &mut T {
let size = mem::size_of::<T>();
let align = mem::align_of::<T>();
let current_offset = unsafe { *self.offset.get() };
let padding = (align - (current_offset % align)) % align;
let start = current_offset + padding;
let end = start + size;
if end > self.buffer.len() {
panic!(
"Arena OOM: Capacity {} bytes, requested {} bytes",
self.buffer.len(),
end
);
}
unsafe {
let ptr = self.buffer.as_ptr().add(start) as *mut T;
ptr::write(ptr, value);
*self.offset.get() = end;
&mut *ptr
}
}
pub fn reset(&mut self) {
unsafe {
*self.offset.get() = 0;
}
}
pub fn used_bytes(&self) -> usize {
unsafe { *self.offset.get() }
}
pub fn capacity(&self) -> usize {
self.buffer.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, PartialEq, Clone, Copy)]
struct TradeEvent {
id: u64,
price: f64,
qty: u32,
}
#[test]
fn test_alloc_and_reset() {
let mut arena = Arena::new(1024);
let event1 = arena.alloc(TradeEvent {
id: 1,
price: 100.0,
qty: 10,
});
assert_eq!(event1.id, 1);
event1.price = 101.5;
let event2 = arena.alloc(TradeEvent {
id: 2,
price: 200.0,
qty: 20,
});
assert_eq!(event1.price, 101.5); assert_eq!(event2.id, 2);
arena.reset();
assert_eq!(arena.used_bytes(), 0);
let event3 = arena.alloc(123u64);
assert_eq!(*event3, 123);
}
#[test]
#[should_panic(expected = "Arena OOM")]
fn test_oom() {
let arena = Arena::new(16);
arena.alloc(0u64); arena.alloc(0u64); arena.alloc(0u8); }
}