Skip to main content

bump_scope/chunk/
header.rs

1use core::{cell::Cell, ptr::NonNull};
2
3use crate::{polyfill::non_null, settings::BumpAllocatorSettings};
4
5/// The chunk header that lives at
6/// - the start of the allocation when upwards bumping
7/// - the end of the allocation when downwards bumping
8///
9/// All non-`Cell` fields are immutable.
10#[repr(C, align(16))]
11pub(crate) struct ChunkHeader<A = ()> {
12    pub(crate) pos: Cell<NonNull<u8>>,
13    pub(crate) end: NonNull<u8>,
14
15    pub(crate) prev: Cell<Option<NonNull<Self>>>,
16    pub(crate) next: Cell<Option<NonNull<Self>>>,
17
18    pub(crate) allocator: A,
19}
20
21/// Wraps a [`ChunkHeader`], making it Sync so it can be used as a static.
22/// The dummy chunk is never mutated, so this is fine.
23struct DummyChunkHeader(ChunkHeader);
24
25unsafe impl Sync for DummyChunkHeader {}
26
27/// We create a dummy chunks with a negative capacity, so all allocations will fail.
28///
29/// The pointers used for `pos` and `end` are chosen to be pointers into the same static dummy chunk.
30///
31/// It's irrelevant where the pointers point to, they just need to:
32/// - be aligned to [`MIN_CHUNK_ALIGN`]
33/// - denote a negative capacity (currently guaranteed to be -16)
34/// - point to some existing object, not a dangling pointer since a dangling pointer could
35///   theoretically be a valid pointer to some other chunk
36macro_rules! dummy_chunk {
37    ($name:ident) => {
38        pub(crate) const fn $name<S: BumpAllocatorSettings>() -> NonNull<ChunkHeader> {
39            static UP_CHUNK: DummyChunkHeader = DummyChunkHeader(ChunkHeader {
40                // SAFETY: Due to `align(16)`, `ChunkHeader`'s size is `>= 16`, so a `byte_add` of 16 is in bounds.
41                // We could also use `.add(1)` here, but we currently guarantee a capacity of -16
42                pos: Cell::new(unsafe { UP_CHUNK_PTR.cast().byte_add(16) }),
43                end: UP_CHUNK_PTR.cast(),
44                prev: Cell::new(None),
45                next: Cell::new(None),
46                allocator: (),
47            });
48
49            static DOWN_CHUNK: DummyChunkHeader = DummyChunkHeader(ChunkHeader {
50                pos: Cell::new(DOWN_CHUNK_PTR.cast()),
51                // SAFETY: Due to `align(16)`, `ChunkHeader`'s size is `>= 16`, so a `byte_add` of 16 is in bounds.
52                // We could also use `.add(1)` here, but we currently guarantee a capacity of -16
53                end: unsafe { DOWN_CHUNK_PTR.cast().byte_add(16) },
54                prev: Cell::new(None),
55                next: Cell::new(None),
56                allocator: (),
57            });
58
59            const UP_CHUNK_PTR: NonNull<ChunkHeader> = non_null::from_ref(&UP_CHUNK.0);
60            const DOWN_CHUNK_PTR: NonNull<ChunkHeader> = non_null::from_ref(&DOWN_CHUNK.0);
61
62            if S::UP { UP_CHUNK_PTR } else { DOWN_CHUNK_PTR }
63        }
64    };
65}
66
67impl ChunkHeader {
68    dummy_chunk!(unallocated);
69    dummy_chunk!(claimed);
70}