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);