diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..9b693d4 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,3 @@ +disallowed-methods = [ + { path = "core::mem::forget", reason = "mem::forget is sketchy around unsafe code, use ManuallyDrop instead" } +] \ No newline at end of file diff --git a/src/backend.rs b/src/backend.rs index ac2e3bc..6715340 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,7 +6,7 @@ use sptr::Strict; /// 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 T, u32)` on 32 bit for `u64`). This implies that `Self` *should* have the same +/// (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 @@ -16,7 +16,7 @@ use sptr::Strict; /// # 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 { +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; @@ -25,11 +25,11 @@ pub unsafe trait Backend { /// 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 T, Self); + 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 T, addr: Self) -> Self::Stored; + 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. @@ -49,19 +49,19 @@ mod backend_size_asserts { } #[cfg(not(target_pointer_width = "16"))] - const _: () = assert_same_size::>::Stored>(); - const _: () = assert_same_size::>::Stored>(); - const _: () = assert_same_size::>::Stored>(); + const _: () = assert_same_size::::Stored>(); + const _: () = assert_same_size::::Stored>(); + const _: () = assert_same_size::::Stored>(); } -unsafe impl Backend for usize { - type Stored = *mut T; +unsafe impl Backend for usize { + type Stored = *mut (); - fn get_ptr(s: Self::Stored) -> (*mut T, Self) { + fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s, Strict::addr(s)) } - fn set_ptr(provenance: *mut T, addr: Self) -> Self::Stored { + fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { Strict::with_addr(provenance, addr) } @@ -72,14 +72,14 @@ unsafe impl Backend for usize { #[cfg(target_pointer_width = "64")] /// on 64 bit, we can just treat u64/usize interchangeably, because uintptr_t == size_t in Rust -unsafe impl Backend for u64 { - type Stored = *mut T; +unsafe impl Backend for u64 { + type Stored = *mut (); - fn get_ptr(s: Self::Stored) -> (*mut T, Self) { + fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s, Strict::addr(s) as u64) } - fn set_ptr(provenance: *mut T, addr: Self) -> Self::Stored { + fn set_ptr(provenance: *mut (), addr: Self) -> Self::Stored { Strict::with_addr(provenance, addr as usize) } @@ -89,17 +89,17 @@ unsafe impl Backend for u64 { } macro_rules! impl_backend_2_tuple { - (impl for $ty:ty { (*mut T, $int:ident), $num:expr }) => { - unsafe impl Backend for $ty { + (impl for $ty:ty { (*mut (), $int:ident), $num:expr }) => { + unsafe impl Backend for $ty { // this one keeps the MSB in the pointer address, and the LSB in the integer - type Stored = (*mut T, $int); + type Stored = (*mut (), $int); - fn get_ptr(s: Self::Stored) -> (*mut T, Self) { + fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s.0, Self::get_int(s)) } - fn set_ptr(provenance: *mut T, addr: Self) -> Self::Stored { + 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) @@ -116,17 +116,17 @@ macro_rules! impl_backend_2_tuple { /// 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 T, $int1:ident, $int2:ident), $num1:expr, $num2:expr }) => { - unsafe impl Backend for $ty { + (impl for $ty:ty { (*mut (), $int1:ident, $int2:ident), $num1:expr, $num2:expr }) => { + 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 T, $int1, $int2); + type Stored = (*mut (), $int1, $int2); - fn get_ptr(s: Self::Stored) -> (*mut T, Self) { + fn get_ptr(s: Self::Stored) -> (*mut (), Self) { (s.0, Self::get_int(s)) } - fn set_ptr(provenance: *mut T, addr: Self) -> Self::Stored { + 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 @@ -148,15 +148,15 @@ macro_rules! impl_backend_3_tuple { } #[cfg(target_pointer_width = "64")] -impl_backend_2_tuple!(impl for u128 { (*mut T, u64), 64 }); +impl_backend_2_tuple!(impl for u128 { (*mut (), u64), 64 }); #[cfg(target_pointer_width = "32")] -impl_backend_2_tuple!(impl for u64 { (*mut T, u32), 32 }); +impl_backend_2_tuple!(impl for u64 { (*mut (), u32), 32 }); #[cfg(target_pointer_width = "32")] -impl_backend_3_tuple!(impl for u128 { (*mut T, u32, u64), 32, 64 }); +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 T, u16, u32), 16, 32 }); +impl_backend_3_tuple!(impl for u64 { (*mut (), u16, u32), 16, 32 }); // no 128 on 16 bit for now diff --git a/src/lib.rs b/src/lib.rs index c374373..14ea2b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![warn(rust_2018_idioms)] #![warn(missing_docs)] +#![deny(clippy::disallowed_methods, clippy::undocumented_unsafe_blocks)] //! A crate for stuffing things into a pointer. //! @@ -29,8 +30,9 @@ //! ``` //! use std::collections::HashMap; //! # use std::convert::{TryFrom, TryInto}; +//! use std::mem::ManuallyDrop; //! -//! use stuff::{StuffedPtr, StuffingStrategy}; +//! use stuff::{StuffedPtr, StuffingStrategy, Either}; //! //! // Create a unit struct for our strategy //! struct NanBoxStrategy; @@ -43,27 +45,22 @@ //! unsafe impl StuffingStrategy for NanBoxStrategy { //! type Other = f64; //! -//! fn is_other(data: u64) -> bool { -//! (data & QNAN) != QNAN -//! } -//! //! fn stuff_other(inner: Self::Other) -> u64 { //! unsafe { std::mem::transmute(inner) } // both are 64 bit POD's //! } //! -//! unsafe fn extract_other(data: u64) -> Self::Other { -//! std::mem::transmute(data) // both are 64 bit POD's +//! unsafe fn extract(data: u64) -> Either> { +//! if (data & QNAN) != QNAN { +//! Either::Other(ManuallyDrop::new(f64::from_bits(data))) +//! } else { +//! Either::Ptr((data & !(SIGN_BIT | QNAN)).try_into().unwrap()) +//! } //! } //! //! fn stuff_ptr(addr: usize) -> u64 { //! // add the QNAN and SIGN_BIT //! SIGN_BIT | QNAN | u64::try_from(addr).unwrap() //! } -//! -//! fn extract_ptr(inner: u64) -> usize { -//! // keep everything except for QNAN and SIGN_BIT -//! (inner & !(SIGN_BIT | QNAN)).try_into().unwrap() -//! } //! } //! //! // a very, very crude representation of an object @@ -88,23 +85,24 @@ //! ``` #[cfg(test)] -extern crate alloc; // we want that for tests so we can use `Box` +extern crate std; mod backend; mod strategy; + +#[cfg(any())] mod tag; use core::{ fmt::{Debug, Formatter}, hash::{Hash, Hasher}, marker::PhantomData, - mem, - ops::Not, + mem::ManuallyDrop, }; use sptr::Strict; -pub use crate::{backend::Backend, strategy::StuffingStrategy}; +pub use crate::{backend::Backend, either::Either, guard::Guard, strategy::StuffingStrategy}; /// A union of a pointer or some `other` data, bitpacked into a value with the size depending on /// `B`. It defaults to `usize`, meaning pointer sized, but `u64` and `u128` are also provided @@ -122,20 +120,20 @@ pub use crate::{backend::Backend, strategy::StuffingStrategy}; /// /// This type is guaranteed to be `#[repr(transparent)]` to a `B::Stored`. #[repr(transparent)] -pub struct StuffedPtr(B::Stored, PhantomData) +pub struct StuffedPtr(B::Stored, PhantomData>) where - B: Backend; + B: Backend; impl StuffedPtr where S: StuffingStrategy, - B: Backend, + B: Backend, { /// Create a new `StuffedPtr` from a pointer pub fn new_ptr(ptr: *mut T) -> Self { let addr = Strict::addr(ptr); let stuffed = S::stuff_ptr(addr); - StuffedPtr(B::set_ptr(ptr, stuffed), PhantomData) + StuffedPtr(B::set_ptr(ptr.cast::<()>(), stuffed), PhantomData) } /// Create a new `StuffPtr` from `other` data @@ -149,76 +147,44 @@ where /// Get the pointer data, or `None` if it contains `other` data pub fn get_ptr(&self) -> Option<*mut T> { - match self.is_other().not() { - true => { - // SAFETY: We have done a check that it's not other - unsafe { Some(self.get_ptr_unchecked()) } - } - false => None, - } - } - - /// Get the unstuffed pointer data from the stuffed pointer, assuming that the `StuffedPtr` - /// contains pointer data. - /// - /// # Safety - /// `StuffedPtr` must contain pointer data and not `other` data - pub unsafe fn get_ptr_unchecked(&self) -> *mut T { let (provenance, stored) = B::get_ptr(self.0); - let addr = S::extract_ptr(stored); - Strict::with_addr(provenance, addr) + let addr = unsafe { S::extract(stored).ptr()? }; + Some(Strict::with_addr(provenance.cast::(), addr)) } /// Get owned `other` data from this, or `None` if it contains pointer data - pub fn into_other(self) -> Option { - match self.is_other() { - true => { - // SAFETY: We checked that it contains an other above - unsafe { Some(self.into_other_unchecked()) } - } - false => None, - } - } - - /// Turn this pointer into `other` data. - /// # Safety - /// `StuffedPtr` must contain `other` data and not pointer - pub unsafe fn into_other_unchecked(self) -> S::Other { + pub unsafe fn into_other(self) -> Option { + let this = ManuallyDrop::new(self); // SAFETY: `self` is consumed and forgotten after this call - let other = self.get_other_unchecked(); - mem::forget(self); - other + let other = this.get_other(); + other.map(|md| ManuallyDrop::into_inner(md)) } /// Get `other` data from this, or `None` if it contains pointer data - /// # Safety - /// The caller must guarantee that only ever on `Other` exists if `Other: !Copy` - pub unsafe fn get_other(&self) -> Option { - match self.is_other() { - true => { - // SAFETY: We checked that it contains other above, the caller guarantees the rest - Some(self.get_other_unchecked()) - } - false => None, - } + pub unsafe fn get_other(&self) -> Option> { + let data = self.addr(); + unsafe { S::extract(data).other() } } - /// Get `other` data from this - /// # Safety - /// Must contain `other` data and not pointer data, - /// and the caller must guarantee that only ever on `Other` exists if `Other: !Copy` - pub unsafe fn get_other_unchecked(&self) -> S::Other { - let data = self.addr(); - S::extract_other(data) + pub fn into_inner(self) -> Either<*mut T, S::Other> { + let (provenance, stored) = B::get_ptr(self.0); + let either = unsafe { S::extract(stored) }; + either + .map_ptr(|addr| Strict::with_addr(provenance.cast::(), addr)) + .map_other(|other| ManuallyDrop::into_inner(other)) + } + + pub fn get(&self) -> Either<*mut T, Guard<'_, S::Other>> { + let (provenance, stored) = B::get_ptr(self.0); + let either = unsafe { S::extract(stored) }; + either + .map_ptr(|addr| Strict::with_addr(provenance.cast::(), addr)) + .map_other(|other| Guard::new(other)) } fn addr(&self) -> B { B::get_int(self.0) } - - fn is_other(&self) -> bool { - S::is_other(self.addr()) - } } /// Extra implementations if the `other` type is `Copy` @@ -226,20 +192,12 @@ impl StuffedPtr where S: StuffingStrategy, S::Other: Copy, - B: Backend, + B: Backend, { /// Get `other` data from this, or `None` if it's pointer data pub fn copy_other(&self) -> Option { // SAFETY: `S::Other: Copy` - unsafe { self.get_other() } - } - - /// Get `other` data from this - /// # Safety - /// Must contain `other` data and not pointer data, - pub unsafe fn copy_other_unchecked(&self) -> S::Other { - // SAFETY: `S::Other: Copy`, and the caller guarantees that it's other - self.get_other_unchecked() + unsafe { self.get_other().map(|other| *other) } } } @@ -247,25 +205,12 @@ impl Debug for StuffedPtr where S: StuffingStrategy, S::Other: Debug, - B: Backend, + B: Backend, { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - // SAFETY: - // If S::Other: !Copy, we can't just copy it out and call it a day - // For example, if it's a Box, not forgetting it here would lead to a double free - // So we just format it and forget it afterwards - if let Some(other) = unsafe { self.get_other() } { - f.debug_struct("StuffedPtr::Other") - .field("other", &other) - .finish()?; - mem::forget(other); - Ok(()) - } else { - // SAFETY: Checked above - let ptr = unsafe { self.get_ptr_unchecked() }; - f.debug_struct("StuffedPtr::Ptr") - .field("ptr", &ptr) - .finish() + match self.get() { + Either::Ptr(ptr) => f.debug_tuple("Ptr").field(&ptr).finish(), + Either::Other(other) => f.debug_tuple("Other").field(other.inner()).finish(), } } } @@ -274,17 +219,15 @@ impl Clone for StuffedPtr where S: StuffingStrategy, S::Other: Clone, - B: Backend, + B: Backend, { fn clone(&self) -> Self { - // SAFETY: We forget that `other` ever existed after taking the reference and cloning it - if let Some(other) = unsafe { self.get_other() } { - let cloned_other = other.clone(); - mem::forget(other); - Self::new_other(cloned_other) - } else { - // just copy the pointer - StuffedPtr(self.0, PhantomData) + match self.get() { + Either::Ptr(ptr) => StuffedPtr::new_ptr(ptr), + Either::Other(other) => { + let cloned_other = other.inner().clone(); + Self::new_other(cloned_other) + } } } } @@ -293,7 +236,7 @@ impl Copy for StuffedPtr where S: StuffingStrategy, S::Other: Copy, - B: Backend, + B: Backend, { } @@ -301,28 +244,14 @@ impl PartialEq for StuffedPtr where S: StuffingStrategy, S::Other: PartialEq, - B: Backend, + B: Backend, { fn eq(&self, other: &Self) -> bool { - // SAFETY: We forget them after - let others = unsafe { (self.get_other(), other.get_other()) }; - - let eq = match &others { - (Some(other1), Some(other2)) => other1.eq(other2), - (None, None) => { - // SAFETY: `get_other` returned `None`, so it must be a ptr - unsafe { - let ptr1 = self.get_ptr_unchecked(); - let ptr2 = self.get_ptr_unchecked(); - core::ptr::eq(ptr1, ptr2) - } - } + match (self.get(), other.get()) { + (Either::Ptr(a), Either::Ptr(b)) => core::ptr::eq(a, b), + (Either::Other(a), Either::Other(b)) => a.inner() == b.inner(), _ => false, - }; - - mem::forget(others); - - eq + } } } @@ -330,7 +259,7 @@ impl Eq for StuffedPtr where S: StuffingStrategy, S::Other: PartialEq + Eq, - B: Backend, + B: Backend, { } @@ -338,17 +267,161 @@ impl Hash for StuffedPtr where S: StuffingStrategy, S::Other: Hash, - B: Backend, + B: Backend, { fn hash(&self, state: &mut H) { - // SAFETY: We forget that `other` ever existed after taking the reference and cloning it - if let Some(other) = unsafe { self.get_other() } { - other.hash(state); - mem::forget(other); - } else { - // SAFETY: Checked above - let ptr = unsafe { self.get_ptr_unchecked() }; - ptr.hash(state); + match self.get() { + Either::Ptr(ptr) => { + ptr.hash(state); + } + Either::Other(other) => { + other.inner().hash(state); + } + } + } +} + +mod either { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum Either { + Ptr(P), + Other(O), + } + + impl Either { + pub fn ptr(&self) -> Option

