mirror of
https://github.com/Noratrieb/stuff.git
synced 2026-01-14 16:35:08 +01:00
try to have a safer api around drops
This commit is contained in:
parent
041a4cd670
commit
6ce36d0506
5 changed files with 291 additions and 238 deletions
3
clippy.toml
Normal file
3
clippy.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "core::mem::forget", reason = "mem::forget is sketchy around unsafe code, use ManuallyDrop instead" }
|
||||||
|
]
|
||||||
|
|
@ -6,7 +6,7 @@ use sptr::Strict;
|
||||||
/// be implemented on `Copy` types like `usize`, `u64`, or `u128`. Note that the `Self` type here
|
/// 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`)
|
/// 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
|
/// 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`.
|
/// size as `Backend::Stored`.
|
||||||
///
|
///
|
||||||
/// This trait is just exposed for convenience and flexibility, you are usually not expected to implement
|
/// This trait is just exposed for convenience and flexibility, you are usually not expected to implement
|
||||||
|
|
@ -16,7 +16,7 @@ use sptr::Strict;
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Implementers of this trait *must* keep provenance of pointers, so if a valid pointer address+provenance
|
/// 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.
|
/// combination is set in `set_ptr`, `get_ptr` *must* return the exact same values and provenance.
|
||||||
pub unsafe trait Backend<T> {
|
pub unsafe trait Backend {
|
||||||
/// The underlying type where the data is stored. Often a tuple of a pointer (for the provenance)
|
/// 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.
|
/// and some integers to fill up the bytes.
|
||||||
type Stored: Copy;
|
type Stored: Copy;
|
||||||
|
|
@ -25,11 +25,11 @@ pub unsafe trait Backend<T> {
|
||||||
/// is able to use the full bytes to pack in the pointer address, the full address is returned
|
/// 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
|
/// 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.
|
/// 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,
|
/// 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.
|
/// 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,
|
/// 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.
|
/// for that use [`Backend::get_ptr`] to keep the provenance.
|
||||||
|
|
@ -49,19 +49,19 @@ mod backend_size_asserts {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_pointer_width = "16"))]
|
#[cfg(not(target_pointer_width = "16"))]
|
||||||
const _: () = assert_same_size::<u128, <u128 as Backend<()>>::Stored>();
|
const _: () = assert_same_size::<u128, <u128 as Backend>::Stored>();
|
||||||
const _: () = assert_same_size::<u64, <u64 as Backend<()>>::Stored>();
|
const _: () = assert_same_size::<u64, <u64 as Backend>::Stored>();
|
||||||
const _: () = assert_same_size::<usize, <usize as Backend<()>>::Stored>();
|
const _: () = assert_same_size::<usize, <usize as Backend>::Stored>();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T> Backend<T> for usize {
|
unsafe impl Backend for usize {
|
||||||
type Stored = *mut T;
|
type Stored = *mut ();
|
||||||
|
|
||||||
fn get_ptr(s: Self::Stored) -> (*mut T, Self) {
|
fn get_ptr(s: Self::Stored) -> (*mut (), Self) {
|
||||||
(s, Strict::addr(s))
|
(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)
|
Strict::with_addr(provenance, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,14 +72,14 @@ unsafe impl<T> Backend<T> for usize {
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
/// on 64 bit, we can just treat u64/usize interchangeably, because uintptr_t == size_t in Rust
|
/// on 64 bit, we can just treat u64/usize interchangeably, because uintptr_t == size_t in Rust
|
||||||
unsafe impl<T> Backend<T> for u64 {
|
unsafe impl Backend for u64 {
|
||||||
type Stored = *mut T;
|
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)
|
(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)
|
Strict::with_addr(provenance, addr as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,17 +89,17 @@ unsafe impl<T> Backend<T> for u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_backend_2_tuple {
|
macro_rules! impl_backend_2_tuple {
|
||||||
(impl for $ty:ty { (*mut T, $int:ident), $num:expr }) => {
|
(impl for $ty:ty { (*mut (), $int:ident), $num:expr }) => {
|
||||||
unsafe impl<T> Backend<T> for $ty {
|
unsafe impl Backend for $ty {
|
||||||
// this one keeps the MSB in the pointer address, and the LSB in the integer
|
// 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))
|
(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 ptr_addr = (addr >> $num) as usize;
|
||||||
let int_addr = addr as $int; // truncate it
|
let int_addr = addr as $int; // truncate it
|
||||||
(Strict::with_addr(provenance, ptr_addr), int_addr)
|
(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
|
/// num1 is ptr-sized, num2 is 2*ptr sized
|
||||||
#[cfg_attr(target_pointer_width = "64", allow(unused))] // not required on 64 bit
|
#[cfg_attr(target_pointer_width = "64", allow(unused))] // not required on 64 bit
|
||||||
macro_rules! impl_backend_3_tuple {
|
macro_rules! impl_backend_3_tuple {
|
||||||
(impl for $ty:ty { (*mut T, $int1:ident, $int2:ident), $num1:expr, $num2:expr }) => {
|
(impl for $ty:ty { (*mut (), $int1:ident, $int2:ident), $num1:expr, $num2:expr }) => {
|
||||||
unsafe impl<T> Backend<T> for $ty {
|
unsafe impl Backend for $ty {
|
||||||
// this one keeps the MSB in the pointer address, ISB in int1 and the LSB in the int2
|
// 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))
|
(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 ptr_addr = (addr >> ($num1 + $num2)) as usize;
|
||||||
let num1_addr = (addr >> $num2) as $int1; // truncate it
|
let num1_addr = (addr >> $num2) as $int1; // truncate it
|
||||||
let num2_addr = addr as $int2; // 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")]
|
#[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")]
|
#[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")]
|
#[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")]
|
#[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
|
// no 128 on 16 bit for now
|
||||||
|
|
|
||||||
368
src/lib.rs
368
src/lib.rs
|
|
@ -1,6 +1,7 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
#![deny(clippy::disallowed_methods, clippy::undocumented_unsafe_blocks)]
|
||||||
|
|
||||||
//! A crate for stuffing things into a pointer.
|
//! A crate for stuffing things into a pointer.
|
||||||
//!
|
//!
|
||||||
|
|
@ -29,8 +30,9 @@
|
||||||
//! ```
|
//! ```
|
||||||
//! use std::collections::HashMap;
|
//! use std::collections::HashMap;
|
||||||
//! # use std::convert::{TryFrom, TryInto};
|
//! # 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
|
//! // Create a unit struct for our strategy
|
||||||
//! struct NanBoxStrategy;
|
//! struct NanBoxStrategy;
|
||||||
|
|
@ -43,27 +45,22 @@
|
||||||
//! unsafe impl StuffingStrategy<u64> for NanBoxStrategy {
|
//! unsafe impl StuffingStrategy<u64> for NanBoxStrategy {
|
||||||
//! type Other = f64;
|
//! type Other = f64;
|
||||||
//!
|
//!
|
||||||
//! fn is_other(data: u64) -> bool {
|
|
||||||
//! (data & QNAN) != QNAN
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn stuff_other(inner: Self::Other) -> u64 {
|
//! fn stuff_other(inner: Self::Other) -> u64 {
|
||||||
//! unsafe { std::mem::transmute(inner) } // both are 64 bit POD's
|
//! unsafe { std::mem::transmute(inner) } // both are 64 bit POD's
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! unsafe fn extract_other(data: u64) -> Self::Other {
|
//! unsafe fn extract(data: u64) -> Either<usize, ManuallyDrop<f64>> {
|
||||||
//! std::mem::transmute(data) // both are 64 bit POD's
|
//! 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 {
|
//! fn stuff_ptr(addr: usize) -> u64 {
|
||||||
//! // add the QNAN and SIGN_BIT
|
//! // add the QNAN and SIGN_BIT
|
||||||
//! SIGN_BIT | QNAN | u64::try_from(addr).unwrap()
|
//! 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
|
//! // a very, very crude representation of an object
|
||||||
|
|
@ -88,23 +85,24 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate alloc; // we want that for tests so we can use `Box`
|
extern crate std;
|
||||||
|
|
||||||
mod backend;
|
mod backend;
|
||||||
mod strategy;
|
mod strategy;
|
||||||
|
|
||||||
|
#[cfg(any())]
|
||||||
mod tag;
|
mod tag;
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
mem,
|
mem::ManuallyDrop,
|
||||||
ops::Not,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use sptr::Strict;
|
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
|
/// 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
|
/// `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`.
|
/// This type is guaranteed to be `#[repr(transparent)]` to a `B::Stored`.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct StuffedPtr<T, S, B = usize>(B::Stored, PhantomData<S>)
|
pub struct StuffedPtr<T, S, B = usize>(B::Stored, PhantomData<Either<*mut T, S>>)
|
||||||
where
|
where
|
||||||
B: Backend<T>;
|
B: Backend;
|
||||||
|
|
||||||
impl<T, S, B> StuffedPtr<T, S, B>
|
impl<T, S, B> StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
/// 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 {
|
||||||
let addr = Strict::addr(ptr);
|
let addr = Strict::addr(ptr);
|
||||||
let stuffed = S::stuff_ptr(addr);
|
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
|
/// Create a new `StuffPtr` from `other` data
|
||||||
|
|
@ -149,76 +147,44 @@ where
|
||||||
|
|
||||||
/// Get the pointer data, or `None` if it contains `other` data
|
/// Get the pointer data, or `None` if it contains `other` data
|
||||||
pub fn get_ptr(&self) -> Option<*mut T> {
|
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 (provenance, stored) = B::get_ptr(self.0);
|
||||||
let addr = S::extract_ptr(stored);
|
let addr = unsafe { S::extract(stored).ptr()? };
|
||||||
Strict::with_addr(provenance, addr)
|
Some(Strict::with_addr(provenance.cast::<T>(), addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get owned `other` data from this, or `None` if it contains pointer data
|
/// Get owned `other` data from this, or `None` if it contains pointer data
|
||||||
pub fn into_other(self) -> Option<S::Other> {
|
pub unsafe fn into_other(self) -> Option<S::Other> {
|
||||||
match self.is_other() {
|
let this = ManuallyDrop::new(self);
|
||||||
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 {
|
|
||||||
// SAFETY: `self` is consumed and forgotten after this call
|
// SAFETY: `self` is consumed and forgotten after this call
|
||||||
let other = self.get_other_unchecked();
|
let other = this.get_other();
|
||||||
mem::forget(self);
|
other.map(|md| ManuallyDrop::into_inner(md))
|
||||||
other
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get `other` data from this, or `None` if it contains pointer data
|
/// Get `other` data from this, or `None` if it contains pointer data
|
||||||
/// # Safety
|
pub unsafe fn get_other(&self) -> Option<ManuallyDrop<S::Other>> {
|
||||||
/// The caller must guarantee that only ever on `Other` exists if `Other: !Copy`
|
let data = self.addr();
|
||||||
pub unsafe fn get_other(&self) -> Option<S::Other> {
|
unsafe { S::extract(data).other() }
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get `other` data from this
|
pub fn into_inner(self) -> Either<*mut T, S::Other> {
|
||||||
/// # Safety
|
let (provenance, stored) = B::get_ptr(self.0);
|
||||||
/// Must contain `other` data and not pointer data,
|
let either = unsafe { S::extract(stored) };
|
||||||
/// and the caller must guarantee that only ever on `Other` exists if `Other: !Copy`
|
either
|
||||||
pub unsafe fn get_other_unchecked(&self) -> S::Other {
|
.map_ptr(|addr| Strict::with_addr(provenance.cast::<T>(), addr))
|
||||||
let data = self.addr();
|
.map_other(|other| ManuallyDrop::into_inner(other))
|
||||||
S::extract_other(data)
|
}
|
||||||
|
|
||||||
|
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::<T>(), addr))
|
||||||
|
.map_other(|other| Guard::new(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addr(&self) -> B {
|
fn addr(&self) -> B {
|
||||||
B::get_int(self.0)
|
B::get_int(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_other(&self) -> bool {
|
|
||||||
S::is_other(self.addr())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extra implementations if the `other` type is `Copy`
|
/// Extra implementations if the `other` type is `Copy`
|
||||||
|
|
@ -226,20 +192,12 @@ impl<T, S, B> StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Copy,
|
S::Other: Copy,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
/// Get `other` data from this, or `None` if it's pointer data
|
/// Get `other` data from this, or `None` if it's pointer data
|
||||||
pub fn copy_other(&self) -> Option<S::Other> {
|
pub fn copy_other(&self) -> Option<S::Other> {
|
||||||
// SAFETY: `S::Other: Copy`
|
// SAFETY: `S::Other: Copy`
|
||||||
unsafe { self.get_other() }
|
unsafe { self.get_other().map(|other| *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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,25 +205,12 @@ impl<T, S, B> Debug for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Debug,
|
S::Other: Debug,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
// SAFETY:
|
match self.get() {
|
||||||
// If S::Other: !Copy, we can't just copy it out and call it a day
|
Either::Ptr(ptr) => f.debug_tuple("Ptr").field(&ptr).finish(),
|
||||||
// For example, if it's a Box, not forgetting it here would lead to a double free
|
Either::Other(other) => f.debug_tuple("Other").field(other.inner()).finish(),
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,17 +219,15 @@ impl<T, S, B> Clone for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Clone,
|
S::Other: Clone,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// SAFETY: We forget that `other` ever existed after taking the reference and cloning it
|
match self.get() {
|
||||||
if let Some(other) = unsafe { self.get_other() } {
|
Either::Ptr(ptr) => StuffedPtr::new_ptr(ptr),
|
||||||
let cloned_other = other.clone();
|
Either::Other(other) => {
|
||||||
mem::forget(other);
|
let cloned_other = other.inner().clone();
|
||||||
Self::new_other(cloned_other)
|
Self::new_other(cloned_other)
|
||||||
} else {
|
}
|
||||||
// just copy the pointer
|
|
||||||
StuffedPtr(self.0, PhantomData)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +236,7 @@ impl<T, S, B> Copy for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Copy,
|
S::Other: Copy,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,28 +244,14 @@ impl<T, S, B> PartialEq for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: PartialEq,
|
S::Other: PartialEq,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
// SAFETY: We forget them after
|
match (self.get(), other.get()) {
|
||||||
let others = unsafe { (self.get_other(), other.get_other()) };
|
(Either::Ptr(a), Either::Ptr(b)) => core::ptr::eq(a, b),
|
||||||
|
(Either::Other(a), Either::Other(b)) => a.inner() == b.inner(),
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
}
|
||||||
|
|
||||||
mem::forget(others);
|
|
||||||
|
|
||||||
eq
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,7 +259,7 @@ impl<T, S, B> Eq for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: PartialEq + Eq,
|
S::Other: PartialEq + Eq,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,17 +267,161 @@ impl<T, S, B> Hash for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Hash,
|
S::Other: Hash,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
// SAFETY: We forget that `other` ever existed after taking the reference and cloning it
|
match self.get() {
|
||||||
if let Some(other) = unsafe { self.get_other() } {
|
Either::Ptr(ptr) => {
|
||||||
other.hash(state);
|
ptr.hash(state);
|
||||||
mem::forget(other);
|
}
|
||||||
} else {
|
Either::Other(other) => {
|
||||||
// SAFETY: Checked above
|
other.inner().hash(state);
|
||||||
let ptr = unsafe { self.get_ptr_unchecked() };
|
}
|
||||||
ptr.hash(state);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod either {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Either<P, O> {
|
||||||
|
Ptr(P),
|
||||||
|
Other(O),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Copy, O> Either<P, O> {
|
||||||
|
pub fn ptr(&self) -> Option<P> {
|
||||||
|
match *self {
|
||||||
|
Self::Ptr(ptr) => Some(ptr),
|
||||||
|
Self::Other(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn other(self) -> Option<O> {
|
||||||
|
match self {
|
||||||
|
Self::Ptr(_) => None,
|
||||||
|
Self::Other(other) => Some(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_ptr<U>(self, f: impl FnOnce(P) -> U) -> Either<U, O> {
|
||||||
|
match self {
|
||||||
|
Self::Ptr(ptr) => Either::Ptr(f(ptr)),
|
||||||
|
Self::Other(other) => Either::Other(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_other<U>(self, f: impl FnOnce(O) -> U) -> Either<P, U> {
|
||||||
|
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<T>(MaybeUninit<T>);
|
||||||
|
|
||||||
|
impl<T> AllowAlias<T> {
|
||||||
|
pub fn new(value: T) -> Self {
|
||||||
|
Self(MaybeUninit::new(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
unsafe { self.0.assume_init() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for AllowAlias<T> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_tuple("NoAlias").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> PartialEq for AllowAlias<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
&*self == &*other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Eq> Eq for AllowAlias<T> {}
|
||||||
|
|
||||||
|
impl<T: PartialOrd> PartialOrd for AllowAlias<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
|
||||||
|
self.deref().partial_cmp(&*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Ord> Ord for AllowAlias<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
|
||||||
|
self.deref().cmp(&*other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hash> Hash for AllowAlias<T> {
|
||||||
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.deref().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for AllowAlias<T> {
|
||||||
|
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<T>,
|
||||||
|
_boo: PhantomData<&'a ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Guard<'a, T> {
|
||||||
|
pub fn new(value: ManuallyDrop<T>) -> 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<T> {
|
||||||
|
ManuallyDrop::new(self.inner.into_inner())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> &T {
|
||||||
|
&*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Guard<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&*self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> 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 {
|
mod tests {
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use alloc::{boxed::Box, format};
|
|
||||||
use core::mem;
|
use core::mem;
|
||||||
|
use std::{boxed::Box, format, println};
|
||||||
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
|
|
@ -373,7 +446,7 @@ mod tests {
|
||||||
fn from_box<T, S, B>(boxed: Box<T>) -> StuffedPtr<T, S, B>
|
fn from_box<T, S, B>(boxed: Box<T>) -> StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
StuffedPtr::new_ptr(Box::into_raw(boxed))
|
StuffedPtr::new_ptr(Box::into_raw(boxed))
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +459,7 @@ mod tests {
|
||||||
unsafe {
|
unsafe {
|
||||||
let boxed = Box::new(1);
|
let boxed = Box::new(1);
|
||||||
let stuffed_ptr: StuffedPtr<i32, (), $backend> = from_box(boxed);
|
let stuffed_ptr: StuffedPtr<i32, (), $backend> = from_box(boxed);
|
||||||
let ptr = stuffed_ptr.get_ptr_unchecked();
|
let ptr = stuffed_ptr.get_ptr().unwrap();
|
||||||
let boxed = Box::from_raw(ptr);
|
let boxed = Box::from_raw(ptr);
|
||||||
assert_eq!(*boxed, 1);
|
assert_eq!(*boxed, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -396,7 +469,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn [<get_other__ $backend>]() {
|
fn [<get_other__ $backend>]() {
|
||||||
let stuffed_ptr: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_other(EmptyInMax);
|
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)));
|
assert!(matches!(stuffed_ptr.copy_other(), Some(EmptyInMax)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,7 +477,8 @@ mod tests {
|
||||||
fn [<debug__ $backend>]() {
|
fn [<debug__ $backend>]() {
|
||||||
let boxed = Box::new(1);
|
let boxed = Box::new(1);
|
||||||
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = from_box(boxed);
|
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = 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()) });
|
drop(unsafe { Box::from_raw(stuffed_ptr.get_ptr().unwrap()) });
|
||||||
|
|
||||||
|
|
@ -412,7 +486,7 @@ mod tests {
|
||||||
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_other(other);
|
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_other(other);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{stuffed_ptr:?}"),
|
format!("{stuffed_ptr:?}"),
|
||||||
"StuffedPtr::Other { other: hello! }"
|
"Other(hello!)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
/// A trait that describes how to stuff others and pointers into the pointer sized object.
|
||||||
///
|
///
|
||||||
|
|
@ -25,18 +25,14 @@ pub unsafe trait StuffingStrategy<B> {
|
||||||
/// The type of the other.
|
/// The type of the other.
|
||||||
type 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
|
/// Stuff other data into a usize that is then put into the pointer. This operation
|
||||||
/// must be infallible.
|
/// must be infallible.
|
||||||
fn stuff_other(inner: Self::Other) -> B;
|
fn stuff_other(inner: Self::Other) -> B;
|
||||||
|
|
||||||
/// Extract other data from the data.
|
/// Extract the pointer data or other data
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `data` must contain data created by [`StuffingStrategy::stuff_other`].
|
/// `data` must contain data created by [`StuffingStrategy::stuff_other`].
|
||||||
unsafe fn extract_other(data: B) -> Self::Other;
|
unsafe fn extract(data: B) -> Either<usize, ManuallyDrop<Self::Other>>;
|
||||||
|
|
||||||
/// Stuff a pointer address into the pointer sized integer.
|
/// Stuff a pointer address into the pointer sized integer.
|
||||||
///
|
///
|
||||||
|
|
@ -45,48 +41,42 @@ pub unsafe trait StuffingStrategy<B> {
|
||||||
///
|
///
|
||||||
/// The default implementation just returns the address directly.
|
/// The default implementation just returns the address directly.
|
||||||
fn stuff_ptr(addr: usize) -> B;
|
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<B> StuffingStrategy<B> for ()
|
unsafe impl<B> StuffingStrategy<B> for ()
|
||||||
where
|
where
|
||||||
B: Backend<()> + Default + TryInto<usize>,
|
B: Backend + Default + TryInto<usize>,
|
||||||
usize: TryInto<B>,
|
usize: TryInto<B>,
|
||||||
{
|
{
|
||||||
type Other = ();
|
type Other = ();
|
||||||
|
|
||||||
fn is_other(_data: B) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn stuff_other(_inner: Self::Other) -> B {
|
fn stuff_other(_inner: Self::Other) -> B {
|
||||||
B::default()
|
B::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_other(_data: B) -> Self::Other {}
|
unsafe fn extract(data: B) -> Either<usize, ManuallyDrop<Self::Other>> {
|
||||||
|
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 {
|
fn stuff_ptr(addr: usize) -> B {
|
||||||
addr.try_into()
|
addr.try_into()
|
||||||
.unwrap_or_else(|_| panic!("Address in `stuff_ptr` too big"))
|
.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)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test_strategies {
|
pub(crate) mod test_strategies {
|
||||||
use core::fmt::{Debug, Formatter};
|
use core::{
|
||||||
|
fmt::{Debug, Formatter},
|
||||||
|
mem::ManuallyDrop,
|
||||||
|
};
|
||||||
|
|
||||||
use super::StuffingStrategy;
|
use super::StuffingStrategy;
|
||||||
|
use crate::Either;
|
||||||
|
|
||||||
macro_rules! impl_usize_max_zst {
|
macro_rules! impl_usize_max_zst {
|
||||||
($ty:ident) => {
|
($ty:ident) => {
|
||||||
|
|
@ -94,78 +84,64 @@ pub(crate) mod test_strategies {
|
||||||
unsafe impl StuffingStrategy<usize> for $ty {
|
unsafe impl StuffingStrategy<usize> for $ty {
|
||||||
type Other = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_other(data: usize) -> bool {
|
|
||||||
data == usize::MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_other(inner: Self::Other) -> usize {
|
fn stuff_other(inner: Self::Other) -> usize {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
usize::MAX
|
usize::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_other(_data: usize) -> Self::Other {
|
unsafe fn extract(data: usize) -> Either<usize, ManuallyDrop<Self::Other>> {
|
||||||
$ty
|
match data == usize::MAX {
|
||||||
|
true => Either::Other(ManuallyDrop::new($ty)),
|
||||||
|
false => Either::Ptr(data),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> usize {
|
fn stuff_ptr(addr: usize) -> usize {
|
||||||
addr
|
addr
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_ptr(inner: usize) -> usize {
|
|
||||||
inner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u64> for $ty {
|
unsafe impl StuffingStrategy<u64> for $ty {
|
||||||
type Other = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_other(data: u64) -> bool {
|
|
||||||
data == u64::MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_other(inner: Self::Other) -> u64 {
|
fn stuff_other(inner: Self::Other) -> u64 {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
u64::MAX
|
u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_other(_data: u64) -> Self::Other {
|
unsafe fn extract(data: u64) -> Either<usize, ManuallyDrop<Self::Other>> {
|
||||||
$ty
|
match data == u64::MAX {
|
||||||
|
true => Either::Other(ManuallyDrop::new($ty)),
|
||||||
|
false => Either::Ptr(data as usize),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> u64 {
|
fn stuff_ptr(addr: usize) -> u64 {
|
||||||
addr as u64
|
addr as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_ptr(inner: u64) -> usize {
|
|
||||||
inner as usize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u128> for $ty {
|
unsafe impl StuffingStrategy<u128> for $ty {
|
||||||
type Other = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_other(data: u128) -> bool {
|
|
||||||
data == u128::MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_other(inner: Self::Other) -> u128 {
|
fn stuff_other(inner: Self::Other) -> u128 {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
u128::MAX
|
u128::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_other(_data: u128) -> Self::Other {
|
unsafe fn extract(data: u128) -> Either<usize, ManuallyDrop<Self::Other>> {
|
||||||
$ty
|
match data == u128::MAX {
|
||||||
|
true => Either::Other(ManuallyDrop::new($ty)),
|
||||||
|
false => Either::Ptr(data as usize),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> u128 {
|
fn stuff_ptr(addr: usize) -> u128 {
|
||||||
addr as u128
|
addr as u128
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_ptr(inner: u128) -> usize {
|
|
||||||
inner as usize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/tag.rs
10
src/tag.rs
|
|
@ -6,14 +6,14 @@ use sptr::Strict;
|
||||||
|
|
||||||
use crate::Backend;
|
use crate::Backend;
|
||||||
|
|
||||||
pub struct TaggedPtr<T, S, B = usize>(B::Stored, PhantomData<S>)
|
pub struct TaggedPtr<T, S, B = usize>(B::Stored, PhantomData<(S, *mut T)>)
|
||||||
where
|
where
|
||||||
B: Backend<T>;
|
B: Backend;
|
||||||
|
|
||||||
impl<T, S, B> TaggedPtr<T, S, B>
|
impl<T, S, B> TaggedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: TaggingStrategy<B>,
|
S: TaggingStrategy<B>,
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
pub fn new(ptr: *mut T, tag: S::Tag) -> Self {
|
pub fn new(ptr: *mut T, tag: S::Tag) -> Self {
|
||||||
let addr = Strict::addr(ptr);
|
let addr = Strict::addr(ptr);
|
||||||
|
|
@ -44,14 +44,14 @@ where
|
||||||
|
|
||||||
impl<T, S, B> Clone for TaggedPtr<T, S, B>
|
impl<T, S, B> Clone for TaggedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
B: Backend<T>,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
TaggedPtr(self.0, self.1)
|
TaggedPtr(self.0, self.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, B> Copy for TaggedPtr<T, S, B> where B: Backend<T> {}
|
impl<T, S, B> Copy for TaggedPtr<T, S, B> where B: Backend {}
|
||||||
|
|
||||||
pub trait TaggingStrategy<B> {
|
pub trait TaggingStrategy<B> {
|
||||||
type Tag: Copy;
|
type Tag: Copy;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue