1use core::{fmt, iter::FusedIterator, marker::PhantomData, ptr::NonNull};
2
3use crate::{chunk::ChunkHeader, settings::BumpAllocatorSettings};
4
5use super::{Chunk, ChunkNextIter, ChunkPrevIter, Stats};
6
7#[derive(Default, Clone, Copy, PartialEq, Eq)]
11pub struct AnyStats<'a> {
12 chunk: Option<AnyChunk<'a>>,
13}
14
15impl<S> From<Stats<'_, S>> for AnyStats<'_>
16where
17 S: BumpAllocatorSettings,
18{
19 fn from(value: Stats<'_, S>) -> Self {
20 Self {
21 chunk: value.current_chunk().map(Into::into),
22 }
23 }
24}
25
26impl fmt::Debug for AnyStats<'_> {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 self.debug_format("AnyStats", f)
29 }
30}
31
32impl<'a> AnyStats<'a> {
33 #[must_use]
35 pub fn count(self) -> usize {
36 let Some(current) = self.chunk else { return 0 };
37
38 let mut sum = 1;
39 current.iter_prev().for_each(|_| sum += 1);
40 current.iter_next().for_each(|_| sum += 1);
41 sum
42 }
43
44 #[must_use]
46 pub fn size(self) -> usize {
47 let Some(current) = self.chunk else { return 0 };
48
49 let mut sum = current.size();
50 current.iter_prev().for_each(|chunk| sum += chunk.size());
51 current.iter_next().for_each(|chunk| sum += chunk.size());
52 sum
53 }
54
55 #[must_use]
57 pub fn capacity(self) -> usize {
58 let Some(current) = self.chunk else { return 0 };
59
60 let mut sum = current.capacity();
61 current.iter_prev().for_each(|chunk| sum += chunk.capacity());
62 current.iter_next().for_each(|chunk| sum += chunk.capacity());
63 sum
64 }
65
66 #[must_use]
72 pub fn allocated(self) -> usize {
73 let Some(current) = self.chunk else { return 0 };
74
75 let mut sum = current.allocated();
76 current.iter_prev().for_each(|chunk| sum += chunk.capacity());
77 sum
78 }
79
80 #[must_use]
85 pub fn remaining(self) -> usize {
86 let Some(current) = self.chunk else { return 0 };
87
88 let mut sum = current.remaining();
89 current.iter_next().for_each(|chunk| sum += chunk.capacity());
90 sum
91 }
92
93 #[must_use]
95 pub fn small_to_big(self) -> AnyChunkNextIter<'a> {
96 let Some(mut start) = self.chunk else {
97 return AnyChunkNextIter { chunk: None };
98 };
99
100 while let Some(chunk) = start.prev() {
101 start = chunk;
102 }
103
104 AnyChunkNextIter { chunk: Some(start) }
105 }
106
107 #[must_use]
109 pub fn big_to_small(self) -> AnyChunkPrevIter<'a> {
110 let Some(mut start) = self.chunk else {
111 return AnyChunkPrevIter { chunk: None };
112 };
113
114 while let Some(chunk) = start.next() {
115 start = chunk;
116 }
117
118 AnyChunkPrevIter { chunk: Some(start) }
119 }
120
121 #[must_use]
123 pub fn current_chunk(self) -> Option<AnyChunk<'a>> {
124 self.chunk
125 }
126
127 pub(crate) fn debug_format(self, name: &str, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.debug_struct(name)
129 .field("allocated", &self.allocated())
130 .field("capacity", &self.capacity())
131 .finish()
132 }
133}
134
135impl<'a> From<AnyChunk<'a>> for AnyStats<'a> {
136 fn from(chunk: AnyChunk<'a>) -> Self {
137 Self { chunk: Some(chunk) }
138 }
139}
140
141#[derive(Clone, Copy, PartialEq, Eq)]
145pub struct AnyChunk<'a> {
146 header: NonNull<ChunkHeader>,
147 marker: PhantomData<&'a ()>,
148}
149
150impl<S> From<Chunk<'_, S>> for AnyChunk<'_>
151where
152 S: BumpAllocatorSettings,
153{
154 fn from(value: Chunk<'_, S>) -> Self {
155 Self {
156 header: value.chunk.header().cast(),
157 marker: PhantomData,
158 }
159 }
160}
161
162impl fmt::Debug for AnyChunk<'_> {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 f.debug_struct("Chunk")
165 .field("allocated", &self.allocated())
166 .field("capacity", &self.capacity())
167 .finish()
168 }
169}
170
171impl<'a> AnyChunk<'a> {
172 fn header(&self) -> &ChunkHeader {
173 unsafe { self.header.as_ref() }
174 }
175
176 #[inline]
177 pub(crate) fn is_upwards_allocating(self) -> bool {
178 let header = self.header.addr();
179 let end = self.header().end.addr();
180 end > header
181 }
182
183 #[must_use]
185 #[inline(always)]
186 pub fn prev(self) -> Option<Self> {
187 Some(AnyChunk {
188 header: self.header().prev.get()?,
189 marker: PhantomData,
190 })
191 }
192
193 #[must_use]
195 #[inline(always)]
196 pub fn next(self) -> Option<Self> {
197 Some(AnyChunk {
198 header: self.header().next.get()?,
199 marker: PhantomData,
200 })
201 }
202
203 #[must_use]
205 #[inline(always)]
206 pub fn iter_prev(self) -> AnyChunkPrevIter<'a> {
207 AnyChunkPrevIter { chunk: self.prev() }
208 }
209
210 #[must_use]
212 #[inline(always)]
213 pub fn iter_next(self) -> AnyChunkNextIter<'a> {
214 AnyChunkNextIter { chunk: self.next() }
215 }
216
217 #[must_use]
219 #[inline]
220 pub fn size(self) -> usize {
221 let start = self.chunk_start();
222 let end = self.chunk_end();
223 end.addr().get() - start.addr().get()
224 }
225
226 #[must_use]
228 #[inline]
229 pub fn capacity(self) -> usize {
230 let start = self.content_start();
231 let end = self.content_end();
232 end.addr().get() - start.addr().get()
233 }
234
235 #[must_use]
242 #[inline]
243 pub fn allocated(self) -> usize {
244 if self.is_upwards_allocating() {
245 let start = self.content_start();
246 let end = self.bump_position();
247 end.addr().get() - start.addr().get()
248 } else {
249 let start = self.bump_position();
250 let end = self.content_end();
251 end.addr().get() - start.addr().get()
252 }
253 }
254
255 #[must_use]
261 #[inline]
262 pub fn remaining(self) -> usize {
263 if self.is_upwards_allocating() {
264 let start = self.bump_position();
265 let end = self.content_end();
266 end.addr().get() - start.addr().get()
267 } else {
268 let start = self.content_start();
269 let end = self.bump_position();
270 end.addr().get() - start.addr().get()
271 }
272 }
273
274 #[must_use]
276 #[inline]
277 pub fn chunk_start(self) -> NonNull<u8> {
278 if self.is_upwards_allocating() {
279 self.header.cast()
280 } else {
281 self.header().end
282 }
283 }
284
285 #[must_use]
287 #[inline]
288 pub fn chunk_end(self) -> NonNull<u8> {
289 if self.is_upwards_allocating() {
290 self.header().end
291 } else {
292 self.after_header()
293 }
294 }
295
296 #[must_use]
298 #[inline]
299 pub fn content_start(self) -> NonNull<u8> {
300 if self.is_upwards_allocating() {
301 self.after_header()
302 } else {
303 self.chunk_start()
304 }
305 }
306
307 #[must_use]
309 #[inline]
310 pub fn content_end(self) -> NonNull<u8> {
311 if self.is_upwards_allocating() {
312 self.chunk_end()
313 } else {
314 self.header.cast()
315 }
316 }
317
318 #[must_use]
323 #[inline]
324 pub fn bump_position(self) -> NonNull<u8> {
325 self.header().pos.get()
326 }
327
328 fn after_header(self) -> NonNull<u8> {
329 unsafe { self.header.add(1).cast() }
330 }
331}
332
333#[derive(Default, Clone, Copy, PartialEq, Eq)]
335pub struct AnyChunkPrevIter<'a> {
336 #[expect(missing_docs)]
337 pub chunk: Option<AnyChunk<'a>>,
338}
339
340impl<S> From<ChunkPrevIter<'_, S>> for AnyChunkPrevIter<'_>
341where
342 S: BumpAllocatorSettings,
343{
344 fn from(value: ChunkPrevIter<'_, S>) -> Self {
345 Self {
346 chunk: value.chunk.map(Into::into),
347 }
348 }
349}
350
351impl<'a> Iterator for AnyChunkPrevIter<'a> {
352 type Item = AnyChunk<'a>;
353
354 #[inline(always)]
355 fn next(&mut self) -> Option<Self::Item> {
356 let chunk = self.chunk?;
357 self.chunk = chunk.prev();
358 Some(chunk)
359 }
360}
361
362impl FusedIterator for AnyChunkPrevIter<'_> {}
363
364impl fmt::Debug for AnyChunkPrevIter<'_> {
365 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366 f.debug_list().entries(*self).finish()
367 }
368}
369
370#[derive(Default, Clone, Copy, PartialEq, Eq)]
372pub struct AnyChunkNextIter<'a> {
373 #[expect(missing_docs)]
374 pub chunk: Option<AnyChunk<'a>>,
375}
376
377impl<S> From<ChunkNextIter<'_, S>> for AnyChunkNextIter<'_>
378where
379 S: BumpAllocatorSettings,
380{
381 fn from(value: ChunkNextIter<'_, S>) -> Self {
382 Self {
383 chunk: value.chunk.map(Into::into),
384 }
385 }
386}
387
388impl<'a> Iterator for AnyChunkNextIter<'a> {
389 type Item = AnyChunk<'a>;
390
391 #[inline(always)]
392 fn next(&mut self) -> Option<Self::Item> {
393 let chunk = self.chunk?;
394 self.chunk = chunk.next();
395 Some(chunk)
396 }
397}
398
399impl FusedIterator for AnyChunkNextIter<'_> {}
400
401impl fmt::Debug for AnyChunkNextIter<'_> {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 f.debug_list().entries(self.map(AnyChunk::size)).finish()
404 }
405}
406
407#[test]
408fn check_from_impls() {
409 #![expect(dead_code, clippy::elidable_lifetime_names)]
410
411 use crate::{BumpScope, alloc::Allocator};
412
413 fn accepting_any_stats(_: AnyStats) {}
414 fn accepting_any_chunk(_: AnyChunk) {}
415 fn accepting_any_chunk_prev_iter(_: AnyChunkPrevIter) {}
416 fn accepting_any_chunk_next_iter(_: AnyChunkNextIter) {}
417
418 fn generic_bump<'a, A, S>(bump: &BumpScope<'a, A, S>)
419 where
420 A: Allocator,
421 S: BumpAllocatorSettings,
422 {
423 let stats = bump.stats();
424 accepting_any_stats(stats.into());
425 accepting_any_chunk(stats.current_chunk().unwrap().into());
426 accepting_any_chunk_next_iter(stats.small_to_big().into());
427 accepting_any_chunk_prev_iter(stats.big_to_small().into());
428 }
429}