tagged_pointer/ptr/
implied.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 super::PtrImpl;
20use core::ptr::NonNull;
21
22/// A tagged pointer with the maximum tag size for the given type.
23///
24/// This type behaves like [`crate::TaggedPtr`] but doesn't have a `BITS`
25/// parameter that determines how many tag bits to store. Instead, this type
26/// uses the largest possible tag size for an *aligned* pointer to `T`; see
27/// [`Self::BITS`] for the exact calculation.
28///
29/// Unlike [`crate::TaggedPtr`], this type always requires pointers to be
30/// properly aligned, even if you don't need all the available tag bits;
31/// see [`Self::new`].
32#[repr(transparent)]
33pub struct TaggedPtr<T>(PtrImpl<T>);
34
35impl<T> TaggedPtr<T> {
36    /// The number of tag bits that this tagged pointer can store. Equal to
37    /// <code>[align_of]::\<T>().[trailing_zeros]\()</code> (because alignment
38    /// is always a power of 2, this is the base-2 logarithm of the alignment
39    /// of `T`).
40    ///
41    /// [align_of]: core::mem::align_of
42    /// [trailing_zeros]: usize::trailing_zeros
43    pub const BITS: u32 = Self::bits();
44
45    // Separate function so Rustdoc doesn't show the expression
46    const fn bits() -> u32 {
47        PtrImpl::<T>::BITS
48    }
49
50    /// The maximum tag (inclusive) that this tagged pointer can store. Equal
51    /// to <code>[align_of]::\<T>() - 1</code>.
52    ///
53    /// [align_of]: core::mem::align_of
54    pub const MAX_TAG: usize = Self::max_tag();
55
56    // Separate function so Rustdoc doesn't show the expression
57    const fn max_tag() -> usize {
58        PtrImpl::<T>::MASK
59    }
60
61    /// Creates a new tagged pointer. Only the lower [`Self::BITS`] bits of
62    /// `tag` are stored.
63    ///
64    /// # Panics
65    ///
66    /// This function may panic if `ptr` is not properly aligned (i.e., aligned
67    /// to at least [`align_of::<T>()`][core::mem::align_of]).
68    pub fn new(ptr: NonNull<T>, tag: usize) -> Self {
69        Self(PtrImpl::new(ptr, tag))
70    }
71
72    /// Equivalent to [`Self::new`] but without some runtime checks.
73    ///
74    /// # Safety
75    ///
76    /// * `ptr` must be properly aligned (i.e., aligned to at least
77    ///   [`align_of::<T>()`][core::mem::align_of]).
78    /// * `tag` cannot be greater than [`Self::MAX_TAG`].
79    pub unsafe fn new_unchecked(ptr: NonNull<T>, tag: usize) -> Self {
80        // SAFETY: Ensured by caller.
81        Self(unsafe { PtrImpl::new_unchecked(ptr, tag) })
82    }
83
84    /// Like [`Self::new_unchecked`], but the pointer must be dereferenceable,
85    /// which allows better optimization.
86    ///
87    /// # Safety
88    ///
89    /// All conditions of [`Self::new_unchecked`] must be upheld, plus `ptr`
90    /// must be "dereferenceable" in the sense defined by
91    /// [`core::ptr`](core::ptr#safety).
92    pub unsafe fn new_unchecked_dereferenceable(
93        ptr: NonNull<T>,
94        tag: usize,
95    ) -> Self {
96        // SAFETY: Ensured by caller.
97        Self(unsafe { PtrImpl::new_unchecked_dereferenceable(ptr, tag) })
98    }
99}
100
101impl_tagged_ptr_common!([T], [T], "# use tagged_pointer::implied::TaggedPtr;");