tagged_pointer/ptr/
mod.rs

1/*
2 * Copyright 2021-2024 taylor.fish <contact@taylor.fish>
3 *
4 * This file is part of tagged-pointer.
5 *
6 * tagged-pointer is licensed under the Apache License, Version 2.0
7 * (the "License"); you may not use tagged-pointer except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19use crate::Bits;
20use core::cmp::Ordering;
21use core::marker::PhantomData;
22use core::mem;
23use core::ptr::NonNull;
24
25#[cfg_attr(feature = "fallback", path = "fallback.rs")]
26#[cfg_attr(not(feature = "fallback"), path = "impl.rs")]
27mod ptr_impl;
28use ptr_impl::PtrImpl;
29
30trait NumBits {
31    const BITS: u32;
32}
33
34impl<T> NumBits for PhantomData<T> {
35    const BITS: u32 = mem::align_of::<T>().trailing_zeros();
36}
37
38struct ConstBits<const N: Bits>;
39
40impl<const N: Bits> NumBits for ConstBits<N> {
41    const BITS: u32 = {
42        const_assert!(N as u32 as Bits == N, "`BITS` is too large");
43        N as _
44    };
45}
46
47macro_rules! check_bits {
48    ($tz_bits:expr, $n:literal, $msg:literal $(,)?) => {
49        const_assert!($tz_bits.0 != $n || $tz_bits.1 <= $n, $msg);
50    };
51}
52
53impl<T, B: NumBits> PtrImpl<T, B> {
54    /// The number of tag bits that can be stored.
55    pub const BITS: u32 = B::BITS;
56
57    /// The alignment required to store [`Self::BITS`] tag bits. Separate
58    /// compile-time checks ensure this value doesn't overflow.
59    pub const ALIGNMENT: usize = 1_usize.wrapping_shl(Self::BITS);
60
61    /// The bitmask that should be applied to the tag to ensure that it is
62    /// smaller than [`Self::ALIGNMENT`]. Since the alignment is always a power
63    /// of 2, simply subtract 1 from the alignment.
64    pub const MASK: usize = Self::ALIGNMENT - 1;
65
66    const ASSERT: bool = {
67        let bits = Self::BITS;
68        let size = mem::size_of::<T>();
69        let align = mem::align_of::<T>();
70        let tz = mem::align_of::<T>().trailing_zeros();
71        // Ensure `BITS` isn't too large.
72        let c = (tz, bits);
73        check_bits!(c, 0, "`BITS` must be 0 (alignment of T is 1)");
74        check_bits!(c, 1, "`BITS` must be <= 1 (alignment of T is 2)");
75        check_bits!(c, 2, "`BITS` must be <= 2 (alignment of T is 4)");
76        check_bits!(c, 3, "`BITS` must be <= 3 (alignment of T is 8)");
77        check_bits!(c, 4, "`BITS` must be <= 4 (alignment of T is 16)");
78        check_bits!(c, 5, "`BITS` must be <= 5 (alignment of T is 32)");
79        check_bits!(c, 6, "`BITS` must be <= 6 (alignment of T is 64)");
80        const_assert!(
81            bits <= tz,
82            "`BITS` cannot exceed align_of::<T>().trailing_zeros()",
83        );
84        // Ensure `1 << BITS` doesn't overflow.
85        const_assert!(
86            1_usize.checked_shl(bits).is_some(),
87            "`BITS` must be less than number of bits in `usize`",
88        );
89        // Assumption about the alignment of `T`. This should always succeed.
90        const_assert!(align.is_power_of_two());
91        // Assumption about the size of `T`. This should always succeed.
92        const_assert!(size == 0 || size >= align);
93        // Assumption about the size of `T`. This should always succeed.
94        const_assert!(
95            size % align == 0,
96            "expected size of `T` to be a multiple of alignment",
97        );
98        true
99    };
100
101    /// Compile-time checks.
102    fn assert() {
103        // This run-time assertion will always succeed (and will likely be
104        // optimized out), but makes it clear that we need the constant to be
105        // evaluated (this type's soundness relies on its validity).
106        assert!(Self::ASSERT);
107    }
108}
109
110impl<T, B> Clone for PtrImpl<T, B> {
111    fn clone(&self) -> Self {
112        *self
113    }
114}
115
116impl<T, B> Copy for PtrImpl<T, B> {}
117
118impl<T, B> Eq for PtrImpl<T, B> {}
119
120impl<T, B> PartialOrd for PtrImpl<T, B> {
121    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
122        Some(self.cmp(other))
123    }
124}
125
126macro_rules! impl_tagged_ptr_common {
127    (
128        [$($ty_params:tt)*],
129        [$($ty_args:tt)*],
130        $doctest_context:literal $(,)?
131    ) => { const _: () = {
132        use core::cmp::Ordering;
133        use core::fmt;
134        use core::hash::{Hash, Hasher};
135        use core::ptr::NonNull;
136
137        impl<$($ty_params)*> TaggedPtr<$($ty_args)*> {
138            /// Gets the pointer and tag stored by the tagged pointer.
139            ///
140            /// If you need both the pointer and tag, this method may be more
141            /// efficient than calling [`Self::ptr`] and [`Self::tag`]
142            /// separately.
143            pub fn get(self) -> (NonNull<T>, usize) {
144                self.0.get()
145            }
146
147            /// Gets the pointer stored by the tagged pointer, without the tag.
148            /// Equivalent to [`self.get().0`](Self::get).
149            pub fn ptr(self) -> NonNull<T> {
150                self.get().0
151            }
152
153            /// Sets the pointer without modifying the tag.
154            ///
155            /// This method is simply equivalent to:
156            ///
157            /// ```
158            #[doc = $doctest_context]
159            /// # use core::ptr::NonNull;
160            /// # trait Ext<T> { fn f(&mut self, ptr: NonNull<T>); }
161            /// # impl<T> Ext<T> for TaggedPtr<T> {
162            /// # fn f(&mut self, ptr: NonNull<T>) {
163            /// *self = Self::new(ptr, self.tag());
164            /// # }}
165            /// ```
166            ///
167            /// See [`Self::new`] for information on argument validity and
168            /// panics.
169            pub fn set_ptr(&mut self, ptr: NonNull<T>) {
170                *self = Self::new(ptr, self.tag());
171            }
172
173            /// Gets the tag stored by the tagged pointer. Equivalent to
174            /// [`self.get().1`](Self::get).
175            pub fn tag(self) -> usize {
176                self.get().1
177            }
178
179            /// Sets the tag without modifying the pointer.
180            ///
181            /// This method is simply equivalent to:
182            ///
183            /// ```
184            #[doc = $doctest_context]
185            /// # trait Ext { fn f(&mut self, tag: usize); }
186            /// # impl<T> Ext for TaggedPtr<T> {
187            /// # fn f(&mut self, tag: usize) {
188            /// *self = Self::new(self.ptr(), tag);
189            /// # }}
190            /// ```
191            ///
192            /// See [`Self::new`] for more information.
193            pub fn set_tag(&mut self, tag: usize) {
194                *self = Self::new(self.ptr(), tag);
195            }
196        }
197
198        impl<$($ty_params)*> Clone for TaggedPtr<$($ty_args)*> {
199            fn clone(&self) -> Self {
200                *self
201            }
202        }
203
204        impl<$($ty_params)*> Copy for TaggedPtr<$($ty_args)*> {}
205
206        impl<$($ty_params)*> Eq for TaggedPtr<$($ty_args)*> {}
207
208        impl<$($ty_params)*> PartialEq for TaggedPtr<$($ty_args)*> {
209            fn eq(&self, other: &Self) -> bool {
210                self.0 == other.0
211            }
212        }
213
214        impl<$($ty_params)*> Ord for TaggedPtr<$($ty_args)*> {
215            fn cmp(&self, other: &Self) -> Ordering {
216                self.0.cmp(&other.0)
217            }
218        }
219
220        impl<$($ty_params)*> PartialOrd for TaggedPtr<$($ty_args)*> {
221            fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
222                Some(self.cmp(other))
223            }
224        }
225
226        impl<$($ty_params)*> Hash for TaggedPtr<$($ty_args)*> {
227            fn hash<H: Hasher>(&self, state: &mut H) {
228                self.0.hash(state);
229            }
230        }
231
232        impl<$($ty_params)*> fmt::Debug for TaggedPtr<$($ty_args)*> {
233            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234                let (ptr, tag) = self.get();
235                f.debug_struct("TaggedPtr")
236                    .field("ptr", &ptr)
237                    .field("tag", &tag)
238                    .finish()
239            }
240        }
241    }; };
242}
243
244pub mod implied;
245
246with_bits_doc! {
247    /// A tagged pointer: a space-efficient representation of a pointer and
248    /// integer tag.
249    ///
250    /// This type stores a pointer and an integer tag without taking up more
251    /// space than a normal pointer. Conceptually, this type behaves as if it
252    /// contains a [`NonNull<T>`] and a certain number of bits of an integer
253    /// tag.
254    #[repr(transparent)]
255    pub struct TaggedPtr<T, const BITS: Bits>(PtrImpl<T, ConstBits<BITS>>);
256}
257
258impl<T, const BITS: Bits> TaggedPtr<T, BITS> {
259    /// The number of tag bits that this tagged pointer can store.
260    pub const BITS: u32 = BITS as _;
261
262    /// The maximum tag (inclusive) that this tagged pointer can store. Equal
263    /// to `(1 << BITS) - 1` (i.e., one less than 2 to the power of `BITS`).
264    pub const MAX_TAG: usize = Self::max_tag();
265
266    // Separate function so Rustdoc doesn't show the expression
267    const fn max_tag() -> usize {
268        PtrImpl::<T, ConstBits<BITS>>::MASK
269    }
270
271    /// Creates a new tagged pointer. Only the lower `BITS` bits of `tag` are
272    /// stored.
273    ///
274    /// # Panics
275    ///
276    /// `ptr` must be aligned to at least 2<sup>`BITS`</sup> (i.e.,
277    /// `1 << BITS`) or panics may occur. It is recommended that `ptr` be
278    /// properly aligned for `T`, which automatically fulfills this requirement
279    /// due to the restriction on `BITS` above.
280    pub fn new(ptr: NonNull<T>, tag: usize) -> Self {
281        Self(PtrImpl::new(ptr, tag))
282    }
283
284    /// Equivalent to [`Self::new`] but without some runtime checks.
285    ///
286    /// # Safety
287    ///
288    /// * `ptr` must be aligned to at least 2<sup>`BITS`</sup>
289    ///   (i.e., `1 << BITS`).
290    /// * `tag` cannot be greater than [`Self::MAX_TAG`].
291    pub unsafe fn new_unchecked(ptr: NonNull<T>, tag: usize) -> Self {
292        // SAFETY: Ensured by caller.
293        Self(unsafe { PtrImpl::new_unchecked(ptr, tag) })
294    }
295
296    /// Like [`Self::new_unchecked`], but the pointer must be dereferenceable,
297    /// which allows better optimization.
298    ///
299    /// # Safety
300    ///
301    /// All conditions of [`Self::new_unchecked`] must be upheld, plus at least
302    /// the first 2<sup>`BITS`</sup> (i.e., `1 << BITS`) bytes of `ptr` must
303    /// be "dereferenceable" in the sense defined by
304    /// [`core::ptr`](core::ptr#safety).
305    pub unsafe fn new_unchecked_dereferenceable(
306        ptr: NonNull<T>,
307        tag: usize,
308    ) -> Self {
309        // SAFETY: Ensured by caller.
310        Self(unsafe { PtrImpl::new_unchecked_dereferenceable(ptr, tag) })
311    }
312}
313
314impl_tagged_ptr_common!(
315    [T, const BITS: Bits],
316    [T, BITS],
317    "# type TaggedPtr<T> = tagged_pointer::TaggedPtr<T, 0>;",
318);