mirror of
https://github.com/Noratrieb/stuff.git
synced 2026-01-14 16:35:08 +01:00
166 lines
6.4 KiB
Rust
166 lines
6.4 KiB
Rust
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<A, B>() {
|
|
let has_equal_size = mem::size_of::<A>() == mem::size_of::<B>();
|
|
assert!(has_equal_size);
|
|
}
|
|
|
|
#[cfg(not(target_pointer_width = "16"))]
|
|
const _: () = assert_same_size::<u128, <u128 as Backend>::Stored>();
|
|
const _: () = assert_same_size::<u64, <u64 as Backend>::Stored>();
|
|
const _: () = assert_same_size::<usize, <usize as Backend>::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
|