use core::{fmt, iter::FusedIterator, marker::PhantomData, ptr::NonNull};
use crate::{chunk::ChunkHeader, settings::BumpAllocatorSettings};
use super::{Chunk, ChunkNextIter, ChunkPrevIter, Stats};
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct AnyStats<'a> {
chunk: Option<AnyChunk<'a>>,
}
impl<A, S> From<Stats<'_, A, S>> for AnyStats<'_>
where
S: BumpAllocatorSettings,
{
fn from(value: Stats<'_, A, S>) -> Self {
Self {
chunk: value.current_chunk().map(Into::into),
}
}
}
impl fmt::Debug for AnyStats<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.debug_format("AnyStats", f)
}
}
impl<'a> AnyStats<'a> {
#[must_use]
pub fn count(self) -> usize {
let Some(current) = self.chunk else { return 0 };
let mut sum = 1;
current.iter_prev().for_each(|_| sum += 1);
current.iter_next().for_each(|_| sum += 1);
sum
}
#[must_use]
pub fn size(self) -> usize {
let Some(current) = self.chunk else { return 0 };
let mut sum = current.size();
current.iter_prev().for_each(|chunk| sum += chunk.size());
current.iter_next().for_each(|chunk| sum += chunk.size());
sum
}
#[must_use]
pub fn capacity(self) -> usize {
let Some(current) = self.chunk else { return 0 };
let mut sum = current.capacity();
current.iter_prev().for_each(|chunk| sum += chunk.capacity());
current.iter_next().for_each(|chunk| sum += chunk.capacity());
sum
}
#[must_use]
pub fn allocated(self) -> usize {
let Some(current) = self.chunk else { return 0 };
let mut sum = current.allocated();
current.iter_prev().for_each(|chunk| sum += chunk.capacity());
sum
}
#[must_use]
pub fn remaining(self) -> usize {
let Some(current) = self.chunk else { return 0 };
let mut sum = current.remaining();
current.iter_next().for_each(|chunk| sum += chunk.capacity());
sum
}
#[must_use]
pub fn small_to_big(self) -> AnyChunkNextIter<'a> {
let Some(mut start) = self.chunk else {
return AnyChunkNextIter { chunk: None };
};
while let Some(chunk) = start.prev() {
start = chunk;
}
AnyChunkNextIter { chunk: Some(start) }
}
#[must_use]
pub fn big_to_small(self) -> AnyChunkPrevIter<'a> {
let Some(mut start) = self.chunk else {
return AnyChunkPrevIter { chunk: None };
};
while let Some(chunk) = start.next() {
start = chunk;
}
AnyChunkPrevIter { chunk: Some(start) }
}
#[must_use]
pub fn current_chunk(self) -> Option<AnyChunk<'a>> {
self.chunk
}
pub(crate) fn debug_format(self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(name)
.field("allocated", &self.allocated())
.field("capacity", &self.capacity())
.finish()
}
}
impl<'a> From<AnyChunk<'a>> for AnyStats<'a> {
fn from(chunk: AnyChunk<'a>) -> Self {
Self { chunk: Some(chunk) }
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct AnyChunk<'a> {
header: NonNull<ChunkHeader>,
marker: PhantomData<&'a ()>,
}
impl<A, S> From<Chunk<'_, A, S>> for AnyChunk<'_>
where
S: BumpAllocatorSettings,
{
fn from(value: Chunk<'_, A, S>) -> Self {
Self {
header: value.chunk.header().cast(),
marker: PhantomData,
}
}
}
impl fmt::Debug for AnyChunk<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Chunk")
.field("allocated", &self.allocated())
.field("capacity", &self.capacity())
.finish()
}
}
impl<'a> AnyChunk<'a> {
fn header(&self) -> &ChunkHeader {
unsafe { self.header.as_ref() }
}
#[inline]
pub(crate) fn is_upwards_allocating(self) -> bool {
let header = self.header.addr();
let end = self.header().end.addr();
end > header
}
#[must_use]
#[inline(always)]
pub fn prev(self) -> Option<Self> {
Some(AnyChunk {
header: self.header().prev.get()?,
marker: PhantomData,
})
}
#[must_use]
#[inline(always)]
pub fn next(self) -> Option<Self> {
Some(AnyChunk {
header: self.header().next.get()?,
marker: PhantomData,
})
}
#[must_use]
#[inline(always)]
pub fn iter_prev(self) -> AnyChunkPrevIter<'a> {
AnyChunkPrevIter { chunk: self.prev() }
}
#[must_use]
#[inline(always)]
pub fn iter_next(self) -> AnyChunkNextIter<'a> {
AnyChunkNextIter { chunk: self.next() }
}
#[must_use]
#[inline]
pub fn size(self) -> usize {
let start = self.chunk_start();
let end = self.chunk_end();
end.addr().get() - start.addr().get()
}
#[must_use]
#[inline]
pub fn capacity(self) -> usize {
let start = self.content_start();
let end = self.content_end();
end.addr().get() - start.addr().get()
}
#[must_use]
#[inline]
pub fn allocated(self) -> usize {
if self.is_upwards_allocating() {
let start = self.content_start();
let end = self.bump_position();
end.addr().get() - start.addr().get()
} else {
let start = self.bump_position();
let end = self.content_end();
end.addr().get() - start.addr().get()
}
}
#[must_use]
#[inline]
pub fn remaining(self) -> usize {
if self.is_upwards_allocating() {
let start = self.bump_position();
let end = self.content_end();
end.addr().get() - start.addr().get()
} else {
let start = self.content_start();
let end = self.bump_position();
end.addr().get() - start.addr().get()
}
}
#[must_use]
#[inline]
pub fn chunk_start(self) -> NonNull<u8> {
if self.is_upwards_allocating() {
self.header.cast()
} else {
self.header().end
}
}
#[must_use]
#[inline]
pub fn chunk_end(self) -> NonNull<u8> {
if self.is_upwards_allocating() {
self.header().end
} else {
self.after_header()
}
}
#[must_use]
#[inline]
pub fn content_start(self) -> NonNull<u8> {
if self.is_upwards_allocating() {
self.after_header()
} else {
self.chunk_start()
}
}
#[must_use]
#[inline]
pub fn content_end(self) -> NonNull<u8> {
if self.is_upwards_allocating() {
self.chunk_end()
} else {
self.header.cast()
}
}
#[must_use]
#[inline]
pub fn bump_position(self) -> NonNull<u8> {
self.header().pos.get()
}
fn after_header(self) -> NonNull<u8> {
unsafe { self.header.add(1).cast() }
}
}
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct AnyChunkPrevIter<'a> {
#[expect(missing_docs)]
pub chunk: Option<AnyChunk<'a>>,
}
impl<A, S> From<ChunkPrevIter<'_, A, S>> for AnyChunkPrevIter<'_>
where
S: BumpAllocatorSettings,
{
fn from(value: ChunkPrevIter<'_, A, S>) -> Self {
Self {
chunk: value.chunk.map(Into::into),
}
}
}
impl<'a> Iterator for AnyChunkPrevIter<'a> {
type Item = AnyChunk<'a>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let chunk = self.chunk?;
self.chunk = chunk.prev();
Some(chunk)
}
}
impl FusedIterator for AnyChunkPrevIter<'_> {}
impl fmt::Debug for AnyChunkPrevIter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(*self).finish()
}
}
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub struct AnyChunkNextIter<'a> {
#[expect(missing_docs)]
pub chunk: Option<AnyChunk<'a>>,
}
impl<A, S> From<ChunkNextIter<'_, A, S>> for AnyChunkNextIter<'_>
where
S: BumpAllocatorSettings,
{
fn from(value: ChunkNextIter<'_, A, S>) -> Self {
Self {
chunk: value.chunk.map(Into::into),
}
}
}
impl<'a> Iterator for AnyChunkNextIter<'a> {
type Item = AnyChunk<'a>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let chunk = self.chunk?;
self.chunk = chunk.next();
Some(chunk)
}
}
impl FusedIterator for AnyChunkNextIter<'_> {}
impl fmt::Debug for AnyChunkNextIter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.map(AnyChunk::size)).finish()
}
}
#[test]
fn check_from_impls() {
#![expect(dead_code, clippy::elidable_lifetime_names)]
use crate::{BaseAllocator, BumpScope};
fn accepting_any_stats(_: AnyStats) {}
fn accepting_any_chunk(_: AnyChunk) {}
fn accepting_any_chunk_prev_iter(_: AnyChunkPrevIter) {}
fn accepting_any_chunk_next_iter(_: AnyChunkNextIter) {}
fn generic_bump<'a, A, S>(bump: &BumpScope<'a, A, S>)
where
A: BaseAllocator<S::GuaranteedAllocated>,
S: BumpAllocatorSettings,
{
let stats = bump.stats();
accepting_any_stats(stats.into());
accepting_any_chunk(stats.current_chunk().unwrap().into());
accepting_any_chunk_next_iter(stats.small_to_big().into());
accepting_any_chunk_prev_iter(stats.big_to_small().into());
}
}