use core::{
fmt::{self, Debug},
iter::FusedIterator,
marker::PhantomData,
ptr::NonNull,
};
use crate::{
raw_bump::{NonDummyChunk, RawChunk},
settings::{BumpAllocatorSettings, BumpSettings, False},
};
#[cfg(debug_assertions)]
use crate::chunk::ChunkHeader;
mod any;
pub use any::{AnyChunk, AnyChunkNextIter, AnyChunkPrevIter, AnyStats};
pub struct Stats<'a, A, S = BumpSettings>
where
S: BumpAllocatorSettings,
{
chunk: RawChunk<A, S>,
marker: PhantomData<&'a ()>,
}
impl<A, S> Clone for Stats<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for Stats<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> PartialEq for Stats<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn eq(&self, other: &Self) -> bool {
self.chunk.header() == other.chunk.header()
}
}
impl<A, S> Eq for Stats<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Debug for Stats<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
AnyStats::from(*self).debug_format("Stats", f)
}
}
impl<'a, A, S> Stats<'a, A, S>
where
S: BumpAllocatorSettings,
{
#[inline]
pub(crate) fn from_raw_chunk(chunk: RawChunk<A, S>) -> Self {
Self {
chunk,
marker: PhantomData,
}
}
#[must_use]
pub fn count(self) -> usize {
let Some(current) = self.current_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.current_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.current_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.current_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.current_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) -> ChunkNextIter<'a, A, S> {
let Some(mut start) = self.current_chunk() else {
return ChunkNextIter { chunk: None };
};
while let Some(chunk) = start.prev() {
start = chunk;
}
ChunkNextIter { chunk: Some(start) }
}
#[must_use]
pub fn big_to_small(self) -> ChunkPrevIter<'a, A, S> {
let Some(mut start) = self.current_chunk() else {
return ChunkPrevIter { chunk: None };
};
while let Some(chunk) = start.next() {
start = chunk;
}
ChunkPrevIter { chunk: Some(start) }
}
#[must_use]
pub fn current_chunk(self) -> Option<Chunk<'a, A, S>> {
Some(Chunk {
chunk: self.chunk.as_non_dummy()?,
marker: self.marker,
})
}
#[inline]
#[must_use]
pub fn allocator(self) -> Option<&'a A> {
Some(self.current_chunk()?.allocator())
}
}
impl<'a, A, S> From<Chunk<'a, A, S>> for Stats<'a, A, S>
where
S: BumpAllocatorSettings,
{
fn from(chunk: Chunk<'a, A, S>) -> Self {
Stats {
chunk: *chunk.chunk,
marker: PhantomData,
}
}
}
impl<A, S> Default for Stats<'_, A, S>
where
S: BumpAllocatorSettings<GuaranteedAllocated = False>,
{
fn default() -> Self {
Self {
chunk: RawChunk::UNALLOCATED,
marker: PhantomData,
}
}
}
#[repr(transparent)]
pub struct Chunk<'a, A, S>
where
S: BumpAllocatorSettings,
{
pub(crate) chunk: NonDummyChunk<A, S>,
marker: PhantomData<&'a ()>,
}
impl<A, S> Clone for Chunk<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for Chunk<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> PartialEq for Chunk<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn eq(&self, other: &Self) -> bool {
self.chunk.header() == other.chunk.header()
}
}
impl<A, S> Eq for Chunk<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Debug for Chunk<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Chunk")
.field("allocated", &self.allocated())
.field("capacity", &self.capacity())
.finish()
}
}
impl<'a, A, S> Chunk<'a, A, S>
where
S: BumpAllocatorSettings,
{
#[cfg(debug_assertions)]
pub(crate) fn header(self) -> NonNull<ChunkHeader<A>> {
self.chunk.header()
}
#[must_use]
#[inline(always)]
pub fn prev(self) -> Option<Self> {
Some(Chunk {
chunk: self.chunk.prev()?,
marker: PhantomData,
})
}
#[must_use]
#[inline(always)]
pub fn next(self) -> Option<Self> {
Some(Chunk {
chunk: self.chunk.next()?,
marker: PhantomData,
})
}
#[must_use]
#[inline(always)]
pub fn iter_prev(self) -> ChunkPrevIter<'a, A, S> {
ChunkPrevIter { chunk: self.prev() }
}
#[must_use]
#[inline(always)]
pub fn iter_next(self) -> ChunkNextIter<'a, A, S> {
ChunkNextIter { chunk: self.next() }
}
#[must_use]
#[inline]
pub fn size(self) -> usize {
self.chunk.size().get()
}
#[inline]
#[must_use]
pub fn capacity(self) -> usize {
self.chunk.capacity()
}
#[inline]
#[must_use]
pub fn allocated(self) -> usize {
self.chunk.allocated()
}
#[inline]
#[must_use]
pub fn remaining(self) -> usize {
self.chunk.remaining()
}
#[inline]
#[must_use]
pub fn chunk_start(self) -> NonNull<u8> {
self.chunk.chunk_start()
}
#[inline]
#[must_use]
pub fn chunk_end(self) -> NonNull<u8> {
self.chunk.chunk_end()
}
#[inline]
#[must_use]
pub fn content_start(self) -> NonNull<u8> {
self.chunk.content_start()
}
#[inline]
#[must_use]
pub fn content_end(self) -> NonNull<u8> {
self.chunk.content_end()
}
#[inline]
#[must_use]
pub fn bump_position(self) -> NonNull<u8> {
self.chunk.pos()
}
#[inline]
#[must_use]
pub fn allocator(self) -> &'a A {
self.chunk.allocator()
}
}
pub struct ChunkPrevIter<'a, A, S>
where
S: BumpAllocatorSettings,
{
#[expect(missing_docs)]
pub chunk: Option<Chunk<'a, A, S>>,
}
impl<A, S> Clone for ChunkPrevIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for ChunkPrevIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> PartialEq for ChunkPrevIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn eq(&self, other: &Self) -> bool {
self.chunk == other.chunk
}
}
impl<A, S> Eq for ChunkPrevIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Default for ChunkPrevIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn default() -> Self {
Self { chunk: None }
}
}
impl<'a, A, S> Iterator for ChunkPrevIter<'a, A, S>
where
S: BumpAllocatorSettings,
{
type Item = Chunk<'a, A, S>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let chunk = self.chunk?;
self.chunk = chunk.prev();
Some(chunk)
}
}
impl<A, S> FusedIterator for ChunkPrevIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Debug for ChunkPrevIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(*self).finish()
}
}
pub struct ChunkNextIter<'a, A, S>
where
S: BumpAllocatorSettings,
{
#[expect(missing_docs)]
pub chunk: Option<Chunk<'a, A, S>>,
}
impl<A, S> Clone for ChunkNextIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn clone(&self) -> Self {
*self
}
}
impl<A, S> Copy for ChunkNextIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> PartialEq for ChunkNextIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn eq(&self, other: &Self) -> bool {
self.chunk == other.chunk
}
}
impl<A, S> Eq for ChunkNextIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Default for ChunkNextIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn default() -> Self {
Self { chunk: None }
}
}
impl<'a, A, S> Iterator for ChunkNextIter<'a, A, S>
where
S: BumpAllocatorSettings,
{
type Item = Chunk<'a, A, S>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let chunk = self.chunk?;
self.chunk = chunk.next();
Some(chunk)
}
}
impl<A, S> FusedIterator for ChunkNextIter<'_, A, S> where S: BumpAllocatorSettings {}
impl<A, S> Debug for ChunkNextIter<'_, A, S>
where
S: BumpAllocatorSettings,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_list().entries(self.map(Chunk::size)).finish()
}
}