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 {
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
19/// Wraps a [`ChunkHeader`], making it Sync so it can be used as a static.
20/// The dummy chunk is never mutated, so this is fine.
21struct DummyChunkHeader(ChunkHeader);
22
23unsafe impl Sync for DummyChunkHeader {}
24
25/// We create a dummy chunks with a negative capacity, so all allocations will fail.
26///
27/// The pointers used for `pos` and `end` are chosen to be pointers into the same static dummy chunk.
28///
29/// It's irrelevant where the pointers point to, they just need to:
30/// - be aligned to [`MIN_CHUNK_ALIGN`]
31/// - denote a negative capacity (currently guaranteed to be -16)
32/// - point to some existing object, not a dangling pointer since a dangling pointer could
33///   theoretically be a valid pointer to some other chunk
34macro_rules! dummy_chunk {
35    ($name:ident) => {
36        pub(crate) const fn $name<S: BumpAllocatorSettings>() -> NonNull<ChunkHeader> {
37            static UP_CHUNK: DummyChunkHeader = DummyChunkHeader(ChunkHeader {
38                // SAFETY: Due to `align(16)`, `ChunkHeader`'s size is `>= 16`, so a `byte_add` of 16 is in bounds.
39                // We could also use `.add(1)` here, but we currently guarantee a capacity of -16
40                pos: Cell::new(unsafe { UP_CHUNK_PTR.cast().byte_add(16) }),
41                end: UP_CHUNK_PTR.cast(),
42                prev: Cell::new(None),
43                next: Cell::new(None),
44            });
45
46            static DOWN_CHUNK: DummyChunkHeader = DummyChunkHeader(ChunkHeader {
47                pos: Cell::new(DOWN_CHUNK_PTR.cast()),
48                // SAFETY: Due to `align(16)`, `ChunkHeader`'s size is `>= 16`, so a `byte_add` of 16 is in bounds.
49                // We could also use `.add(1)` here, but we currently guarantee a capacity of -16
50                end: unsafe { DOWN_CHUNK_PTR.cast().byte_add(16) },
51                prev: Cell::new(None),
52                next: Cell::new(None),
53            });
54
55            const UP_CHUNK_PTR: NonNull<ChunkHeader> = non_null::from_ref(&UP_CHUNK.0);
56            const DOWN_CHUNK_PTR: NonNull<ChunkHeader> = non_null::from_ref(&DOWN_CHUNK.0);
57
58            if S::UP { UP_CHUNK_PTR } else { DOWN_CHUNK_PTR }
59        }
60    };
61}
62
63impl ChunkHeader {
64    dummy_chunk!(unallocated);
65    dummy_chunk!(claimed);
66}