tagptr/lib.rs
1//! Strongly typed pointers with reserved space for storing additional bit
2//! patterns within the same memory word.
3//!
4//! # Motivation
5//!
6//! In low-level concurrent programming (synchronization primitives,
7//! lock-free data structures, etc) it is often required to store additional
8//! state information (tags) alongside pointers to objects in memory, since
9//! most atomic CPU instructions operate on pointer-wide memory words.
10//! The marked pointer types provided by this crate encapsulate the logic and
11//! pointer arithmetic for composing (creating), decomposing and mutating
12//! such pointers and tag values.
13//!
14//! # Tag Bits and Type Alignment
15//!
16//! The possible space for storing tag bits in a pointer is determined by the
17//! alignment of the pointed-to type, as long as the pointer is well-aligned
18//! (e.g., not packed).
19//! For instance, pointers to types with an alignment of 2 (2^1) bytes (e.g.,
20//! `u16`) never use the first of their lower bits (i.e., it is always zero),
21//! pointers to types with an alignment of 8 (2^3) bytes such as `u64` never
22//! use their 3 lowest bits and so on.
23//! Great care must be taken at all times to avoid over- or underflows in the
24//! usually highly restricted range of valid tags for common tag sizes when
25//! doing arithmetic operations.
26//! Any operations resulting in tag values outside of their valid range will
27//! invariably corrupt the bits representing the pointer and hence invoke
28//! undefined behavior when dereferencing these pointers.
29//!
30//! Constructing a type such as `TagPtr<u64, 4>` is hence usually a user error,
31//! since a pointer to a `u64` has only 3 unused bits.
32//! The resulting type would consider the first actual bit of the pointer to be
33//! part of its tag and return a potentially corrupted pointer in methods such
34//! as [`decompose`][TagPtr::decompose].
35//! The [`has_sufficient_alignment`] and [`assert_alignment`] functions can be
36//! used to explicitly check for or assert this property.
37//! There is, however, one exception where using an otherwise ill-formed tag
38//! pointer type is valid:
39//! After composing a well-formed tag pointer instance (e.g., `TagPtr<u64, 3>`)
40//! it is valid to [`cast`][TagPtr::cast] it to a type with a smaller alignment
41//! and the same number of tag bits such as `TagPtr<(), 3>` for the purpose of
42//! type-erasure.
43//!
44//! # Example
45//!
46//! Storing a boolean status flag alongside the pointer to a mutable `u64`:
47//!
48//! ```
49//! type TagPtr = tagptr::TagPtr<u64, 3>;
50//!
51//! let mut val = 0xCAFE;
52//! let is_ok = true;
53//!
54//! let ptr = TagPtr::compose(&mut val, is_ok as usize);
55//! let (reference, tag) = unsafe { ptr.decompose_mut() };
56//! assert_eq!(reference, Some(&mut 0xCAFE));
57//! assert_eq!(tag == 1, true);
58//! ```
59
60#![no_std]
61
62#[cfg(test)]
63extern crate std;
64
65#[macro_use]
66mod macros;
67
68mod imp {
69 mod atomic;
70 mod non_null;
71 mod ptr;
72}
73
74use core::{marker::PhantomData, mem, ptr::NonNull, sync::atomic::AtomicUsize};
75
76// *************************************************************************************************
77// AtomicTagPtr (impl in "imp/atomic.rs")
78// *************************************************************************************************
79
80/// A raw pointer type which can be safely shared between threads and which can
81/// use up to `N` of its lower bits to store additional information (the *tag*).
82///
83/// This type has the same in-memory representation as a `*mut T`.
84/// It is mostly identical to [`AtomicPtr`][atomic], except that all of its
85/// methods take or return a [`TagPtr`] instead of `*mut T`.
86/// See the [crate][crate] level documentation for restrictions on the value of
87/// `N`.
88///
89/// [atomic]: core::sync::atomic::AtomicPtr
90#[repr(transparent)]
91pub struct AtomicTagPtr<T, const N: usize> {
92 inner: AtomicUsize,
93 _marker: PhantomData<*mut T>,
94}
95
96// *************************************************************************************************
97// TagPtr (impl in "imp/ptr.rs")
98// *************************************************************************************************
99
100/// A raw, unsafe pointer type like `*mut T` which can use up to `N` of its
101/// lower bits to store additional information (the *tag*).
102///
103/// This type has the same in-memory representation as a `*mut T`.
104/// See the [crate][crate] level documentation for restrictions on the value of
105/// `N`.
106#[repr(transparent)]
107pub struct TagPtr<T, const N: usize> {
108 inner: *mut T,
109 _marker: PhantomData<()>, // the "fake" marker allows to use the same macro for all pointers
110}
111
112// *************************************************************************************************
113// TagNonNull (impl in "imp/non_null.rs")
114// *************************************************************************************************
115
116/// A non-nullable tagged raw pointer type similar to [`NonNull`] which can use
117/// up to `N` of its lower bits to store additional information (the *tag*).
118///
119/// This type has the same in-memory representation as a `NonNull<T>`.
120/// See the [crate][crate] level documentation for restrictions on the value of
121/// `N`.
122///
123/// # Invariants
124///
125/// This type imposes stricter construction requirements than a regular
126/// [`NonNull`], since it requires the pointer to be non-null even after its `N`
127/// tag bits are stripped off as well.
128/// For instance, the value `0x1` can be used to construct a valid (but not
129/// dereferencable) [`NonNull`] since it is not zero, but it can not be used to
130/// construct e.g. a valid `TagNonNull<u64, 1>`, since its only non-zero bit
131/// would be considered to represent the tag and the value of the pointer would
132/// be 0.
133/// For valid, well-aligned pointers, this is usually not a concern.
134#[repr(transparent)]
135pub struct TagNonNull<T, const N: usize> {
136 inner: NonNull<T>,
137 _marker: PhantomData<()>,
138}
139
140// *************************************************************************************************
141// Null
142// *************************************************************************************************
143
144/// A type representing a `null` pointer with potential tag bits.
145///
146/// The contained `usize` is the value of the pointer's tag.
147#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
148#[repr(transparent)]
149pub struct Null(pub usize);
150
151/********** impl inherent *************************************************************************/
152
153impl Null {
154 /// Returns the tag value.
155 #[inline]
156 pub fn tag(self) -> usize {
157 self.0
158 }
159}
160
161/********** public functions **********************************************************************/
162
163/// Returns `true` if the alignment of `T` is large enough so a pointer to an
164/// instance may store the given number of `tag_bits`.
165#[inline]
166pub const fn has_sufficient_alignment<T>(tag_bits: usize) -> bool {
167 lower_bits::<T>() >= tag_bits
168}
169
170/// Asserts that the alignment of `U` is large enough so a pointer to an
171/// instance may store `N` tag bits.
172///
173/// # Panics
174///
175/// This function panics if the alignment of `U` is insufficient for storing
176/// `N` tag bits.
177#[inline]
178pub fn assert_alignment<T, const N: usize>() {
179 assert!(
180 has_sufficient_alignment::<T>(N),
181 "the respective type has insufficient alignment for storing N tag bits"
182 );
183}
184
185/********** helper functions **********************************************************************/
186
187/// Composes the given `ptr` with `tag` and returns the composed marked pointer
188/// as a raw `*mut T`.
189///
190/// # Panics
191///
192/// Panics in *debug builds only* if `ptr` is not well aligned, i.e., if it
193/// contains any bits in its lower bits reserved for the tag value.
194#[inline(always)]
195fn compose<T, const N: usize>(ptr: *mut T, tag: usize) -> *mut T {
196 debug_assert_eq!(ptr as usize & mark_mask(N), 0, "tag bits in raw pointer must be zeroed");
197 ((ptr as usize) | (mark_mask(N) & tag)) as *mut _
198}
199
200/// Decomposes the integer representation of a `ptr` for a given number
201/// of `tag_bits` into only a raw pointer stripped of its tag.
202#[inline(always)]
203const fn decompose_ptr<T>(ptr: usize, tag_bits: usize) -> *mut T {
204 (ptr & !mark_mask(tag_bits)) as *mut _
205}
206
207/// Decomposes the integer representation of a `ptr` for a given number
208/// of `tag_bits` into only a separated tag value.
209#[inline(always)]
210const fn decompose_tag<T>(ptr: usize, tag_bits: usize) -> usize {
211 ptr & mark_mask(tag_bits)
212}
213
214/// Returns the (alignment-dependent) number of unused lower bits in a pointer
215/// to type `T`.
216#[inline(always)]
217const fn lower_bits<T>() -> usize {
218 mem::align_of::<T>().trailing_zeros() as usize
219}
220
221/// Returns the bit-mask for the lower bits containing the tag value.
222#[inline(always)]
223const fn mark_mask(tag_bits: usize) -> usize {
224 (1 << tag_bits) - 1
225}