use sptr::Strict; /// A backend where the stuffed pointer is stored. Must be bigger or equal to the pointer size. /// /// The `Backend` is a trait to define types that store the stuffed pointer. It's supposed to /// be implemented on `Copy` types like `usize`, `u64`, or `u128`. Note that the `Self` type here /// serves as the main interchange format between the `Backend` and [`StuffedPtr`](`crate::StuffedPtr`) /// but *not* the actual underlying storage, which always contains a pointer to keep provenance /// (for example `(*mut (), u32)` on 32 bit for `u64`). This implies that `Self` *should* have the same /// size as `Backend::Stored`. /// /// This trait is just exposed for convenience and flexibility, you are usually not expected to implement /// it yourself, although such occasions could occur (for example to have a bigger storage than `u128` /// or smaller storage that only works on 32-bit or 16-bit platforms. /// /// # Safety /// Implementers of this trait *must* keep provenance of pointers, so if a valid pointer address+provenance /// combination is set in `set_ptr`, `get_ptr` *must* return the exact same values and provenance. pub unsafe trait Backend { /// The underlying type where the data is stored. Often a tuple of a pointer (for the provenance) /// and some integers to fill up the bytes. type Stored: Copy; /// Get the pointer from the backed. Since the [`StuffingStrategy`](`crate::StuffingStrategy`) /// is able to use the full bytes to pack in the pointer address, the full address is returned /// in the second tuple field, as the integer. The provenance of the pointer is returned as /// the first tuple field, but its address should be ignored and may be invalid. fn get_ptr(s: Self::Stored) -> (*mut (), Self); /// Set a new pointer address. The provenance of the new pointer is transferred in the first argument, /// and the address in the second. See [`Backend::get_ptr`] for more details on the separation. fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored; /// Get the integer value from the backend. Note that this *must not* be used to create a pointer, /// for that use [`Backend::get_ptr`] to keep the provenance. fn get_int(s: Self::Stored) -> Self; } #[cfg(test)] // todo: this mustn't affect the msrv, fix this later mod backend_size_asserts { use core::mem; use super::Backend; #[allow(dead_code)] // :/ const fn assert_same_size() { let has_equal_size = mem::size_of::() == mem::size_of::(); assert!(has_equal_size); } #[cfg(not(target_pointer_width = "16"))] const _: () = assert_same_size::::Stored>(); const _: () = assert_same_size::::Stored>(); const _: () = assert_same_size::::Stored>(); } // SAFETY: We are careful around provenance unsafe impl Backend for usize { type Stored = *mut (); fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s, Strict::addr(s)) } fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { Strict::with_addr(provenance, addr) } fn get_int(s: Self::Stored) -> Self { Strict::addr(s) } } #[cfg(target_pointer_width = "64")] /// on 64 bit, we can just treat u64/usize interchangeably, because uintptr_t == size_t in Rust // SAFETY: We are careful around provenance unsafe impl Backend for u64 { type Stored = *mut (); fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s, Strict::addr(s) as u64) } fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { Strict::with_addr(provenance, addr as usize) } fn get_int(s: Self::Stored) -> Self { Strict::addr(s) as u64 } } macro_rules! impl_backend_2_tuple { (impl for $ty:ty { (*mut (), $int:ident), $num:expr }) => { // SAFETY: We are careful around provenance unsafe impl Backend for $ty { // this one keeps the MSB in the pointer address, and the LSB in the integer type Stored = (*mut (), $int); fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s.0, Self::get_int(s)) } fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { let ptr_addr = (addr >> $num) as usize; let int_addr = addr as $int; // truncate it (Strict::with_addr(provenance, ptr_addr), int_addr) } fn get_int(s: Self::Stored) -> Self { let ptr_addr = Strict::addr(s.0) as $int; (<$ty>::from(ptr_addr) << $num) | <$ty>::from(s.1) } } }; } /// num1 is ptr-sized, num2 is 2*ptr sized #[cfg_attr(target_pointer_width = "64", allow(unused))] // not required on 64 bit macro_rules! impl_backend_3_tuple { (impl for $ty:ty { (*mut (), $int1:ident, $int2:ident), $num1:expr, $num2:expr }) => { // SAFETY: We are careful around provenance unsafe impl Backend for $ty { // this one keeps the MSB in the pointer address, ISB in int1 and the LSB in the int2 type Stored = (*mut (), $int1, $int2); fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s.0, Self::get_int(s)) } fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { let ptr_addr = (addr >> ($num1 + $num2)) as usize; let num1_addr = (addr >> $num2) as $int1; // truncate it let num2_addr = addr as $int2; // truncate it ( Strict::with_addr(provenance, ptr_addr), num1_addr, num2_addr, ) } fn get_int(s: Self::Stored) -> Self { let ptr_addr = Strict::addr(s.0) as $ty; let num1_addr = s.1 as $ty; let num2_addr = s.2 as $ty; (ptr_addr << ($num1 + $num2)) | (num1_addr << ($num2)) | num2_addr } } }; } #[cfg(target_pointer_width = "64")] impl_backend_2_tuple!(impl for u128 { (*mut (), u64), 64 }); #[cfg(target_pointer_width = "32")] impl_backend_2_tuple!(impl for u64 { (*mut (), u32), 32 }); #[cfg(target_pointer_width = "32")] impl_backend_3_tuple!(impl for u128 { (*mut (), u32, u64), 32, 64 }); #[cfg(target_pointer_width = "16")] impl_backend_3_tuple!(impl for u64 { (*mut (), u16, u32), 16, 32 }); // no 128 on 16 bit for now