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