use core::alloc::Layout;
use core::fmt;
use core::ptr::NonNull;
use crate::alloc::AllocError;
use crate::alloc::Allocator;
use crate::alloc::Global;
use crate::boxed::Box;
use crate::vec::Vec;
pub struct LocalArena<A: Allocator = Global>(blink_alloc::BlinkAlloc<A>);
pub struct SharedArena<A: Allocator = Global>(blink_alloc::SyncBlinkAlloc<A>);
pub struct ScopedArena<'shared, A: Allocator = Global>(blink_alloc::LocalBlinkAlloc<'shared, A>);
impl LocalArena<Global> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self::new_in(Global)
}
#[inline]
#[must_use]
pub const fn with_chunk_size(chunk_size: usize) -> Self {
Self::with_chunk_size_in(chunk_size, Global)
}
}
impl<A: Allocator> LocalArena<A> {
#[inline]
#[must_use]
pub const fn new_in(arena: A) -> Self {
Self(blink_alloc::BlinkAlloc::new_in(arena))
}
#[inline]
#[must_use]
pub const fn with_chunk_size_in(chunk_size: usize, arena: A) -> Self {
Self(blink_alloc::BlinkAlloc::with_chunk_size_in(chunk_size, arena))
}
#[inline]
pub fn reset(&mut self) {
self.0.reset();
}
}
impl Default for LocalArena {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl SharedArena<Global> {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self::new_in(Global)
}
#[inline]
#[must_use]
pub const fn with_chunk_size(chunk_size: usize) -> Self {
Self::with_chunk_size_in(chunk_size, Global)
}
}
impl<A: Allocator> SharedArena<A> {
#[inline]
#[must_use]
pub const fn new_in(arena: A) -> Self {
Self(blink_alloc::SyncBlinkAlloc::new_in(arena))
}
#[inline]
#[must_use]
pub const fn with_chunk_size_in(chunk_size: usize, arena: A) -> Self {
Self(blink_alloc::SyncBlinkAlloc::with_chunk_size_in(chunk_size, arena))
}
#[inline]
pub fn scoped(&self) -> ScopedArena<'_, A> {
ScopedArena(self.0.local())
}
#[inline]
pub fn reset(&mut self) {
self.0.reset();
}
}
impl Default for SharedArena {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<A: Allocator> ScopedArena<'_, A> {
#[inline]
pub fn reset(&mut self) {
self.0.reset();
}
}
macro_rules! forward_allocator {
() => {
#[inline]
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Allocator::allocate(&self.0, layout)
}
#[inline]
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Allocator::allocate_zeroed(&self.0, layout)
}
#[inline]
unsafe fn deallocate(&self, pointer: NonNull<u8>, layout: Layout) {
unsafe { Allocator::deallocate(&self.0, pointer, layout) }
}
#[inline]
unsafe fn grow(
&self,
pointer: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
unsafe { Allocator::grow(&self.0, pointer, old_layout, new_layout) }
}
#[inline]
unsafe fn grow_zeroed(
&self,
pointer: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
unsafe { Allocator::grow_zeroed(&self.0, pointer, old_layout, new_layout) }
}
#[inline]
unsafe fn shrink(
&self,
pointer: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
unsafe { Allocator::shrink(&self.0, pointer, old_layout, new_layout) }
}
};
}
unsafe impl<A: Allocator> Allocator for LocalArena<A> {
forward_allocator!();
}
unsafe impl<A: Allocator> Allocator for SharedArena<A> {
forward_allocator!();
}
unsafe impl<A: Allocator> Allocator for ScopedArena<'_, A> {
forward_allocator!();
}
struct ArenaFormatter<'buffer, 'arena, A: Allocator + ?Sized> {
buffer: &'buffer mut Vec<'arena, u8, A>,
}
impl<A: Allocator + ?Sized> fmt::Write for ArenaFormatter<'_, '_, A> {
#[inline]
fn write_str(&mut self, fragment: &str) -> fmt::Result {
self.buffer.extend_from_slice(fragment.as_bytes());
Ok(())
}
}
pub trait Arena: Allocator {
fn alloc<T>(&self, value: T) -> &mut T;
fn alloc_with<T>(&self, f: impl FnOnce() -> T) -> &mut T;
fn alloc_str(&self, src: &str) -> &mut str;
fn alloc_fmt(&self, arguments: fmt::Arguments<'_>) -> &mut str;
fn alloc_slice_copy<T>(&self, src: &[T]) -> &mut [T]
where
T: Copy;
fn alloc_slice_clone<T>(&self, src: &[T]) -> &mut [T]
where
T: Clone;
fn alloc_slice_fill_copy<T>(&self, len: usize, value: T) -> &mut [T]
where
T: Copy;
fn alloc_slice_fill_clone<T>(&self, len: usize, value: &T) -> &mut [T]
where
T: Clone;
fn alloc_slice_fill_with<T>(&self, len: usize, f: impl FnMut(usize) -> T) -> &mut [T];
fn alloc_slice_fill_default<T>(&self, len: usize) -> &mut [T]
where
T: Default;
fn alloc_slice_fill_iter<T>(&self, iter: impl IntoIterator<Item = T>) -> &mut [T];
}
impl<A: Allocator + ?Sized> Arena for A {
#[inline]
fn alloc<T>(&self, value: T) -> &mut T {
Box::leak(Box::new_in(value, self))
}
#[inline]
fn alloc_with<T>(&self, f: impl FnOnce() -> T) -> &mut T {
Box::leak(Box::new_in(f(), self))
}
#[inline]
fn alloc_str(&self, src: &str) -> &mut str {
let bytes = self.alloc_slice_copy(src.as_bytes());
unsafe { core::str::from_utf8_unchecked_mut(bytes) }
}
#[inline]
fn alloc_fmt(&self, arguments: fmt::Arguments<'_>) -> &mut str {
let mut buffer = Vec::new_in(self);
let mut formatter = ArenaFormatter { buffer: &mut buffer };
let _ = fmt::write(&mut formatter, arguments);
let bytes = buffer.leak();
unsafe { core::str::from_utf8_unchecked_mut(bytes) }
}
#[inline]
fn alloc_slice_copy<T>(&self, src: &[T]) -> &mut [T]
where
T: Copy,
{
let mut vec = Vec::with_capacity_in(src.len(), self);
vec.extend_from_slice(src);
vec.leak()
}
#[inline]
fn alloc_slice_clone<T>(&self, src: &[T]) -> &mut [T]
where
T: Clone,
{
let mut vec = Vec::with_capacity_in(src.len(), self);
vec.extend(src.iter().cloned());
vec.leak()
}
#[inline]
fn alloc_slice_fill_copy<T>(&self, len: usize, value: T) -> &mut [T]
where
T: Copy,
{
let mut vec = Vec::with_capacity_in(len, self);
vec.resize(len, value);
vec.leak()
}
#[inline]
fn alloc_slice_fill_clone<T>(&self, len: usize, value: &T) -> &mut [T]
where
T: Clone,
{
let mut vec = Vec::with_capacity_in(len, self);
vec.resize_with(len, || value.clone());
vec.leak()
}
#[inline]
fn alloc_slice_fill_with<T>(&self, len: usize, mut f: impl FnMut(usize) -> T) -> &mut [T] {
let mut vec = Vec::with_capacity_in(len, self);
for index in 0..len {
vec.push(f(index));
}
vec.leak()
}
#[inline]
fn alloc_slice_fill_default<T>(&self, len: usize) -> &mut [T]
where
T: Default,
{
self.alloc_slice_fill_with(len, |_| T::default())
}
#[inline]
fn alloc_slice_fill_iter<T>(&self, iter: impl IntoIterator<Item = T>) -> &mut [T] {
let mut vec = Vec::new_in(self);
vec.extend(iter);
vec.leak()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::collections::HashMap;
use crate::vec::Vec;
#[test]
fn local_alloc_roundtrips() {
let arena = LocalArena::new();
let value = arena.alloc(42u32);
assert_eq!(*value, 42);
}
#[test]
fn alloc_str_copies() {
let arena = LocalArena::new();
let copied = arena.alloc_str("hello");
assert_eq!(copied, "hello");
}
#[test]
fn alloc_fmt_formats_into_arena() {
let arena = LocalArena::new();
let formatted = arena.alloc_fmt(format_args!("{}+{}={}", 2, 3, 5));
assert_eq!(formatted, "2+3=5");
}
#[test]
fn format_in_macro_formats_into_arena() {
let arena = LocalArena::new();
let label = crate::format_in!(arena, "{}::{}", "App", "VERSION");
assert_eq!(label, "App::VERSION");
}
#[test]
fn alloc_slice_copy_copies() {
let arena = LocalArena::new();
let slice = arena.alloc_slice_copy(&[1, 2, 3]);
assert_eq!(slice, &[1, 2, 3]);
}
#[test]
fn alloc_slice_fill_with_invokes_closure() {
let arena = LocalArena::new();
let slice = arena.alloc_slice_fill_with(4, |index| index * 2);
assert_eq!(slice, &[0, 2, 4, 6]);
}
#[test]
fn shared_vec_grows_in_place() {
let arena = SharedArena::new();
let mut vec = Vec::new_in(&arena);
for value in 0..1000 {
vec.push(value);
}
assert_eq!(vec.len(), 1000);
assert_eq!(vec.last(), Some(&999));
}
#[test]
fn scoped_alloc_roundtrips() {
let shared = SharedArena::new();
let scoped = shared.scoped();
let value = scoped.alloc(7u8);
assert_eq!(*value, 7);
}
#[test]
fn arena_hashmap_roundtrips() {
let arena = SharedArena::new();
let mut map = HashMap::new_in(&arena);
map.insert(1u32, "one");
assert_eq!(map.get(&1), Some(&"one"));
}
#[cfg(feature = "serde")]
#[test]
fn arena_vec_serializes() {
let arena = LocalArena::new();
let mut numbers = Vec::new_in(&arena);
numbers.extend([1, 2, 3]);
let json = serde_json::to_string(&numbers);
assert!(matches!(json.as_deref(), Ok("[1,2,3]")));
}
}