make backend generic

This commit is contained in:
nora 2022-04-04 08:18:53 +02:00
parent 3e11ae0f17
commit d144819eb0
3 changed files with 68 additions and 45 deletions

3
rust-toolchain.toml Normal file
View file

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2022-04-03"
components = ["miri"]

View file

@ -4,6 +4,7 @@
//! A crate for stuffing things into a pointer. //! A crate for stuffing things into a pointer.
mod backend;
pub mod strategies; pub mod strategies;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
@ -11,20 +12,25 @@ use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::Not; use std::ops::Not;
use crate::backend::Backend;
use sptr::Strict; use sptr::Strict;
/// A union of a pointer and some extra data. /// A union of a pointer and some extra data.
pub struct StuffedPtr<T, S>(*mut T, PhantomData<S>) pub struct StuffedPtr<T, S, I = usize>(I::Stored, PhantomData<S>)
where where
S: StuffingStrategy; S: StuffingStrategy<I>,
I: Backend<T>;
impl<T, S> StuffedPtr<T, S> impl<T, S, I> StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
I: Backend<T>,
{ {
/// Create a new `StuffedPtr` from a pointer /// Create a new `StuffedPtr` from a pointer
pub fn new_ptr(ptr: *mut T) -> Self { pub fn new_ptr(ptr: *mut T) -> Self {
Self(map_ptr(ptr, S::stuff_ptr), PhantomData) let addr = Strict::addr(ptr);
let stuffed = S::stuff_ptr(addr);
Self(I::set_ptr(ptr, stuffed), PhantomData)
} }
/// Create a new `StuffPtr` from extra /// Create a new `StuffPtr` from extra
@ -32,8 +38,8 @@ where
// this doesn't have any provenance, which is ok, since it's never a pointer anyways. // this doesn't have any provenance, which is ok, since it's never a pointer anyways.
// if the user calls `set_ptr` it will use the new provenance from that ptr // if the user calls `set_ptr` it will use the new provenance from that ptr
let ptr = std::ptr::null_mut(); let ptr = std::ptr::null_mut();
let ptr = Strict::with_addr(ptr, S::stuff_extra(extra)); let extra = S::stuff_extra(extra);
Self(ptr, PhantomData) Self(I::set_ptr(ptr, extra), PhantomData)
} }
/// Get the pointer data, or `None` if it contains extra /// Get the pointer data, or `None` if it contains extra
@ -48,7 +54,9 @@ where
/// # Safety /// # Safety
/// Must contain pointer data and not extra /// Must contain pointer data and not extra
pub unsafe fn get_ptr_unchecked(&self) -> *mut T { pub unsafe fn get_ptr_unchecked(&self) -> *mut T {
map_ptr(self.0, S::extract_ptr) let (provenance, addr) = I::get_ptr(self.0);
let addr = S::extract_ptr(addr);
Strict::with_addr(provenance, addr)
} }
/// Get owned extra data from this, or `None` if it contains pointer data /// Get owned extra data from this, or `None` if it contains pointer data
@ -88,8 +96,8 @@ where
unsafe { S::extract_extra(data) } unsafe { S::extract_extra(data) }
} }
fn addr(&self) -> usize { fn addr(&self) -> I {
Strict::addr(self.0) I::get_int(self.0)
} }
fn is_extra(&self) -> bool { fn is_extra(&self) -> bool {
@ -97,10 +105,11 @@ where
} }
} }
impl<T, S> StuffedPtr<T, S> impl<T, S, I> StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
S::Extra: Copy, S::Extra: Copy,
I: Backend<T>,
{ {
/// Get extra data from this, or `None` if it's pointer data /// Get extra data from this, or `None` if it's pointer data
pub fn copy_extra(&self) -> Option<S::Extra> { pub fn copy_extra(&self) -> Option<S::Extra> {
@ -117,10 +126,11 @@ where
} }
} }
impl<T, S> Debug for StuffedPtr<T, S> impl<T, S, I> Debug for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
S::Extra: Debug, S::Extra: Debug,
I: Backend<T>,
{ {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
// SAFETY: // SAFETY:
@ -134,7 +144,8 @@ where
mem::forget(extra); mem::forget(extra);
Ok(()) Ok(())
} else { } else {
let ptr = map_ptr(self.0, S::extract_ptr); // SAFETY: Checked above
let ptr = unsafe { self.get_ptr_unchecked() };
f.debug_struct("StuffedPtr::Ptr") f.debug_struct("StuffedPtr::Ptr")
.field("ptr", &ptr) .field("ptr", &ptr)
.finish() .finish()
@ -142,9 +153,10 @@ where
} }
} }
impl<T, S> Drop for StuffedPtr<T, S> impl<T, S, I> Drop for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
I: Backend<T>,
{ {
fn drop(&mut self) { fn drop(&mut self) {
if self.is_extra() { if self.is_extra() {
@ -157,10 +169,11 @@ where
} }
} }
impl<T, S> Clone for StuffedPtr<T, S> impl<T, S, I> Clone for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
S::Extra: Clone, S::Extra: Clone,
I: Backend<T>,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
// SAFETY: We forget that `extra` ever existed after taking the reference and cloning it // SAFETY: We forget that `extra` ever existed after taking the reference and cloning it
@ -175,10 +188,11 @@ where
} }
} }
impl<T, S> PartialEq for StuffedPtr<T, S> impl<T, S, I> PartialEq for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
S::Extra: PartialEq, S::Extra: PartialEq,
I: Backend<T>,
{ {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
// SAFETY: We forget them after // SAFETY: We forget them after
@ -203,16 +217,18 @@ where
} }
} }
impl<T, S> Eq for StuffedPtr<T, S> impl<T, S, I> Eq for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
S::Extra: PartialEq + Eq, S::Extra: PartialEq + Eq,
I: Backend<T>,
{ {
} }
impl<T, S> From<Box<T>> for StuffedPtr<T, S> impl<T, S, I> From<Box<T>> for StuffedPtr<T, S, I>
where where
S: StuffingStrategy, S: StuffingStrategy<I>,
I: Backend<T>,
{ {
fn from(boxed: Box<T>) -> Self { fn from(boxed: Box<T>) -> Self {
Self::new_ptr(Box::into_raw(boxed)) Self::new_ptr(Box::into_raw(boxed))
@ -230,22 +246,22 @@ where
/// ///
/// For [`StuffingStrategy::stuff_ptr`] and [`StuffingStrategy::extract_ptr`], /// For [`StuffingStrategy::stuff_ptr`] and [`StuffingStrategy::extract_ptr`],
/// `ptr == extract_ptr(stuff_ptr(ptr))` *must* hold true. /// `ptr == extract_ptr(stuff_ptr(ptr))` *must* hold true.
pub unsafe trait StuffingStrategy { pub unsafe trait StuffingStrategy<I> {
/// The type of the extra. /// The type of the extra.
type Extra; type Extra;
/// Checks whether the `StufferPtr` data value contains an extra value. The result of this /// Checks whether the `StufferPtr` data value contains an extra value. The result of this
/// function can be trusted. /// function can be trusted.
fn is_extra(data: usize) -> bool; fn is_extra(data: I) -> bool;
/// Stuff extra data into a usize that is then put into the pointer. This operation /// Stuff extra data into a usize that is then put into the pointer. This operation
/// must be infallible. /// must be infallible.
fn stuff_extra(inner: Self::Extra) -> usize; fn stuff_extra(inner: Self::Extra) -> I;
/// Extract extra data from the data. /// Extract extra data from the data.
/// # Safety /// # Safety
/// `data` must contain data created by [`StuffingStrategy::stuff_extra`]. /// `data` must contain data created by [`StuffingStrategy::stuff_extra`].
unsafe fn extract_extra(data: usize) -> Self::Extra; unsafe fn extract_extra(data: I) -> Self::Extra;
/// Stuff a pointer address into the pointer sized integer. /// Stuff a pointer address into the pointer sized integer.
/// ///
@ -253,24 +269,12 @@ pub unsafe trait StuffingStrategy {
/// cursed things with it. /// cursed things with it.
/// ///
/// The default implementation just returns the address directly. /// The default implementation just returns the address directly.
fn stuff_ptr(inner: usize) -> usize { fn stuff_ptr(addr: usize) -> I;
inner
}
/// Extract the pointer address from the data. /// Extract the pointer address from the data.
/// ///
/// This function expects `inner` to come directly from [`StuffingStrategy::stuff_ptr`]. /// This function expects `inner` to come directly from [`StuffingStrategy::stuff_ptr`].
/// fn extract_ptr(inner: I) -> usize;
/// The default implementation just returns the value directly.
fn extract_ptr(inner: usize) -> usize {
inner
}
}
fn map_ptr<T>(ptr: *mut T, map: impl FnOnce(usize) -> usize) -> *mut T {
let int = Strict::addr(ptr);
let result = map(int);
Strict::with_addr(ptr, result)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -4,7 +4,7 @@
use crate::StuffingStrategy; use crate::StuffingStrategy;
unsafe impl StuffingStrategy for () { unsafe impl StuffingStrategy<usize> for () {
type Extra = (); type Extra = ();
fn is_extra(_data: usize) -> bool { fn is_extra(_data: usize) -> bool {
@ -16,6 +16,14 @@ unsafe impl StuffingStrategy for () {
} }
unsafe fn extract_extra(_data: usize) -> Self::Extra {} unsafe fn extract_extra(_data: usize) -> Self::Extra {}
fn stuff_ptr(addr: usize) -> usize {
addr
}
fn extract_ptr(inner: usize) -> usize {
inner
}
} }
#[cfg(test)] #[cfg(test)]
@ -26,7 +34,7 @@ pub(crate) mod test_strategies {
macro_rules! impl_usize_max_zst { macro_rules! impl_usize_max_zst {
($ty:ident) => { ($ty:ident) => {
// this one lives in usize::MAX // this one lives in usize::MAX
unsafe impl StuffingStrategy for $ty { unsafe impl StuffingStrategy<usize> for $ty {
type Extra = Self; type Extra = Self;
fn is_extra(data: usize) -> bool { fn is_extra(data: usize) -> bool {
@ -42,6 +50,14 @@ pub(crate) mod test_strategies {
unsafe fn extract_extra(_data: usize) -> Self::Extra { unsafe fn extract_extra(_data: usize) -> Self::Extra {
$ty $ty
} }
fn stuff_ptr(addr: usize) -> usize {
addr
}
fn extract_ptr(inner: usize) -> usize {
inner
}
} }
}; };
} }