tidy up docs

This commit is contained in:
nora 2022-04-05 17:12:37 +02:00
parent 82cc768531
commit fe9196eae0
4 changed files with 85 additions and 64 deletions

View file

@ -4,8 +4,8 @@ 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`, `u128`. Note that these integers are basically
/// 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 these integers are basically
/// just the strategy and exchange types for addresses, but *not* the actual underlying storage, which
/// always contains a pointer to keep provenance (for example `(*mut T, u32)` on 32 bit for `u64`).
///
@ -21,10 +21,10 @@ pub unsafe trait Backend<T> {
/// and some integers to fill up the bytes.
type Stored: Copy;
/// Get the pointer from the backed. Since the [`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.
/// 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 T, Self);
/// Set a new pointer address. The provenance of the new pointer is transferred in the first argument,

View file

@ -5,18 +5,20 @@
//! A crate for stuffing things into a pointer.
//!
//! This crate consists of three parts:
//! * The type [`StuffedPtr`]
//! * The trait [`StuffingStrategy`]
//! * The trait [`Backend`]
//! `stuff` helps you to
//!
//! - Stuff arbitrary data into pointers
//! - Stuff pointers or arbitrary data into fixed size storage (u64, u128)
//!
//! in a **portable and provenance friendly** way.
//!
//! [`StuffedPtr`] is the main type of this crate. You it's a type whose size depends on the
//! choice of [`Backend`] (defaults to `usize`, `u64` and `u128` are also possible). It can store a
//! pointer or some extra data.
//!
//! except that the extra data is bitstuffed into the pointer. You can chose any arbitrary bitstuffing
//! depending on the [`StuffingStrategy`], an unsafe trait that governs how the extra data
//! (or the pointer itself) will be packed into the backend.
//! You can choose any arbitrary bitstuffing depending on the [`StuffingStrategy`], an unsafe trait that governs
//! how the extra data (or the pointer itself) will be packed into the backend. While this trait is still unsafe,
//! it's a lot safer than doing everything by hand.
//!
//! # Example: NaN-Boxing
//! Pointers are hidden in the NaN values of floats. NaN boxing often involves also hiding booleans
@ -99,29 +101,37 @@ use sptr::Strict;
pub use crate::{backend::Backend, strategy::StuffingStrategy};
/// A union of a pointer and some extra data, bitpacked into a pointer (or custom, using the third
/// generic param `I`) size.
/// A union of a pointer or some extra 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
/// by this crate. You can also provide your own [`Backend`] implementation
///
/// The stuffing strategy is supplied as the second generic parameter `S`.
///
/// The first generic parameter `T` is the type that the pointer is pointing to.
///
/// For a usage example, view the crate level documentation.
///
/// This pointer does *not* drop extra data, [`StuffedPtr::into_extra`] can be used if that is required.
///
/// `StuffedPtr` implements most traits like `Clone`, `PartialEq` or `Copy` if the extra type does.
pub struct StuffedPtr<T, S, I = usize>(I::Stored, PhantomData<S>)
///
/// This type is guaranteed to be `#[repr(transparent)]` to a `B::Stored`.
#[repr(transparent)]
pub struct StuffedPtr<T, S, B = usize>(B::Stored, PhantomData<S>)
where
S: StuffingStrategy<I>,
I: Backend<T>;
S: StuffingStrategy<B>,
B: Backend<T>;
impl<T, S, I> StuffedPtr<T, S, I>
impl<T, S, B> StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
I: Backend<T>,
S: StuffingStrategy<B>,
B: Backend<T>,
{
/// 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);
Self(I::set_ptr(ptr, stuffed), PhantomData)
Self(B::set_ptr(ptr, stuffed), PhantomData)
}
/// Create a new `StuffPtr` from extra data
@ -130,7 +140,7 @@ where
// if the user calls `set_ptr` it will use the new provenance from that ptr
let ptr = core::ptr::null_mut();
let extra = S::stuff_extra(extra);
Self(I::set_ptr(ptr, extra), PhantomData)
Self(B::set_ptr(ptr, extra), PhantomData)
}
/// Get the pointer data, or `None` if it contains extra data
@ -147,7 +157,7 @@ where
/// # Safety
/// `StuffedPtr` must contain pointer data and not extra data
pub unsafe fn get_ptr_unchecked(&self) -> *mut T {
let (provenance, addr) = I::get_ptr(self.0);
let (provenance, addr) = B::get_ptr(self.0);
let addr = S::extract_ptr(addr);
Strict::with_addr(provenance, addr)
}
@ -189,8 +199,8 @@ where
unsafe { S::extract_extra(data) }
}
fn addr(&self) -> I {
I::get_int(self.0)
fn addr(&self) -> B {
B::get_int(self.0)
}
fn is_extra(&self) -> bool {
@ -198,11 +208,12 @@ where
}
}
impl<T, S, I> StuffedPtr<T, S, I>
/// Extra implementations if the extra type is `Copy`
impl<T, S, B> StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: Copy,
I: Backend<T>,
B: Backend<T>,
{
/// Get extra data from this, or `None` if it's pointer data
pub fn copy_extra(&self) -> Option<S::Extra> {
@ -219,11 +230,11 @@ where
}
}
impl<T, S, I> Debug for StuffedPtr<T, S, I>
impl<T, S, B> Debug for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: Debug,
I: Backend<T>,
B: Backend<T>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
// SAFETY:
@ -246,11 +257,11 @@ where
}
}
impl<T, S, I> Clone for StuffedPtr<T, S, I>
impl<T, S, B> Clone for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: Clone,
I: Backend<T>,
B: Backend<T>,
{
fn clone(&self) -> Self {
// SAFETY: We forget that `extra` ever existed after taking the reference and cloning it
@ -265,19 +276,19 @@ where
}
}
impl<T, S, I> Copy for StuffedPtr<T, S, I>
impl<T, S, B> Copy for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: Copy,
I: Backend<T>,
B: Backend<T>,
{
}
impl<T, S, I> PartialEq for StuffedPtr<T, S, I>
impl<T, S, B> PartialEq for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: PartialEq,
I: Backend<T>,
B: Backend<T>,
{
fn eq(&self, other: &Self) -> bool {
// SAFETY: We forget them after
@ -302,19 +313,19 @@ where
}
}
impl<T, S, I> Eq for StuffedPtr<T, S, I>
impl<T, S, B> Eq for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: PartialEq + Eq,
I: Backend<T>,
B: Backend<T>,
{
}
impl<T, S, I> Hash for StuffedPtr<T, S, I>
impl<T, S, B> Hash for StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
S: StuffingStrategy<B>,
S::Extra: Hash,
I: Backend<T>,
B: Backend<T>,
{
fn hash<H: Hasher>(&self, state: &mut H) {
// SAFETY: We forget that `extra` ever existed after taking the reference and cloning it
@ -346,10 +357,10 @@ mod tests {
// note: the tests mostly use the `PanicsInDrop` type and strategy, to make sure that no
// extra is ever dropped accidentally.
fn from_box<T, S, I>(boxed: Box<T>) -> StuffedPtr<T, S, I>
fn from_box<T, S, B>(boxed: Box<T>) -> StuffedPtr<T, S, B>
where
S: StuffingStrategy<I>,
I: Backend<T>,
S: StuffingStrategy<B>,
B: Backend<T>,
{
StuffedPtr::new_ptr(Box::into_raw(boxed))
}

View file

@ -5,6 +5,8 @@
/// it's also completely possible to implement it on the type in [`StuffingStrategy::Extra`] directly
/// if possible.
///
/// The generic parameter `B` stands for the [`Backend`](`crate::Backend`) used by the strategy.
///
/// # Safety
///
/// If [`StuffingStrategy::is_extra`] returns true for a value, then
@ -15,22 +17,22 @@
///
/// For [`StuffingStrategy::stuff_ptr`] and [`StuffingStrategy::extract_ptr`],
/// `ptr == extract_ptr(stuff_ptr(ptr))` *must* hold true.
pub unsafe trait StuffingStrategy<I> {
pub unsafe trait StuffingStrategy<B> {
/// The type of the extra.
type Extra;
/// Checks whether the `StufferPtr` data value contains an extra value. The result of this
/// function can be trusted.
fn is_extra(data: I) -> bool;
fn is_extra(data: B) -> bool;
/// Stuff extra data into a usize that is then put into the pointer. This operation
/// must be infallible.
fn stuff_extra(inner: Self::Extra) -> I;
fn stuff_extra(inner: Self::Extra) -> B;
/// Extract extra data from the data.
/// # Safety
/// `data` must contain data created by [`StuffingStrategy::stuff_extra`].
unsafe fn extract_extra(data: I) -> Self::Extra;
unsafe fn extract_extra(data: B) -> Self::Extra;
/// Stuff a pointer address into the pointer sized integer.
///
@ -38,12 +40,12 @@ pub unsafe trait StuffingStrategy<I> {
/// cursed things with it.
///
/// The default implementation just returns the address directly.
fn stuff_ptr(addr: usize) -> I;
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: I) -> usize;
fn extract_ptr(inner: B) -> usize;
}
unsafe impl StuffingStrategy<usize> for () {