ointers/
lib.rs

1//! What do you call a pointer we stole the high bits off? An ointer.
2//!
3//! Ointers is a library for representing pointers where some bits have
4//! been stolen so that they may be used by the programmer for something
5//! else. In effect, it's a small amount of free storage
6//!
7//! Fully supports no_std, dependency-free.
8//!
9//! Ointers supports a handful of bit sources. It's up to you to determine
10//! when it is safe to use them.
11//!
12//! ## Alignment bits (const parameter A)
13//!
14//! If we know that a pointer's address will always be aligned to `A`
15//! bytes where A > 1, we can steal log2(A-1) bits. For an 8-byte
16//! aligned value, this provides a modest 3 bits.
17//!
18//! If you have values aligned to some larger width, you could get even
19//! more. It's common in parallel programming to pad data to a cache line
20//! by increasing its alignment requirements in order eliminate false
21//! sharing. Because a cache line on amd64 or aarch64 is effectively 128
22//! bytes thanks to prefetching, you can reclaim a respectable 7 extra
23//! bits.
24//!
25//! If your data is aligned wider still, the sky is the limit, but you
26//! could get an incredible 24 bits if you have 16MB-aligned data!
27//! Remember that the only alignment rust knows about is what is
28//! declared for the type, so you must create a newtype wrapper to
29//! take full advantage of large alignment sizes.
30//!
31//! | Bits | Min alignment |
32//! |------|---------------|
33//! | 1    |            2b |
34//! | 2    |            4b |
35//! | 3    |            8b |
36//! | 4    |           16b |
37//! | 5    |           32b |
38//! | 6    |           64b |
39//! | 7    |          128b |
40//! | 8    |          256b |
41//! | 9    |          512b |
42//! | 10   |            1k |
43//! | 11   |            2k |
44//! | 12   |            4k |
45//! | 13   |            8k |
46//! | 14   |           16k |
47//! | 15   |           32k |
48//! | 16   |           64k |
49//! | 17   |          128k |
50//! | 18   |          256k |
51//! | 19   |          512k |
52//! | 20   |            1m |
53//! | 21   |            2m |
54//! | 22   |            4m |
55//! | 23   |            8m |
56//! | 24   |           16m |
57//!
58//! Stealing bits from alignment is relatively innocuous, but we can only
59//! get the compiler to check it for you in dev mode as things stand in
60//! rust today.
61//!
62//! ## Sign bit (parameter S)
63//!
64//! The most commonly used operating systems arrange memory so that the
65//! high half of virtual memory space is reserved for the kernel and the
66//! low half is given to userspace.
67//!
68//! Looked at as a signed integer, this makes the kernel half of address
69//! space negative and the userspace half positive.
70//!
71//! Most programs do not deal with kernel addresses, thus giving us an
72//! extra bit to play with.
73//!
74//! We can also get this extra bit in kernel mode if we know we will not
75//! be dealing with userspace addresses. We do this by taking a pointer to
76//! a value on the stack and stealing its sign bit.
77//!
78//! If you know you will be dealing with userspace addresses in kernel
79//! space or kernel space addresses in userspace, or you are using or
80//! implementing a kernel which does not follow this convention, you must
81//! set `S` to `false`.
82//!
83//! The S bit is currently only tested with userspace pointers in
84//! userspace. While we think it should work more generally, we currently
85//! haven't got a test rig for other scenarios so we can't promise it does.
86//!
87//! ## Unused virtual address space (V)
88//!
89//! In 64-bit mode, address space is plentiful: nobody has 64 bits' worth
90//! of RAM and even if they did, their processor is unable to address it
91//! all. V is required to be 0 unless compiling for a 64bit target.
92//!
93//! The number of bits that may be safely stolen by this method depends
94//! upon the microarchitecture in question and the page table depth.
95//!
96//! For x86-64 and aarch64, the following sizes apply:
97//!
98//! | Bits | PT depth | Support                           |
99//! |------|----------|-----------------------------------|
100//! | 25   |        3 | aarch64 only, uncommon, opt-in    |
101//! | 16   |        4 | most common default               |
102//! | 7    |        5 | some intel only, uncommon, opt-in |
103//!
104//! If you are made of money and need more than 128TB virtual address
105//! space, you should limit yourself to 7 bits for V. Likewise if you know
106//! you'll be on 3-deep page tables, you can steal a whopping 25 bits. But
107//! you're probably limited to 16 bits in general.
108#![no_std]
109#![allow(unstable_name_collisions)]
110
111#[cfg(feature = "alloc")]
112extern crate alloc;
113#[cfg(feature = "alloc")]
114use alloc::boxed::Box;
115
116use core::hash::*;
117#[cfg(feature = "alloc")]
118use core::ops::{Deref, DerefMut};
119use core::ptr::NonNull;
120
121/// A pointer we stole the high bits off
122///
123/// T: type pointed to.
124/// A: number of bits to steal based on the alignment requirements of T.
125/// S: whether to steal the sign bit.
126/// V: number of bits to steal from unused virtual address space.
127
128#[derive(Debug)]
129#[repr(transparent)]
130pub struct Ointer<T: ?Sized, const A: u8, const S: bool, const V: u8> {
131  ptr: *mut T,
132}
133
134impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Hash for Ointer<T, A, S, V> {
135  fn hash<H: Hasher>(&self, state: &mut H) {
136    self.ptr.hash(state)
137  }
138}
139
140impl<T: ?Sized, const A: u8, const S: bool, const V: u8> PartialEq<Self> for Ointer<T, A, S, V> {
141  #![allow(
142    ambiguous_wide_pointer_comparisons,
143    reason = "mirroring the behaviour of rust ptrs"
144  )]
145  fn eq(&self, other: &Self) -> bool {
146    self.ptr == other.ptr
147  }
148}
149
150impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Eq for Ointer<T, A, S, V> {}
151
152impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Clone for Ointer<T, A, S, V> {
153  fn clone(&self) -> Self {
154    *self
155  }
156}
157
158impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Copy for Ointer<T, A, S, V> {}
159
160impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Ointer<T, A, S, V> {
161  /// Creates a new Ointer from a presumed legitimate pointer.
162  ///
163  /// # Safety
164  ///
165  /// * T's alignment must enable stealing A bits.
166  /// * The high bits (sign upwards) must match a stack pointer's high bits.
167  /// * If compiling for a 64bit arch, V must be at most 25.
168  /// * If compiling for a non-64bit arch, V must be 0.
169  ///
170  /// These invariants are checked with `debug_assert` only, hence
171  /// `unsafe`. The usual caveats of pointers apply.
172  pub unsafe fn new(ptr: *mut T) -> Self {
173    Ointer {
174      ptr: pack(ptr, A, S, V),
175    }
176  }
177
178  /// Constructor that enables stealing bits.
179  ///
180  /// # Safety
181  ///
182  /// Same as `new`
183  pub unsafe fn new_stealing(ptr: *mut T, bits: usize) -> Self {
184    let mask = asv_mask(A, S, V);
185    let ptr = ptr.with_addr((bits & mask) | (ptr.addr() & !mask));
186    Self { ptr }
187  }
188
189  /// Returns the stolen bits in the high pos.
190  pub fn stolen(self) -> usize {
191    self.ptr.addr() & asv_mask(A, S, V)
192  }
193
194  /// Takes a value from the high bits of the provided usize and
195  /// steals them from the ointer.
196  pub fn steal(self, bits: usize) -> Self {
197    let mask = asv_mask(A, S, V);
198    let ptr = self
199      .ptr
200      .with_addr((bits & mask) | (self.ptr.addr() & !mask));
201    Self { ptr }
202  }
203
204  /// Get the pointer without the stolen bits
205  pub fn as_ptr(self) -> *mut T {
206    unsafe { unpack(self.ptr, A, S, V) }
207  }
208
209  /// Direct access to the underlying data. The pointer it returns
210  /// may not be valid.
211  pub fn raw(self) -> usize {
212    self.ptr.expose_provenance()
213  }
214}
215
216/// A non-null pointer that we stole the high bits off.
217///
218/// T: type pointed to.
219/// V: number of bits to steal directly by masking them off.
220/// A: number of bits to steal based on the alignment requirements of T.
221#[derive(Debug)]
222#[repr(transparent)]
223pub struct NotNull<T: ?Sized, const A: u8, const S: bool, const V: u8>(NonNull<T>);
224
225impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Clone for NotNull<T, A, S, V> {
226  fn clone(&self) -> Self {
227    *self
228  }
229}
230
231impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Copy for NotNull<T, A, S, V> {}
232
233impl<T: ?Sized, const A: u8, const S: bool, const V: u8> PartialEq<Self> for NotNull<T, A, S, V> {
234  #![allow(
235    ambiguous_wide_pointer_comparisons,
236    reason = "mirroring the behaviour of NonNull<T>"
237  )]
238  fn eq(&self, other: &Self) -> bool {
239    self.0 == other.0
240  }
241}
242
243impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Eq for NotNull<T, A, S, V> {}
244
245impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Hash for NotNull<T, A, S, V> {
246  fn hash<H: Hasher>(&self, state: &mut H) {
247    self.0.hash(state)
248  }
249}
250
251impl<T: ?Sized, const A: u8, const S: bool, const V: u8> NotNull<T, A, S, V> {
252  /// Creates a new Ointer from a presumed legitimate pointer.
253  ///
254  /// # Safety
255  ///
256  /// * T's alignment must enable stealing A bits.
257  /// * The high bits (sign upwards) must match a stack pointer's high bits.
258  /// * If compiling for a 64bit arch, V must be at most 25.
259  /// * If compiling for a non-64bit arch, V must be 0.
260  ///
261  /// These invariants are checked with `debug_assert` only, hence
262  /// `unsafe`. The usual caveats of pointers apply.
263  pub unsafe fn new(ptr: NonNull<T>) -> Self {
264    let ptr = pack(ptr.as_ptr(), A, S, V);
265    NotNull(NonNull::new_unchecked(ptr))
266  }
267
268  /// Constructor that enables stealing bits.
269  ///
270  /// # Safety
271  ///
272  /// Same as `new`
273  pub unsafe fn new_stealing(ptr: NonNull<T>, bits: usize) -> Self {
274    let mask = asv_mask(A, S, V);
275    let ptr = ptr.as_ptr().map_addr(|addr| (bits & mask) | (addr & !mask));
276    NotNull(NonNull::new_unchecked(ptr))
277  }
278
279  /// Returns the stolen bits in the high pos.
280  pub fn stolen(self) -> usize {
281    self.0.as_ptr().addr() & asv_mask(A, S, V)
282  }
283
284  /// Takes a value from the high bits of the provided usize and
285  /// steals them from the ointer.
286  pub fn steal(self, bits: usize) -> Self {
287    let mask = asv_mask(A, S, V);
288    let bits = bits & mask;
289    let ptr = self.0.as_ptr();
290    let addr = ptr.addr() & !mask;
291    Self(unsafe { NonNull::new_unchecked(ptr.with_addr(addr | bits)) })
292  }
293
294  /// Get the pointer without the stolen bits
295  pub fn as_non_null(self) -> NonNull<T> {
296    unsafe { NonNull::new_unchecked(unpack(self.0.as_ptr(), A, S, V)) }
297  }
298
299  /// Direct access to the underlying data. The pointer it returns
300  /// may not be valid.
301  pub fn raw(self) -> usize {
302    self.0.as_ptr().expose_provenance()
303  }
304}
305
306/// A Box that we stole the high bits off.
307///
308/// T: type pointed to.
309/// V: number of bits to steal directly by masking them off.
310/// A: number of bits to steal based on the alignment requirements of T.
311#[derive(Debug)]
312#[repr(transparent)]
313#[cfg(feature = "alloc")]
314pub struct Ox<T: ?Sized, const A: u8, const S: bool, const V: u8>(NotNull<T, A, S, V>);
315
316#[cfg(feature = "alloc")]
317impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Clone for Ox<T, A, S, V>
318where
319  Box<T>: Clone,
320{
321  fn clone(&self) -> Self {
322    // Safety: even though we have mutable access to the ptr, we don't use it as it is ManuallyDrop
323    let old = core::mem::ManuallyDrop::new(unsafe { Box::from_raw(self.as_ptr()) });
324    let boxed = core::mem::ManuallyDrop::into_inner(old.clone());
325    // Safety: the stolen bits where taken from an existing Ox (thus safe)
326    unsafe { Ox::new_stealing(boxed, self.stolen()) }
327  }
328}
329
330#[cfg(feature = "alloc")]
331impl<T: ?Sized, const A: u8, const S: bool, const V: u8> PartialEq<Self> for Ox<T, A, S, V> {
332  fn eq(&self, other: &Self) -> bool {
333    self.0 == other.0
334  }
335}
336
337#[cfg(feature = "alloc")]
338impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Eq for Ox<T, A, S, V> {}
339
340#[cfg(feature = "alloc")]
341impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Hash for Ox<T, A, S, V> {
342  fn hash<H: Hasher>(&self, state: &mut H) {
343    self.0.hash(state)
344  }
345}
346
347#[cfg(feature = "alloc")]
348impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Ox<T, A, S, V> {
349  /// Creates a new Ox from a box.
350  ///
351  /// # Safety
352  ///
353  /// * T's alignment must enable stealing A bits.
354  /// * The high bits (sign upwards) must match a stack pointer's high bits.
355  /// * If compiling for a 64bit arch, V must be at most 25.
356  /// * If compiling for a non-64bit arch, V must be 0.
357  ///
358  /// These invariants are checked with `debug_assert` only, hence
359  /// `unsafe`. The usual caveats of pointers apply.
360  pub unsafe fn new(boxed: Box<T>) -> Self {
361    let ptr = Box::into_raw(boxed);
362    let ptr = pack(ptr, A, S, V);
363    Ox(NotNull::new(NonNull::new_unchecked(ptr)))
364  }
365
366  /// Constructor that enables stealing bits.
367  ///
368  /// # Safety
369  ///
370  /// Same as `new`
371  pub unsafe fn new_stealing(boxed: Box<T>, bits: usize) -> Self {
372    let mask = asv_mask(A, S, V);
373    let orig_ptr = Box::into_raw(boxed);
374    let ptr = orig_ptr.map_addr(|addr| (bits & mask) | (addr & !mask));
375    Self(NotNull::new(NonNull::new_unchecked(ptr)))
376  }
377
378  /// Returns the stolen bits in the high pos.
379  pub fn stolen(&self) -> usize {
380    self.0.stolen()
381  }
382
383  /// Takes a value from the high bits of the provided usize and
384  /// steals them from the ox.
385  pub fn steal(&mut self, bits: usize) {
386    let mask = asv_mask(A, S, V);
387    let bits = bits & mask;
388    let ptr = self.as_ptr().map_addr(|addr| (addr & !mask) | bits);
389    self.0 = unsafe { NotNull::new(NonNull::new_unchecked(ptr)) };
390  }
391
392  /// Get the box back without the stolen bits
393  pub fn into_box(self) -> Box<T> {
394    unsafe { Box::from_raw(unpack(self.as_ptr(), A, S, V)) }
395  }
396
397  /// Get the box back without the stolen bits
398  pub fn as_ptr(&self) -> *mut T {
399    self.0.as_non_null().as_ptr()
400  }
401
402  /// Direct access to the underlying data. The pointer it returns
403  /// may not be valid.
404  pub fn raw(&self) -> usize {
405    self.0.as_non_null().as_ptr().expose_provenance()
406  }
407}
408
409#[cfg(feature = "alloc")]
410impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Deref for Ox<T, A, S, V> {
411  type Target = T;
412  fn deref(&self) -> &T {
413    unsafe { self.0.as_non_null().as_ref() }
414  }
415}
416
417#[cfg(feature = "alloc")]
418impl<T: ?Sized, const A: u8, const S: bool, const V: u8> DerefMut for Ox<T, A, S, V> {
419  fn deref_mut(&mut self) -> &mut T {
420    unsafe { self.0.as_non_null().as_mut() }
421  }
422}
423
424#[cfg(feature = "alloc")]
425impl<T: ?Sized, const A: u8, const S: bool, const V: u8> Drop for Ox<T, A, S, V> {
426  fn drop(&mut self) {
427    drop(unsafe { Box::from_raw(self.0.as_non_null().as_ptr()) })
428  }
429}
430
431/// Packs a pointer into the bottom `sizeof(usize) - (a + s + v)` bits of a usize.
432///
433/// # Safety
434///
435/// * T's alignment must enable stealing A bits.
436/// * The high bits (sign upwards) must match a stack pointer's high bits.
437/// * If compiling for a 64bit arch, V must be at most 25.
438/// * If compiling for a non-64bit arch, V must be 0.
439///
440/// These invariants are checked with `debug_assert` only, hence
441/// `unsafe`. The usual caveats of pointers apply.
442pub unsafe fn pack<T: ?Sized>(ptr: *mut T, a: u8, s: bool, v: u8) -> *mut T {
443  let sv = asv_mask(0, s, v);
444  #[cfg(debug_assertions)]
445  {
446    if let Some(p) = ptr.as_ref() {
447      debug_assert!((1 << a) <= core::mem::align_of_val(p));
448    }
449    #[cfg(all(
450      not(target_pointer_width = "64"),
451      not(feature = "i_know_what_im_doing")
452    ))]
453    debug_assert!(v == 0);
454    debug_assert!(v <= 25);
455    // If S is set, the user has indicated they will never be
456    // dealing with foreign pointers, so we can check that
457    // too. We need only really check the sign bit because of
458    // canonicalisation, but it's actually cheaper to check
459    // all the high bits.
460    if s {
461      // We don't want to take account of A yet as the pointer
462      // is still in its undoctored state.
463      let ptr = ptr.addr();
464      let stack = (&ptr as *const usize).addr();
465      // the top bits should match
466      debug_assert!((sv & ptr) == (sv & stack));
467    }
468  }
469  ptr.with_addr((ptr.addr() & !sv) >> a as usize)
470}
471
472/// Turns the `sizeof(usize) - (a + s + v)` bits of a usize (as
473/// returned from `pack`) back into a pointer.
474///
475/// # Safety
476///
477/// The pointer must be of the correct type, otherwise you're
478/// basically unsafely casting the pointer.
479///
480/// You must use the same settings as you packed the pointer with. The
481/// pointer must be packed into the lower bits. Not strictly unsafe,
482/// but indicative of a bug in your program.
483pub unsafe fn unpack<T: ?Sized>(packed: *mut T, a: u8, s: bool, v: u8) -> *mut T {
484  // Mask off all the stolen bits to get the pointer data.
485  let asv = asv_mask(a, s, v);
486  let masked = packed.addr() & !asv;
487  // Restore the empty alignment bits
488  let base = masked << a;
489  if s {
490    // Copy the top bits of a stack pointer
491    let sv = asv_mask(0, s, v);
492    let base = base & !sv;
493    let stack = (&base as *const usize).addr() & sv;
494    packed.with_addr(stack | base)
495  } else {
496    // We need to extend the sign bit.
497    packed.with_addr((((base << v as usize) as isize) >> v as usize) as usize)
498  }
499}
500
501/// Produces a mask where the stolen bits (at the top) are set
502pub const fn asv_mask(a: u8, s: bool, v: u8) -> usize {
503  mask(a + s as u8 + v)
504}
505
506/// Produces a mask where the stolen bits (at the top) are set
507pub const fn mask(bits: u8) -> usize {
508  (isize::MIN >> (max(bits as usize, 1) - 1)) as usize
509}
510
511// core::cmp::max and usize::max aren't const fns
512const fn max(x: usize, y: usize) -> usize {
513  if x <= y {
514    y
515  } else {
516    x
517  }
518}