#![warn(clippy::pedantic)]
#![doc = include_str!(concat!(env!("OUT_DIR"), "/README-rustdocified.md"))]
#[cfg(any(feature = "allocator-api2", feature = "nightly"))]
mod allocator_api;
#[cfg(nightly)]
use core::iter::TrustedLen;
use core::{
alloc::Layout,
cell::Cell,
ptr::{self, NonNull},
};
use core_alloc::boxed::Box;
use core_alloc::vec::Vec;
type Result<T> = core::result::Result<T, Error>;
use crate::{
arena_ref::ArenaRef,
chunk::{ChunkHeader, FIRST_HEADER, HEADER_SIZE, INITIAL_CHUNK_CAPACITY},
errors::*,
join_chunk_chains,
};
#[doc(hidden)]
extern crate alloc as core_alloc;
#[repr(C)]
pub struct Arena {
pub(crate) top: Cell<*mut u8>,
pub(crate) bottom: Cell<*mut ChunkHeader>,
pub(crate) fresh_chunks: Cell<*mut ChunkHeader>,
pub(crate) _padding: *const (),
}
unsafe impl Send for Arena {}
impl Default for Arena {
fn default() -> Self {
Self::new()
}
}
impl Arena {
#[must_use]
pub fn new() -> Self {
let header = ptr::from_ref(&FIRST_HEADER).cast_mut();
let bottom = unsafe { header.add(1) };
Self {
top: Cell::new(bottom.cast::<u8>()),
bottom: Cell::new(bottom),
fresh_chunks: Cell::new(ptr::null_mut()),
_padding: ptr::null(),
}
}
#[must_use]
pub fn with_default_chunk() -> Self {
Self::with_chunk(INITIAL_CHUNK_CAPACITY)
}
#[must_use]
pub fn try_with_default_chunk() -> Result<Self> {
Self::try_with_chunk(INITIAL_CHUNK_CAPACITY)
}
#[must_use]
pub fn with_chunk(default_chunk_size: usize) -> Self {
let header = ChunkHeader::new(default_chunk_size, ptr::null_mut());
let bottom = unsafe { header.add(1) };
let top = unsafe { bottom.byte_add((*header).capacity).cast::<u8>() };
Self {
top: Cell::new(top),
bottom: Cell::new(bottom),
fresh_chunks: Cell::new(ptr::null_mut()),
_padding: ptr::null(),
}
}
pub fn try_with_chunk(first_chunk_size: usize) -> Result<Self> {
let header = ChunkHeader::try_new(first_chunk_size, ptr::null_mut())?;
let bottom = unsafe { header.add(1) };
let top = unsafe { bottom.byte_add((*header).capacity).cast::<u8>() };
Ok(Self {
top: Cell::new(top),
bottom: Cell::new(bottom),
fresh_chunks: Cell::new(ptr::null_mut()),
_padding: ptr::null(),
})
}
#[inline(always)]
pub fn as_arena_ref(&self) -> &ArenaRef<'_> {
unsafe { &*(ptr::from_ref(self).cast::<ArenaRef<'_>>()) }
}
#[inline(always)]
pub fn as_arena_ref_mut(&mut self) -> &mut ArenaRef<'_> {
unsafe { &mut *(ptr::from_mut(self).cast::<ArenaRef<'_>>()) }
}
#[inline]
pub fn alloc<T>(&self, data: T) -> &mut T {
self.as_arena_ref().alloc(data)
}
#[inline]
pub fn try_alloc<T>(&self, data: T) -> Result<&mut T> {
self.as_arena_ref().try_alloc(data)
}
#[inline(never)]
pub fn alloc_no_inline<T>(&self, data: T) -> &mut T {
self.alloc(data)
}
#[inline(never)]
pub fn try_alloc_no_inline<T>(&self, data: T) -> Result<&mut T> {
self.try_alloc(data)
}
#[inline]
pub fn alloc_with<F, T>(&self, f: F) -> &mut T
where
F: FnOnce() -> T,
{
self.as_arena_ref().alloc_with(f)
}
#[inline]
pub fn try_alloc_with<F, T>(&self, f: F) -> Result<&mut T>
where
F: FnOnce() -> T,
{
self.as_arena_ref().try_alloc_with(f)
}
#[inline]
pub fn alloc_vec<T>(&self, data: Vec<T>) -> &mut [T] {
self.as_arena_ref().alloc_vec(data)
}
#[inline]
pub fn try_alloc_vec<T>(&self, data: Vec<T>) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_vec(data)
}
#[inline]
pub fn alloc_boxed_slice<T>(&self, data: Box<[T]>) -> &mut [T] {
self.as_arena_ref().alloc_boxed_slice(data)
}
#[inline]
pub fn try_alloc_boxed_slice<T>(&self, data: Box<[T]>) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_boxed_slice(data)
}
#[inline]
pub fn alloc_layout(&self, layout: Layout) -> NonNull<[u8]> {
self.as_arena_ref().alloc_layout(layout)
}
#[inline]
pub fn try_alloc_layout(&self, layout: Layout) -> Result<NonNull<[u8]>> {
self.as_arena_ref().try_alloc_layout(layout)
}
#[inline]
pub fn alloc_slice_copy<T: Copy>(&self, data: &[T]) -> &mut [T] {
self.as_arena_ref().alloc_slice_copy(data)
}
#[inline]
pub fn try_alloc_slice_copy<T: Copy>(&self, data: &[T]) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_slice_copy(data)
}
#[inline]
pub fn alloc_slice_lit_copy<T: Copy>(&self, data: &'static [T]) -> &mut [T] {
self.as_arena_ref().alloc_slice_lit_copy(data)
}
#[inline]
pub fn try_alloc_slice_lit_copy<T: Copy>(&self, data: &'static [T]) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_slice_lit_copy(data)
}
#[inline]
pub fn alloc_slice_clone<T: Clone>(&self, data: &[T]) -> &mut [T] {
self.as_arena_ref().alloc_slice_clone(data)
}
#[inline]
pub fn try_alloc_slice_clone<T: Clone>(&self, data: &[T]) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_slice_clone(data)
}
#[inline]
pub fn alloc_sized_slice_with<T, const N: usize, F>(&self, f: F) -> &mut [T; N]
where
F: FnOnce() -> [T; N],
{
self.as_arena_ref().alloc_sized_slice_with(f)
}
#[inline]
pub fn try_alloc_sized_slice_with<T, const N: usize, F>(&self, f: F) -> Result<&mut [T; N]>
where
F: FnOnce() -> [T; N],
{
self.as_arena_ref().try_alloc_sized_slice_with(f)
}
#[inline]
pub fn alloc_sized_slice_fill_with<T, const N: usize, F>(&self, f: F) -> &mut [T; N]
where
F: FnMut(usize) -> T,
{
self.as_arena_ref().alloc_sized_slice_fill_with(f)
}
#[inline]
pub fn try_alloc_sized_slice_fill_with<T, const N: usize, F>(&self, f: F) -> Result<&mut [T; N]>
where
F: FnMut(usize) -> T,
{
self.as_arena_ref().try_alloc_sized_slice_fill_with(f)
}
#[inline]
pub fn alloc_slice_fill_with<T, F>(&self, len: usize, f: F) -> &mut [T]
where
F: FnMut(usize) -> T,
{
self.as_arena_ref().alloc_slice_fill_with(len, f)
}
#[inline]
pub fn try_alloc_slice_fill_with<T, F>(&self, len: usize, f: F) -> Result<&mut [T]>
where
F: FnMut(usize) -> T,
{
self.as_arena_ref().try_alloc_slice_fill_with(len, f)
}
#[inline]
pub fn alloc_slice_try_fill_with<T, E, F>(
&self,
len: usize,
f: F,
) -> core::result::Result<&mut [T], E>
where
F: FnMut(usize) -> core::result::Result<T, E>,
{
self.as_arena_ref().alloc_slice_try_fill_with(len, f)
}
#[inline]
pub fn alloc_sized_slice_copy<T: Copy, const N: usize>(&self, data: &[T; N]) -> &mut [T; N] {
self.as_arena_ref().alloc_sized_slice_copy(data)
}
#[inline]
pub fn try_alloc_sized_slice_copy<T: Copy, const N: usize>(
&self,
data: &[T; N],
) -> Result<&mut [T; N]> {
self.as_arena_ref().try_alloc_sized_slice_copy(data)
}
#[inline]
pub fn alloc_sized_slice_clone<T: Clone, const N: usize>(&self, data: &[T; N]) -> &mut [T; N] {
self.as_arena_ref().alloc_sized_slice_clone(data)
}
#[inline]
pub fn try_alloc_sized_slice_clone<T: Clone, const N: usize>(
&self,
data: &[T; N],
) -> Result<&mut [T; N]> {
self.as_arena_ref().try_alloc_sized_slice_clone(data)
}
#[inline]
pub fn alloc_sized_slice_fill_default<T: Default, const N: usize>(&self) -> &mut [T; N] {
self.as_arena_ref().alloc_sized_slice_fill_default()
}
#[inline]
pub fn try_alloc_sized_slice_fill_default<T: Default, const N: usize>(
&self,
) -> Result<&mut [T; N]> {
self.as_arena_ref().try_alloc_sized_slice_fill_default()
}
#[inline]
pub fn alloc_slice_fill_default<T: Default>(&self, len: usize) -> &mut [T] {
self.as_arena_ref().alloc_slice_fill_default(len)
}
#[inline]
pub fn try_alloc_slice_fill_default<T: Default>(&self, len: usize) -> Result<&mut [T]> {
self.as_arena_ref().try_alloc_slice_fill_default(len)
}
#[inline]
pub fn alloc_slice_fill_iter<T, I>(&self, iter: I) -> &mut [T]
where
I: IntoIterator<Item = T>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.as_arena_ref().alloc_slice_fill_iter(iter)
}
#[inline]
pub fn try_alloc_slice_fill_iter<T, I>(&self, iter: I) -> Result<&mut [T]>
where
I: IntoIterator<Item = T>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
self.as_arena_ref().try_alloc_slice_fill_iter(iter)
}
#[inline]
#[cfg(nightly)]
pub fn alloc_slice_fill_iter_trusted<T, I>(&self, iter: I) -> &mut [T]
where
I: IntoIterator<Item = T>,
<I as IntoIterator>::IntoIter: TrustedLen,
{
self.as_arena_ref().alloc_slice_fill_iter_trusted(iter)
}
#[inline]
pub fn alloc_str(&self, s: &str) -> &mut str {
self.as_arena_ref().alloc_str(s)
}
#[inline]
pub fn try_alloc_str(&self, s: &str) -> Result<&mut str> {
self.as_arena_ref().try_alloc_str(s)
}
#[inline]
pub fn alloc_str_lit(&self, s: &'static str) -> &mut str {
self.as_arena_ref().alloc_str_lit(s)
}
#[inline]
pub fn try_alloc_str_lit(&self, s: &'static str) -> Result<&mut str> {
self.as_arena_ref().try_alloc_str_lit(s)
}
pub fn allocated_bytes(&self) -> usize {
self.as_arena_ref().allocated_bytes()
}
pub fn allocated_bytes_including_metadata(&self) -> usize {
self.as_arena_ref().allocated_bytes_including_metadata()
}
pub fn chunk_capacity(&self) -> usize {
self.as_arena_ref().chunk_capacity()
}
pub fn has_fresh_chunks(&self) -> bool {
self.as_arena_ref().has_fresh_chunks()
}
fn header(&self) -> *mut ChunkHeader {
let ptr = self.bottom.get();
unsafe { ptr.sub(1) }
}
pub fn clear(&mut self) {
let current = self.header();
let capacity = unsafe { (*current).capacity };
if capacity == 0 {
return;
}
self.top
.set(unsafe { (current.add(1).cast::<u8>()).byte_add(capacity) });
let used_chain = unsafe { (*current).next };
unsafe { (*current).next = ptr::null_mut() };
self.fresh_chunks
.set(join_chunk_chains(self.fresh_chunks.get(), used_chain));
}
pub fn free_fresh_chunks(&mut self) {
unsafe { ChunkHeader::free_chunk_chain(self.fresh_chunks.get()) };
}
pub fn reset(&mut self) {
self.clear();
}
pub fn with_scope<'brand, F, R>(&'brand mut self, f: F) -> R
where
F: for<'scope> FnOnce(&'scope mut ArenaRef<'scope>) -> R,
{
unsafe { self.as_arena_ref_mut().with_scope_unchecked(f) }
}
}
impl Drop for Arena {
fn drop(&mut self) {
unsafe {
let bottom = self.bottom.get();
if !bottom.is_null() {
let current = bottom.byte_sub(HEADER_SIZE).cast::<ChunkHeader>();
ChunkHeader::free_chunk_chain(current);
}
ChunkHeader::free_chunk_chain(self.fresh_chunks.get());
}
}
}