#![deny(missing_docs)]
use std::alloc::Layout;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_void;
use std::ptr::{self, NonNull};
use bumpalo::Bump;
pub trait SendAbility: Sized {
fn create_arena<'a>() -> DynamicArena<'a, Self>;
}
pub struct Sendable {
_marker: (),
}
impl SendAbility for Sendable {
#[inline]
fn create_arena<'a>() -> DynamicArena<'a, Self> {
DynamicArena::new_send()
}
}
pub struct NonSend {
_marker: std::rc::Rc<()>,
}
impl SendAbility for NonSend {
#[inline]
fn create_arena<'a>() -> DynamicArena<'a, Self> {
DynamicArena::new_bounded()
}
}
struct DynamicArenaItem {
drop: unsafe fn(*mut c_void),
value: *mut c_void,
}
impl Drop for DynamicArenaItem {
#[inline]
fn drop(&mut self) {
unsafe { (self.drop)(self.value) }
}
}
unsafe impl Send for DynamicArenaItem {}
pub type DynamicSendArena<'a> = DynamicArena<'a, Sendable>;
pub struct DynamicArena<'a, S = NonSend> {
handle: Bump,
items: RefCell<Vec<DynamicArenaItem>>,
marker: PhantomData<*mut &'a ()>,
send: PhantomData<S>,
}
impl DynamicArena<'static, NonSend> {
#[inline]
pub fn new() -> Self {
DynamicArena::new_bounded()
}
}
impl<'a, S> DynamicArena<'a, S> {
pub fn with_capacity(item_capacity: usize, byte_capacity: usize) -> Self {
DynamicArena {
handle: Bump::with_capacity(byte_capacity),
items: RefCell::new(Vec::with_capacity(item_capacity)),
marker: PhantomData,
send: PhantomData,
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc_copy<T: Copy + Send>(&self, value: T) -> &mut T {
unsafe { self.alloc_unchecked(value) }
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn alloc_unchecked<T>(&self, value: T) -> &mut T {
let ptr = self.alloc_layout(Layout::new::<T>()).as_ptr().cast::<T>();
ptr.write(value);
&mut *ptr
}
#[inline]
pub unsafe fn alloc_layout(&self, layout: Layout) -> NonNull<u8> {
self.handle.alloc_layout(layout)
}
#[inline]
pub unsafe fn dynamic_drop<T>(&self, value: *mut T) {
if mem::needs_drop::<T>() {
self.items.borrow_mut().push(DynamicArenaItem {
drop: mem::transmute::<unsafe fn(*mut T), unsafe fn(*mut c_void)>(
ptr::drop_in_place::<T>,
),
value: value as *mut c_void,
})
}
}
#[inline]
pub fn as_bumpalo(&self) -> &'_ bumpalo::Bump {
&self.handle
}
}
impl<'a> DynamicArena<'a, Sendable> {
pub fn new_send() -> Self {
DynamicArena {
handle: Bump::new(),
items: RefCell::new(Vec::new()),
marker: PhantomData,
send: PhantomData,
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc<T: Send + 'a>(&self, value: T) -> &mut T {
unsafe {
let target = self.alloc_unchecked(value);
self.dynamic_drop(target);
target
}
}
}
impl<'a> DynamicArena<'a, NonSend> {
pub fn new_bounded() -> Self {
DynamicArena {
handle: Bump::new(),
items: RefCell::new(Vec::new()),
marker: PhantomData,
send: PhantomData,
}
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc<T: 'a>(&self, value: T) -> &mut T {
unsafe {
let target = self.alloc_unchecked(value);
self.dynamic_drop(target);
target
}
}
}
impl<'a, S: SendAbility> Default for DynamicArena<'a, S> {
#[inline]
fn default() -> Self {
S::create_arena()
}
}
unsafe impl<'a, S: SendAbility + Send> Send for DynamicArena<'a, S> {}
impl<'a, S> Drop for DynamicArena<'a, S> {
#[inline]
fn drop(&mut self) {
self.items.get_mut().clear();
}
}
#[cfg(test)]
mod test {
use super::*;
use std::cell::Cell;
const EXPECTED_DROP_COUNT: u32 = 4787;
const EXPECTED_DEPTHS: &[u32] = &[5, 27, 43];
struct DropCounted<'a>(&'a Cell<u32>);
impl<'a> Drop for DropCounted<'a> {
fn drop(&mut self) {
let old_count = self.0.get();
self.0.set(old_count + 1);
}
}
#[derive(Copy, Clone)]
pub struct SelfReferential<'a>(u32, Option<&'a SelfReferential<'a>>);
impl<'a> SelfReferential<'a> {
#[inline]
pub fn with_depth(arena: &'a DynamicArena, depth: u32) -> &'a Self {
arena.alloc_copy(match depth {
0 => SelfReferential(depth, None),
_ => SelfReferential(depth, Some(SelfReferential::with_depth(arena, depth - 1))),
})
}
#[inline]
pub fn depth(&self) -> u32 {
match self.1 {
Some(inner) => inner.depth() + 1,
None => 0,
}
}
}
#[test]
fn test_send() {
let arena = DynamicArena::new_send();
verify_copyable(do_copyable(&arena));
::std::thread::spawn(move || {
verify_copyable(do_copyable(&arena));
});
}
#[test]
fn copyable() {
let arena = DynamicArena::new();
for _ in 0..5 {
verify_copyable(do_copyable(&arena));
}
}
#[test]
fn self_referential() {
let arena = DynamicArena::new();
for _ in 0..5 {
verify_self_referential(do_self_referential(&arena));
}
}
#[test]
fn drop_counted() {
let cell = Box::new(Cell::new(0));
let arena = DynamicArena::new_bounded();
{
do_drop_counted(&arena, &cell);
assert_eq!(cell.get(), 0);
}
drop(arena);
assert_eq!(cell.get(), EXPECTED_DROP_COUNT);
}
#[test]
fn mixed() {
let cell = Cell::new(0);
let arena = DynamicArena::new_bounded();
{
do_drop_counted(&arena, &cell);
for _ in 0..5 {
verify_copyable(do_copyable(&arena));
verify_self_referential(do_self_referential(&arena));
}
assert_eq!(cell.get(), 0);
}
drop(arena);
assert_eq!(cell.get(), EXPECTED_DROP_COUNT);
}
fn do_copyable<'a, S>(arena: &'a DynamicArena<S>) -> Vec<&'a u32> {
let mut results = Vec::new();
for i in 0..10 {
results.push(&*arena.alloc_copy(i * 3));
}
results
}
fn verify_copyable(results: Vec<&u32>) {
for (actual, expected) in results.iter().zip(0..10) {
assert_eq!(**actual, expected * 3);
}
}
fn do_drop_counted<'a, 'd: 'a>(arena: &'a DynamicArena<'d>, counter: &'d Cell<u32>) {
for _ in 0..EXPECTED_DROP_COUNT {
arena.alloc(DropCounted(counter));
}
}
fn do_self_referential<'a>(arena: &'a DynamicArena) -> Vec<&'a SelfReferential<'a>> {
let mut result = Vec::new();
for &depth in EXPECTED_DEPTHS {
result.push(SelfReferential::with_depth(arena, depth));
}
result
}
fn verify_self_referential<'a>(results: Vec<&'a SelfReferential<'a>>) {
for (&actual, &depth) in results.iter().zip(EXPECTED_DEPTHS.iter()) {
assert_eq!(actual.depth(), depth);
}
}
}