{ + match *self { + Self::Ptr(ptr) => Some(ptr), + Self::Other(_) => None, + } + } + + pub fn other(self) -> Option { + match self { + Self::Ptr(_) => None, + Self::Other(other) => Some(other), + } + } + + pub fn map_ptr(self, f: impl FnOnce(P) -> U) -> Either { + match self { + Self::Ptr(ptr) => Either::Ptr(f(ptr)), + Self::Other(other) => Either::Other(other), + } + } + + pub fn map_other(self, f: impl FnOnce(O) -> U) -> Either { + match self { + Self::Ptr(ptr) => Either::Ptr(ptr), + Self::Other(other) => Either::Other(f(other)), + } + } + } +} + +mod guard { + use core::{fmt::Debug, marker::PhantomData, mem::ManuallyDrop, ops::Deref}; + + use self::no_alias::AllowAlias; + + mod no_alias { + use core::{fmt::Debug, hash::Hash, mem::MaybeUninit, ops::Deref}; + + /// A `T` except with aliasing problems removed + /// + /// this is very sketchy and actually kinda equivalent to whatever ralf wants to add to ManuallyDrop + /// so idk but whatever this is a great type + pub struct AllowAlias(MaybeUninit); + + impl AllowAlias { + pub fn new(value: T) -> Self { + Self(MaybeUninit::new(value)) + } + + pub fn into_inner(self) -> T { + unsafe { self.0.assume_init() } + } + } + + impl Debug for AllowAlias { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("NoAlias").field(&self.0).finish() + } + } + + impl PartialEq for AllowAlias { + fn eq(&self, other: &Self) -> bool { + &*self == &*other + } + } + + impl Eq for AllowAlias {} + + impl PartialOrd for AllowAlias { + fn partial_cmp(&self, other: &Self) -> Option { + self.deref().partial_cmp(&*other) + } + } + + impl Ord for AllowAlias { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.deref().cmp(&*other) + } + } + + impl Hash for AllowAlias { + fn hash(&self, state: &mut H) { + self.deref().hash(state); + } + } + + impl Deref for AllowAlias { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { self.0.assume_init_ref() } + } + } + } + + /// Acts like a `&T` but has to carry around the value. + pub struct Guard<'a, T> { + inner: AllowAlias, + _boo: PhantomData<&'a ()>, + } + + impl<'a, T> Guard<'a, T> { + pub fn new(value: ManuallyDrop) -> Self { + Self { + inner: AllowAlias::new(ManuallyDrop::into_inner(value)), + _boo: PhantomData, + } + } + + /// # Safety + /// Make sure to not violate aliasing with this + pub unsafe fn into_inner(self) -> ManuallyDrop { + ManuallyDrop::new(self.inner.into_inner()) + } + + pub fn inner(&self) -> &T { + &*self + } + } + + impl Deref for Guard<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &*self.inner + } + } + + impl Debug for Guard<'_, T> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("NeverDrop") + .field("inner", &*self.inner) + .finish() } } } @@ -357,8 +430,8 @@ where mod tests { #![allow(non_snake_case)] - use alloc::{boxed::Box, format}; use core::mem; + use std::{boxed::Box, format, println}; use paste::paste; @@ -373,7 +446,7 @@ mod tests { fn from_box(boxed: Box) -> StuffedPtr where S: StuffingStrategy, - B: Backend, + B: Backend, { StuffedPtr::new_ptr(Box::into_raw(boxed)) } @@ -386,7 +459,7 @@ mod tests { unsafe { let boxed = Box::new(1); let stuffed_ptr: StuffedPtr = from_box(boxed); - let ptr = stuffed_ptr.get_ptr_unchecked(); + let ptr = stuffed_ptr.get_ptr().unwrap(); let boxed = Box::from_raw(ptr); assert_eq!(*boxed, 1); } @@ -396,7 +469,7 @@ mod tests { #[test] fn []() { let stuffed_ptr: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_other(EmptyInMax); - assert!(stuffed_ptr.is_other()); + assert!(unsafe { stuffed_ptr.get_other() }.is_some()); assert!(matches!(stuffed_ptr.copy_other(), Some(EmptyInMax))); } @@ -404,7 +477,8 @@ mod tests { fn []() { let boxed = Box::new(1); let stuffed_ptr: StuffedPtr = from_box(boxed); - assert!(format!("{stuffed_ptr:?}").starts_with("StuffedPtr::Ptr {")); + println!("{stuffed_ptr:?}"); + assert!(format!("{stuffed_ptr:?}").starts_with("Ptr(")); drop(unsafe { Box::from_raw(stuffed_ptr.get_ptr().unwrap()) }); @@ -412,7 +486,7 @@ mod tests { let stuffed_ptr: StuffedPtr = StuffedPtr::new_other(other); assert_eq!( format!("{stuffed_ptr:?}"), - "StuffedPtr::Other { other: hello! }" + "Other(hello!)" ); } diff --git a/src/strategy.rs b/src/strategy.rs index 7efa0f4..794e881 100644 --- a/src/strategy.rs +++ b/src/strategy.rs @@ -1,6 +1,6 @@ -use core::convert::TryInto; +use core::{convert::TryInto, mem::ManuallyDrop}; -use crate::Backend; +use crate::{Backend, Either}; /// A trait that describes how to stuff others and pointers into the pointer sized object. /// @@ -25,18 +25,14 @@ pub unsafe trait StuffingStrategy { /// The type of the other. type Other; - /// Checks whether the `StufferPtr` data value contains an other value. The result of this - /// function can be trusted. - fn is_other(data: B) -> bool; - /// Stuff other data into a usize that is then put into the pointer. This operation /// must be infallible. fn stuff_other(inner: Self::Other) -> B; - /// Extract other data from the data. + /// Extract the pointer data or other data /// # Safety /// `data` must contain data created by [`StuffingStrategy::stuff_other`]. - unsafe fn extract_other(data: B) -> Self::Other; + unsafe fn extract(data: B) -> Either>; /// Stuff a pointer address into the pointer sized integer. /// @@ -45,48 +41,42 @@ pub unsafe trait StuffingStrategy { /// /// The default implementation just returns the address directly. fn stuff_ptr(addr: usize) -> B; - - /// Extract the pointer address from the data. - /// - /// This function expects `inner` to come directly from [`StuffingStrategy::stuff_ptr`]. - fn extract_ptr(inner: B) -> usize; } unsafe impl StuffingStrategy for () where - B: Backend<()> + Default + TryInto, + B: Backend + Default + TryInto, usize: TryInto, { type Other = (); - fn is_other(_data: B) -> bool { - false - } - fn stuff_other(_inner: Self::Other) -> B { B::default() } - unsafe fn extract_other(_data: B) -> Self::Other {} + unsafe fn extract(data: B) -> Either> { + Either::Ptr( + data.try_into() + // note: this can't happen 🤔 + .unwrap_or_else(|_| panic!("Pointer value too big for usize")), + ) + } fn stuff_ptr(addr: usize) -> B { addr.try_into() .unwrap_or_else(|_| panic!("Address in `stuff_ptr` too big")) } - - fn extract_ptr(inner: B) -> usize { - inner - .try_into() - // note: this can't happen 🤔 - .unwrap_or_else(|_| panic!("Pointer value too big for usize")) - } } #[cfg(test)] pub(crate) mod test_strategies { - use core::fmt::{Debug, Formatter}; + use core::{ + fmt::{Debug, Formatter}, + mem::ManuallyDrop, + }; use super::StuffingStrategy; + use crate::Either; macro_rules! impl_usize_max_zst { ($ty:ident) => { @@ -94,78 +84,64 @@ pub(crate) mod test_strategies { unsafe impl StuffingStrategy for $ty { type Other = Self; - fn is_other(data: usize) -> bool { - data == usize::MAX - } - #[allow(clippy::forget_copy)] fn stuff_other(inner: Self::Other) -> usize { core::mem::forget(inner); usize::MAX } - unsafe fn extract_other(_data: usize) -> Self::Other { - $ty + unsafe fn extract(data: usize) -> Either> { + match data == usize::MAX { + true => Either::Other(ManuallyDrop::new($ty)), + false => Either::Ptr(data), + } } fn stuff_ptr(addr: usize) -> usize { addr } - - fn extract_ptr(inner: usize) -> usize { - inner - } } + unsafe impl StuffingStrategy for $ty { type Other = Self; - fn is_other(data: u64) -> bool { - data == u64::MAX - } - #[allow(clippy::forget_copy)] fn stuff_other(inner: Self::Other) -> u64 { core::mem::forget(inner); u64::MAX } - unsafe fn extract_other(_data: u64) -> Self::Other { - $ty + unsafe fn extract(data: u64) -> Either> { + match data == u64::MAX { + true => Either::Other(ManuallyDrop::new($ty)), + false => Either::Ptr(data as usize), + } } fn stuff_ptr(addr: usize) -> u64 { addr as u64 } - - fn extract_ptr(inner: u64) -> usize { - inner as usize - } } unsafe impl StuffingStrategy for $ty { type Other = Self; - fn is_other(data: u128) -> bool { - data == u128::MAX - } - #[allow(clippy::forget_copy)] fn stuff_other(inner: Self::Other) -> u128 { core::mem::forget(inner); u128::MAX } - unsafe fn extract_other(_data: u128) -> Self::Other { - $ty + unsafe fn extract(data: u128) -> Either> { + match data == u128::MAX { + true => Either::Other(ManuallyDrop::new($ty)), + false => Either::Ptr(data as usize), + } } fn stuff_ptr(addr: usize) -> u128 { addr as u128 } - - fn extract_ptr(inner: u128) -> usize { - inner as usize - } } }; } diff --git a/src/tag.rs b/src/tag.rs index d1ac0cd..0be118e 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -6,14 +6,14 @@ use sptr::Strict; use crate::Backend; -pub struct TaggedPtr(B::Stored, PhantomData) +pub struct TaggedPtr(B::Stored, PhantomData<(S, *mut T)>) where - B: Backend; + B: Backend; impl TaggedPtr where S: TaggingStrategy, - B: Backend, + B: Backend, { pub fn new(ptr: *mut T, tag: S::Tag) -> Self { let addr = Strict::addr(ptr); @@ -44,14 +44,14 @@ where impl Clone for TaggedPtr where - B: Backend, + B: Backend, { fn clone(&self) -> Self { TaggedPtr(self.0, self.1) } } -impl Copy for TaggedPtr where B: Backend {} +impl Copy for TaggedPtr where B: Backend {} pub trait TaggingStrategy { type Tag: Copy;