Skip to main content

gc_arena/
zst_cache.rs

1use core::mem;
2
3use crate::{
4    collect::{Collect, Trace},
5    context::Mutation,
6    gc::Gc,
7};
8
9/// Provides an optimization for allocating [`Gc`] pointers to Zero Sized Types.
10///
11/// This type stores a single dummy allocation of the requested alignment. When an alloc method is
12/// called, if the given value is a ZST whose alignment is less than or equal to the given one, then
13/// the dummy allocation is cast to the correct type and that is returned instead.
14///
15/// All ZSTs allocated this way can share a single allocated pointer. This obviously breaks pointer
16/// uniqueness, so if pointer uniqueness is required, [`Gc::new`] should be used instead.
17#[derive(Copy, Clone)]
18pub struct ZstCache<'gc, const MAX_ALIGN: usize> {
19    cached_ptr: Gc<'gc, ()>,
20}
21
22unsafe impl<'gc, const MAX_ALIGN: usize> Collect<'gc> for ZstCache<'gc, MAX_ALIGN> {
23    const NEEDS_TRACE: bool = true;
24
25    fn trace<T: Trace<'gc>>(&self, cc: &mut T) {
26        cc.trace_gc(self.cached_ptr);
27    }
28}
29
30impl<'gc, const MAX_ALIGN: usize> ZstCache<'gc, MAX_ALIGN>
31where
32    Alignment<MAX_ALIGN>: ValidAlignment,
33{
34    pub fn new(mc: &Mutation<'gc>) -> Self {
35        let cached_ptr = Gc::erase(Gc::new_static(
36            mc,
37            <Alignment<MAX_ALIGN> as HasAlignedType>::AlignedType::default(),
38        ));
39        ZstCache { cached_ptr }
40    }
41
42    /// Returns the internally held pointer used as a ZST ptr cache.
43    ///
44    /// It will always be aligned to `MAX_ALIGN`.
45    pub fn cached_ptr(&self) -> Gc<'gc, ()> {
46        self.cached_ptr
47    }
48
49    /// Returns true if the given pointer is cached by this `ZstCache`.
50    #[inline]
51    pub fn is_cached<T: ?Sized>(&self, p: Gc<'gc, T>) -> bool {
52        Gc::ptr_eq(self.cached_ptr, Gc::erase(p))
53    }
54
55    /// Return the cached pointer as a pointer to `T`, if possible.
56    ///
57    /// If the given type `T` is not a ZST or has an alignment which is greater than `MAX_ALIGN`,
58    /// returns `None`.
59    ///
60    /// This method never performs any actual allocation.
61    #[inline]
62    pub fn alloc_zst<T: 'gc>(&self) -> Option<Gc<'gc, T>> {
63        if mem::size_of::<T>() == 0 && mem::align_of::<T>() <= MAX_ALIGN {
64            debug_assert!(Gc::as_ptr(self.cached_ptr).align_offset(mem::align_of::<T>()) == 0);
65            // SAFETY: The value is zero sized, and this pointer is at least of the correct
66            // alignment for the pointed to type.
67            Some(unsafe { Gc::cast::<T>(self.cached_ptr) })
68        } else {
69            None
70        }
71    }
72
73    /// Like [`Gc::new`], but returns the cached pointer if possible.
74    #[inline]
75    pub fn alloc<T: Collect<'gc>>(&self, mc: &Mutation<'gc>, t: T) -> Gc<'gc, T> {
76        if let Some(ptr) = self.alloc_zst() {
77            ptr
78        } else {
79            Gc::new(mc, t)
80        }
81    }
82
83    /// Like [`Gc::new_static`], but returns the cached pointer if possible.
84    #[inline]
85    pub fn alloc_static<T: 'static>(&self, mc: &Mutation<'gc>, t: T) -> Gc<'gc, T> {
86        if let Some(ptr) = self.alloc_zst() {
87            ptr
88        } else {
89            Gc::new_static(mc, t)
90        }
91    }
92}
93
94pub struct Alignment<const ALIGN: usize>;
95
96/// For all alignments `ALIGN` that `ZstCache` supports, [`Alignment<ALIGN>`] will implement this
97/// trait.
98///
99/// All positive powers of 2 up to 2^29 are supported.
100#[allow(private_bounds)]
101pub trait ValidAlignment: HasAlignedType {}
102
103impl<T: HasAlignedType> ValidAlignment for T {}
104
105trait HasAlignedType {
106    type AlignedType: Default;
107}
108
109macro_rules! impl_has_aligned_type {
110    ($($align:expr),* $(,)?) => {
111        $(
112            const _: () = {
113                #[repr(align($align))]
114                struct AlignedType;
115
116                impl Default for AlignedType {
117                    #[inline(always)]
118                    fn default() -> Self {
119                        Self
120                    }
121                }
122
123                impl HasAlignedType for Alignment<$align> {
124                    type AlignedType = AlignedType;
125                }
126            };
127        )*
128    };
129}
130
131impl_has_aligned_type!(
132    1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072,
133    262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728,
134    268435456, 536870912
135);