Expand description
A crate for stuffing things into a pointer.
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 other data.
You can choose any arbitrary bitstuffing depending on the StuffingStrategy, an unsafe trait that governs
how the other 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
or null in there, but we stay with floats and pointers (pointers to a HashMap that servers
as our “object” type).
See crafting interpreters for more details.
use std::collections::HashMap;
use stuff::{StuffedPtr, StuffingStrategy};
// Create a unit struct for our strategy
struct NanBoxStrategy;
// implementation detail of NaN boxing, a quiet NaN mask
const QNAN: u64 = 0x7ffc000000000000;
// implementation detail of NaN boxing, the sign bit of an f64
const SIGN_BIT: u64 = 0x8000000000000000;
unsafe impl StuffingStrategy<u64> 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
}
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
type Object = HashMap<String, u32>;
// our value type
type Value = StuffedPtr<Object, NanBoxStrategy, u64>;
let float: Value = StuffedPtr::new_other(123.5);
assert_eq!(float.copy_other(), Some(123.5));
let object: Object = HashMap::from([("a".to_owned(), 457)]);
let boxed = Box::new(object);
let ptr: Value = StuffedPtr::new_ptr(Box::into_raw(boxed));
let object = unsafe { &*ptr.get_ptr().unwrap() };
assert_eq!(object.get("a"), Some(&457));
drop(unsafe { Box::from_raw(ptr.get_ptr().unwrap()) });
// be careful, `ptr` is a dangling pointer now!Structs§
- Stuffed
Ptr - A union of a pointer or some
otherdata, bitpacked into a value with the size depending onB. It defaults tousize, meaning pointer sized, butu64andu128are also provided by this crate. You can also provide your ownBackendimplementation
Traits§
- Backend
- A backend where the stuffed pointer is stored. Must be bigger or equal to the pointer size.
- Stuffing
Strategy - A trait that describes how to stuff others and pointers into the pointer sized object.