#![doc = include_str!(concat!(env!("OUT_DIR"), "/README-rustdocified.md"))]
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
extern crate alloc as core_alloc;
mod limits;
use core::{
alloc::Layout,
array,
cell::Cell,
hint::cold_path,
marker::PhantomData,
mem,
ptr::{self, NonNull},
};
use core_alloc::alloc::{alloc, dealloc};
use crate::limits::SAFE_SIZE;
const HEADER_SIZE: usize = mem::size_of::<ChunkHeader>();
pub const INITIAL_CHUNK_CAPACITY: usize = 256 - HEADER_SIZE;
const CHUNK_ALIGN: usize = 16;
const MIN_TOP: usize = HEADER_SIZE * 2;
struct ChunkHeader {
next: *mut ChunkHeader,
capacity: usize,
}
#[repr(C)]
pub struct Arena {
top: Cell<*mut u8>,
bottom: Cell<*mut u8>,
fresh_chunks: Cell<*mut ChunkHeader>,
_padding: *const (),
}
unsafe impl Send for Arena {}
impl Arena {
pub fn new() -> Self {
let header = (&FIRST_HEADER) as *const ChunkHeader as *mut ChunkHeader;
let bottom = unsafe { header.add(1) as *mut u8 };
Self {
top: Cell::new(bottom),
bottom: Cell::new(bottom),
fresh_chunks: Cell::new(ptr::null_mut()),
_padding: ptr::null(),
}
}
pub fn with_default_chunk() -> Self {
Self::with_chunk(INITIAL_CHUNK_CAPACITY)
}
pub fn with_chunk(default_chunk_size: usize) -> Self {
let header = ArenaRef::chunk_new(default_chunk_size, ptr::null_mut());
let bottom = unsafe { header.add(1) as *mut u8 };
let top = unsafe { bottom.byte_add((*header).capacity) as *mut u8 };
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 { &*(self as *const Arena as *const ArenaRef<'_>) }
}
#[inline(always)]
pub fn as_arena_ref_mut(&mut self) -> &mut ArenaRef<'_> {
unsafe { &mut *(self as *mut Arena as *mut ArenaRef<'_>) }
}
#[inline]
pub fn alloc<T>(&self, data: T) -> &mut T {
self.as_arena_ref().alloc(data)
}
#[inline(never)]
pub fn alloc_no_inline<T>(&self, data: T) -> &mut T {
self.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 alloc_slice_copy<T: Copy>(&self, data: &[T]) -> &mut [T] {
self.as_arena_ref().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 alloc_slice_clone<T: Clone>(&self, data: &[T]) -> &mut [T] {
self.as_arena_ref().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 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 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 alloc_slice_try_fill_with<T, E, F>(&self, len: usize, f: F) -> Result<&mut [T], E>
where
F: FnMut(usize) -> 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 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 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 alloc_slice_fill_default<T: Default>(&self, len: usize) -> &mut [T] {
self.as_arena_ref().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 alloc_str(&self, s: &str) -> &mut str {
self.as_arena_ref().alloc_str(s)
}
#[inline]
pub fn alloc_str_lit(&self, s: &'static str) -> &mut str {
self.as_arena_ref().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() as *mut ChunkHeader;
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) as *mut u8).byte_add(capacity) });
let used_chain = unsafe { (*current).next };
unsafe { (*current).next = ptr::null_mut() };
self.fresh_chunks.set(ArenaRef::join_chunk_chains(
self.fresh_chunks.get(),
used_chain,
));
}
pub fn free_fresh_chunks(&mut self) {
unsafe { ArenaRef::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) }
}
}
#[repr(C)]
pub struct ArenaRef<'a> {
top: Cell<*mut u8>,
bottom: Cell<*mut u8>,
fresh_chunks: Cell<*mut ChunkHeader>,
arena_ptr: *const Arena,
_marker: PhantomData<&'a ()>,
}
const FIRST_HEADER: ChunkHeader = ChunkHeader {
next: ptr::null::<ChunkHeader>() as *mut ChunkHeader,
capacity: 0,
};
impl<'a> ArenaRef<'a> {
#[inline]
pub fn alloc<T>(&self, data: T) -> &'a mut T {
self.alloc_with(|| data)
}
#[inline(never)]
pub fn alloc_no_inline<T>(&self, data: T) -> &'a mut T {
self.alloc(data)
}
#[inline]
pub fn alloc_with<F, T>(&self, f: F) -> &'a mut T
where
F: FnOnce() -> T,
{
let layout = Layout::new::<T>();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, true);
let ptr: *mut T = if layout.size() <= 16 {
if !take_slow_path {
unsafe {
let top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size()) as *mut T;
ptr::write(top, f());
self.top.set(top as *mut u8);
top
}
} else {
cold_path();
self.alloc_slow_with(f)
}
} else {
unsafe {
let ptr: *mut T = if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
self.top.set(new_top);
new_top as *mut T
} else {
cold_path();
self.alloc_slow_raw::<T>()
};
ptr::write(ptr, f());
ptr
}
};
unsafe { &mut *ptr }
}
#[inline]
pub fn alloc_slice_copy<T: Copy>(&self, data: &[T]) -> &'a mut [T] {
let len = data.len();
let layout = Layout::array::<T>(len).unwrap();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, false);
unsafe {
let ptr: *mut T = if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
self.top.set(new_top);
new_top as *mut T
} else {
cold_path();
self.alloc_layout_slow(layout).cast().as_ptr()
};
core::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len);
core::slice::from_raw_parts_mut(ptr, len)
}
}
#[inline]
pub fn alloc_slice_lit_copy<T: Copy>(&self, data: &'static [T]) -> &'a mut [T] {
let len = data.len();
let layout = Layout::array::<T>(len).unwrap();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, true);
unsafe {
let ptr: *mut T = if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
self.top.set(new_top);
new_top as *mut T
} else {
cold_path();
self.alloc_layout_slow(layout).cast().as_ptr()
};
core::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len);
core::slice::from_raw_parts_mut(ptr, len)
}
}
#[inline]
pub fn alloc_slice_clone<T: Clone>(&self, data: &[T]) -> &'a mut [T] {
self.alloc_slice_fill_with(data.len(), |n| data[n].clone())
}
#[inline]
pub fn alloc_sized_slice_with<T, const N: usize, F>(&self, f: F) -> &'a mut [T; N]
where
F: FnOnce() -> [T; N],
{
let layout = Layout::new::<[T; N]>();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, true);
let ptr: *mut [T; N] = if layout.size() <= 16 {
unsafe {
if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
let top = (new_top as *mut [T; N]).as_mut_unchecked();
let data = f();
core::ptr::copy_nonoverlapping(data.as_ptr(), top as *mut T, data.len());
self.top.set(new_top);
new_top as *mut [T; N]
} else {
cold_path();
self.alloc_slow_with::<_, [T; N]>(f)
}
}
} else {
unsafe {
let inner: *mut [T; N] = if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
self.top.set(new_top);
new_top as *mut [T; N]
} else {
cold_path();
self.alloc_slow_raw::<[T; N]>()
};
let data = f();
core::ptr::copy_nonoverlapping(data.as_ptr(), inner as *mut T, data.len());
inner
}
};
unsafe { &mut *ptr }
}
#[inline]
pub fn alloc_sized_slice_fill_with<T, const N: usize, F>(&self, mut f: F) -> &'a mut [T; N]
where
F: FnMut(usize) -> T,
{
let layout = Layout::new::<[T; N]>();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, true);
let ptr: *mut [T; N] = if layout.size() <= 16 {
unsafe {
if !take_slow_path {
let top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size()) as *mut T;
let mut p = top;
for i in 0..N {
core::ptr::write(p, f(i));
p = p.add(1);
}
self.top.set(top as *mut u8);
top as *mut [T; N]
} else {
cold_path();
self.alloc_slow_with::<_, [T; N]>(|| array::from_fn(f))
}
}
} else {
unsafe {
let inner: *mut T = if !take_slow_path {
let new_top = top.byte_sub(extra_bytes_needed).byte_sub(layout.size());
self.top.set(new_top);
new_top as *mut T
} else {
cold_path();
self.alloc_layout_slow(layout).cast().as_ptr()
};
let mut p = inner;
for i in 0..N {
core::ptr::write(p, f(i));
p = p.add(1);
}
inner as *mut [T; N]
}
};
unsafe { &mut *ptr }
}
#[inline]
pub fn alloc_slice_fill_with<T, F>(&self, len: usize, mut f: F) -> &'a mut [T]
where
F: FnMut(usize) -> T,
{
let layout = Layout::array::<T>(len).unwrap();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, false);
unsafe {
let ptr: *mut T = if !take_slow_path {
let ptr = top.byte_sub(extra_bytes_needed).byte_sub(layout.size()) as *mut T;
self.top.set(ptr as *mut u8);
ptr
} else {
cold_path();
self.alloc_layout_slow(layout).cast().as_ptr()
};
let mut p = ptr;
for i in 0..len {
core::ptr::write(p, f(i));
p = p.add(1);
}
core::slice::from_raw_parts_mut(ptr, len)
}
}
#[inline]
pub fn alloc_slice_try_fill_with<T, E, F>(&self, len: usize, mut f: F) -> Result<&'a mut [T], E>
where
F: FnMut(usize) -> Result<T, E>,
{
let layout = Layout::array::<T>(len).unwrap();
let top = self.top.get();
let bottom = self.bottom.get() as usize;
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let take_slow_path = self.need_slow_path(layout, top, bottom, false);
unsafe {
let ptr: *mut T = if !take_slow_path {
let ptr = top.byte_sub(extra_bytes_needed).byte_sub(layout.size()) as *mut T;
self.top.set(ptr as *mut u8);
ptr
} else {
cold_path();
self.alloc_layout_slow(layout).cast().as_ptr()
};
let mut p = ptr;
for i in 0..len {
match f(i) {
Ok(a) => {
core::ptr::write(p, a);
p = p.add(1);
}
Err(e) => {
cold_path();
if take_slow_path {
cold_path();
let header = self.header().as_ref_unchecked();
self.top.set(self.bottom.get().byte_add(header.capacity));
} else {
self.top.set(top);
}
return Err(e);
}
}
}
Ok(core::slice::from_raw_parts_mut(ptr, len))
}
}
#[inline]
pub fn alloc_sized_slice_copy<T: Copy, const N: usize>(&self, data: &[T; N]) -> &'a mut [T; N] {
self.alloc_sized_slice_with(|| *data)
}
#[inline]
pub fn alloc_sized_slice_clone<T: Clone, const N: usize>(
&self,
data: &[T; N],
) -> &'a mut [T; N] {
self.alloc_sized_slice_fill_with(|n| data[n].clone())
}
#[inline]
pub fn alloc_sized_slice_fill_default<T: Default, const N: usize>(&self) -> &'a mut [T; N] {
self.alloc_sized_slice_fill_with(|_| Default::default())
}
#[inline]
pub fn alloc_slice_fill_default<T: Default>(&self, len: usize) -> &'a mut [T] {
self.alloc_slice_fill_with(len, |_| Default::default())
}
#[inline]
pub fn alloc_slice_fill_iter<T, I>(&self, iter: I) -> &'a mut [T]
where
I: IntoIterator<Item = T>,
<I as IntoIterator>::IntoIter: ExactSizeIterator,
{
const ERR: &str = "Iterator didn't produce enough elements to match its `.len()`.";
let mut iter = iter.into_iter();
self.alloc_slice_fill_with(iter.len(), |_| iter.next().expect(ERR))
}
#[inline]
pub fn alloc_str(&self, s: &str) -> &'a mut str {
let slice = self.alloc_slice_copy(s.as_bytes());
unsafe { core::str::from_utf8_unchecked_mut(slice) }
}
pub fn allocated_bytes(&self) -> usize {
let header = unsafe { self.header().as_ref_unchecked() };
let mut res = header.capacity;
res += Self::count_bytes_allocated_in_chunk_seq(header.next);
res += Self::count_bytes_allocated_in_chunk_seq(self.fresh_chunks.get());
res
}
pub fn allocated_bytes_including_metadata(&self) -> usize {
let header = unsafe { self.header().as_ref_unchecked() };
let mut res = header.capacity;
res += Self::count_bytes_allocated_in_chunk_seq_with_meta(header.next);
res += Self::count_bytes_allocated_in_chunk_seq_with_meta(self.fresh_chunks.get());
res
}
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.with_scope_unchecked(f) }
}
unsafe fn with_scope_unchecked<'brand, F, R>(&'brand mut self, f: F) -> R
where
F: for<'scope> FnOnce(&'scope mut ArenaRef<'scope>) -> R,
{
let checkpoint = unsafe { self.checkpoint() };
let inner = self as *mut ArenaRef<'_>;
let result = f(unsafe { &mut *inner });
unsafe { self.restore_checkpoint(checkpoint) };
result
}
unsafe fn restore_checkpoint(&self, checkpoint: Checkpoint) {
let mut header = self.header();
let mut fresh_head = self.fresh_chunks.get();
while header != checkpoint.header && !header.is_null() {
let next_header = unsafe { (*header).next };
unsafe { ptr::write(&raw mut (*header).next, fresh_head) };
fresh_head = header;
header = next_header;
}
self.top.set(checkpoint.top);
let new_bottom = unsafe { checkpoint.header.add(1) as *mut u8 };
self.bottom.set(new_bottom);
self.fresh_chunks.set(fresh_head);
}
unsafe fn checkpoint(&self) -> Checkpoint {
Checkpoint {
top: self.top.get(),
header: self.header(),
}
}
#[inline]
pub fn alloc_str_lit(&self, s: &'static str) -> &'a mut str {
let slice = self.alloc_slice_lit_copy(s.as_bytes());
unsafe { core::str::from_utf8_unchecked_mut(slice) }
}
}
struct Checkpoint {
top: *mut u8,
header: *mut ChunkHeader,
}
impl<'a> ArenaRef<'a> {
#[inline(never)]
fn count_bytes_allocated_in_chunk_seq(mut head: *mut ChunkHeader) -> usize {
let mut res = 0;
while !head.is_null() {
unsafe {
let node = head.as_ref_unchecked();
res += node.capacity;
head = node.next;
}
}
res
}
#[inline(never)]
fn count_bytes_allocated_in_chunk_seq_with_meta(mut head: *mut ChunkHeader) -> usize {
let mut res = 0;
while !head.is_null() {
unsafe {
let node = head.as_ref_unchecked();
res += node.capacity + HEADER_SIZE;
head = node.next;
}
}
res
}
#[doc(hidden)]
pub fn chunk_capacity(&self) -> usize {
self.top.get() as usize - self.bottom.get() as usize
}
#[doc(hidden)]
pub fn has_fresh_chunks(&self) -> bool {
!self.fresh_chunks.get().is_null()
}
}
impl<'a> ArenaRef<'a> {
#[inline(never)]
fn alloc_slow_with<F, T>(&self, f: F) -> *mut T
where
F: FnOnce() -> T,
{
let p = self.alloc_slow_raw::<T>();
unsafe { ptr::write(p, f()) };
p
}
fn alloc_slow_raw<T>(&self) -> *mut T {
let layout = Layout::new::<T>();
self.alloc_layout_slow(layout).cast().as_ptr() as *mut T
}
#[inline(always)]
fn need_slow_path(
&self,
layout: Layout,
top: *mut u8,
bottom: usize,
comptime_layout: bool,
) -> bool {
let extra_bytes_needed = ArenaRef::extra_bytes_needed(top, layout.align());
let max_padding = layout.align() - 1;
let max_space: usize = layout.size() + max_padding;
let top = top as usize;
if comptime_layout && max_space < SAFE_SIZE {
if max_space <= MIN_TOP {
let top = top - extra_bytes_needed;
let top = top - layout.size();
bottom > top as usize
} else if max_padding < MIN_TOP {
let top = top - extra_bytes_needed;
bottom + layout.size() > top as usize
} else {
bottom + layout.size() + extra_bytes_needed > top
}
} else if max_padding
.checked_add(isize::MAX as usize)
.is_some_and(|a| a < SAFE_SIZE)
{
if max_padding < MIN_TOP {
let top = top - extra_bytes_needed;
bottom + layout.size() > top
} else {
bottom + extra_bytes_needed + layout.size() > top
}
} else {
if max_padding < MIN_TOP {
let top = top - extra_bytes_needed;
top.checked_sub(layout.size()).is_none_or(|t| t < bottom)
} else {
top.checked_sub(extra_bytes_needed)
.and_then(|p| p.checked_sub(layout.size()))
.is_none_or(|p| p < bottom)
}
}
}
fn join_chunk_chains(a: *mut ChunkHeader, b: *mut ChunkHeader) -> *mut ChunkHeader {
if a.is_null() {
return b;
}
if b.is_null() {
return a;
}
let mut curr = a;
loop {
let next = unsafe { (*curr).next };
if next.is_null() {
break;
}
curr = next;
}
unsafe { (*curr).next = b };
a
}
#[inline(always)]
fn extra_bytes_needed(ptr: *const u8, alignment: usize) -> usize {
assert!(alignment.is_power_of_two());
return ptr as usize & (alignment - 1);
}
fn header(&self) -> *mut ChunkHeader {
let ptr = self.bottom.get() as *mut ChunkHeader;
unsafe { ptr.sub(1) }
}
fn extra_fresh_bytes_needed(alignment: usize) -> usize {
let start: usize = if alignment > HEADER_SIZE {
alignment - HEADER_SIZE
} else {
HEADER_SIZE
};
start & (alignment - 1)
}
#[inline]
fn chunk_new(size: usize, next: *mut ChunkHeader) -> *mut ChunkHeader {
let total = size + HEADER_SIZE;
let layout = match Layout::from_size_align(total, CHUNK_ALIGN) {
Ok(l) => l,
Err(_) => {
panic!(
"OOM: requested chunk size {} too large (exceeds Layout limits)",
size,
);
}
};
let chunk: *mut ChunkHeader = unsafe { alloc(layout) } as *mut ChunkHeader;
if chunk.is_null() {
panic!(
"OOM: failed to allocate chunk of {} bytes (alignment {})",
total, CHUNK_ALIGN,
);
}
let header = ChunkHeader {
next,
capacity: size,
};
unsafe {
ptr::write(chunk, header);
}
chunk
}
#[inline(never)]
fn alloc_layout_slow(&self, layout: Layout) -> NonNull<u8> {
let extra_fresh_bytes = Self::extra_fresh_bytes_needed(layout.align());
let min_size = layout.size() + extra_fresh_bytes;
loop {
let fresh = self.fresh_chunks.get();
if fresh.is_null() {
break;
}
let capacity = unsafe { (*fresh).capacity };
if capacity >= min_size {
let next = self.header();
unsafe {
self.fresh_chunks.set((*fresh).next);
(*fresh).next = next;
}
let new_bottom = unsafe { fresh.add(1) as *mut u8 };
self.bottom.set(new_bottom);
let new_top = unsafe { new_bottom.byte_add(capacity) };
let extra = Self::extra_bytes_needed(new_top, layout.align());
let top = unsafe { new_top.byte_sub(layout.size() + extra) };
self.top.set(top);
return unsafe { NonNull::new_unchecked(top) };
}
unsafe {
self.fresh_chunks.set((*fresh).next);
let layout = Layout::from_size_align_unchecked(capacity + HEADER_SIZE, CHUNK_ALIGN);
dealloc(fresh as *mut u8, layout);
}
}
let next = self.header();
let next = if unsafe { (*next).capacity == 0 } {
ptr::null_mut()
} else {
next
};
let size = self.next_chunk_size().max(min_size);
let new_chunk = Self::chunk_new(size, next);
let new_bottom = unsafe { new_chunk.add(1) as *mut u8 };
self.bottom.set(new_bottom);
let new_top = unsafe { new_bottom.byte_add(size) };
let extra = Self::extra_bytes_needed(new_top, layout.align());
let top = unsafe { new_top.byte_sub(layout.size() + extra) };
self.top.set(top);
unsafe { NonNull::new_unchecked(top) }
}
unsafe fn free_chunk_chain(mut current: *mut ChunkHeader) {
while !current.is_null() {
let prev = unsafe { (*current).next };
let capacity = unsafe { (*current).capacity };
if capacity > 0 {
let layout = unsafe {
Layout::from_size_align_unchecked(capacity + HEADER_SIZE, CHUNK_ALIGN)
};
unsafe { dealloc(current as *mut u8, layout) };
}
current = prev;
}
}
fn next_chunk_size(&self) -> usize {
let header = self.header();
let cap = unsafe { header.as_ref_unchecked().capacity };
let res = (cap + HEADER_SIZE).saturating_mul(2) - HEADER_SIZE;
res.max(INITIAL_CHUNK_CAPACITY)
}
}
impl<'a> Drop for ArenaRef<'a> {
fn drop(&mut self) {
let arena = unsafe { &*self.arena_ptr };
arena.top.set(self.top.get());
arena.bottom.set(self.bottom.get());
arena.fresh_chunks.set(self.fresh_chunks.get());
}
}
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) as *mut ChunkHeader;
ArenaRef::free_chunk_chain(current);
}
ArenaRef::free_chunk_chain(self.fresh_chunks.get());
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn size_of_header() {
assert_eq!(crate::HEADER_SIZE, 16);
}
}