use std::{
iter::FusedIterator,
marker::PhantomData,
mem,
ptr::{self, NonNull},
slice,
};
use super::{Arena, CHUNK_FOOTER_SIZE, ChunkFooter};
impl<const MIN_ALIGN: usize> Arena<MIN_ALIGN> {
pub fn chunk_capacity(&self) -> usize {
self.cursor_ptr.get().as_ptr() as usize - self.start_ptr.get().as_ptr() as usize
}
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: self.current_chunk_footer.get(),
current_chunk_cursor_ptr: Some(self.cursor_ptr.get()),
arena: PhantomData,
}
}
pub fn allocated_bytes(&self) -> usize {
let mut total = 0;
let mut footer_ptr = self.current_chunk_footer.get();
unsafe {
while !footer_ptr.as_ref().is_empty() {
total += footer_ptr.as_ref().layout.size() - CHUNK_FOOTER_SIZE;
footer_ptr = footer_ptr.as_ref().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> {
unsafe {
let (ptr, len) = self.raw.next()?;
let slice = slice::from_raw_parts(ptr as *const mem::MaybeUninit<u8>, len);
Some(slice)
}
}
}
impl<const MIN_ALIGN: usize> FusedIterator for ChunkIter<'_, MIN_ALIGN> {}
#[derive(Debug)]
pub struct ChunkRawIter<'a, const MIN_ALIGN: usize = 1> {
footer: 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 = (*mut u8, usize);
fn next(&mut self) -> Option<(*mut u8, usize)> {
unsafe {
let foot = self.footer.as_ref();
if foot.is_empty() {
return None;
}
let start_ptr = foot.start_ptr.as_ptr();
let cursor_ptr = self
.current_chunk_cursor_ptr
.take()
.unwrap_or_else(|| foot.cursor_ptr.get())
.as_ptr();
let end_ptr = ptr::from_ref(foot).cast::<u8>();
debug_assert!(start_ptr <= cursor_ptr);
debug_assert!(cursor_ptr.cast_const() <= end_ptr);
let len = end_ptr.offset_from_unsigned(cursor_ptr.cast_const());
self.footer = foot.previous_chunk_footer_ptr.get();
Some((cursor_ptr, len))
}
}
}
impl<const MIN_ALIGN: usize> FusedIterator for ChunkRawIter<'_, MIN_ALIGN> {}