use std::{iter::FusedIterator, marker::PhantomData, mem, ptr::NonNull};
use super::{Arena, CHUNK_FOOTER_SIZE, ChunkFooter};
impl<const MIN_ALIGN: usize> Arena<MIN_ALIGN> {
pub fn chunk_capacity(&self) -> usize {
unsafe { self.cursor_ptr.get().offset_from_unsigned(self.start_ptr.get()) }
}
pub fn iter_allocated_chunks(&mut self) -> ChunkIter<'_, MIN_ALIGN> {
let raw = unsafe { self.iter_allocated_chunks_raw() };
ChunkIter { raw, arena: PhantomData }
}
pub unsafe fn iter_allocated_chunks_raw(&self) -> ChunkRawIter<'_, MIN_ALIGN> {
ChunkRawIter {
footer_ptr: self.current_chunk_footer_ptr.get(),
current_chunk_cursor_ptr: Some(self.cursor_ptr.get()),
arena: PhantomData,
}
}
pub fn allocated_bytes(&self) -> usize {
let mut total = 0;
let mut next_footer_ptr = self.current_chunk_footer_ptr.get();
while let Some(footer_ptr) = next_footer_ptr {
let footer = unsafe { footer_ptr.as_ref() };
total += footer.layout.size() - CHUNK_FOOTER_SIZE;
next_footer_ptr = footer.previous_chunk_footer_ptr.get();
}
total
}
pub fn used_bytes(&self) -> usize {
let Some(current_footer_ptr) = self.current_chunk_footer_ptr.get() else {
return 0;
};
let end_ptr = current_footer_ptr.cast::<u8>();
let mut total = unsafe { end_ptr.offset_from_unsigned(self.cursor_ptr.get()) };
let mut next_footer_ptr =
unsafe { current_footer_ptr.as_ref() }.previous_chunk_footer_ptr.get();
while let Some(footer_ptr) = next_footer_ptr {
let footer = unsafe { footer_ptr.as_ref() };
let cursor_ptr = footer.cursor_ptr.get();
let end_ptr = footer_ptr.cast::<u8>();
debug_assert!(cursor_ptr <= end_ptr);
total += unsafe { end_ptr.offset_from_unsigned(cursor_ptr) };
next_footer_ptr = footer.previous_chunk_footer_ptr.get();
}
total
}
}
#[derive(Debug)]
pub struct ChunkIter<'a, const MIN_ALIGN: usize = 1> {
raw: ChunkRawIter<'a, MIN_ALIGN>,
arena: PhantomData<&'a mut Arena<MIN_ALIGN>>,
}
impl<'a, const MIN_ALIGN: usize> Iterator for ChunkIter<'a, MIN_ALIGN> {
type Item = &'a [mem::MaybeUninit<u8>];
fn next(&mut self) -> Option<Self::Item> {
let (ptr, len) = self.raw.next()?;
let slice_ptr = NonNull::slice_from_raw_parts(ptr.cast::<mem::MaybeUninit<u8>>(), len);
let slice = unsafe { slice_ptr.as_ref() };
Some(slice)
}
}
impl<const MIN_ALIGN: usize> FusedIterator for ChunkIter<'_, MIN_ALIGN> {}
#[derive(Debug)]
pub struct ChunkRawIter<'a, const MIN_ALIGN: usize = 1> {
footer_ptr: Option<NonNull<ChunkFooter>>,
current_chunk_cursor_ptr: Option<NonNull<u8>>,
arena: PhantomData<&'a Arena<MIN_ALIGN>>,
}
impl<const MIN_ALIGN: usize> Iterator for ChunkRawIter<'_, MIN_ALIGN> {
type Item = (NonNull<u8>, usize);
fn next(&mut self) -> Option<(NonNull<u8>, usize)> {
let footer_ptr = self.footer_ptr?;
let footer = unsafe { footer_ptr.as_ref() };
let cursor_ptr =
self.current_chunk_cursor_ptr.take().unwrap_or_else(|| footer.cursor_ptr.get());
let end_ptr = footer_ptr.cast::<u8>();
debug_assert!(footer.start_ptr <= cursor_ptr);
debug_assert!(cursor_ptr <= end_ptr);
let len = unsafe { end_ptr.offset_from_unsigned(cursor_ptr) };
self.footer_ptr = footer.previous_chunk_footer_ptr.get();
Some((cursor_ptr, len))
}
}
impl<const MIN_ALIGN: usize> FusedIterator for ChunkRawIter<'_, MIN_ALIGN> {}