mirror of
https://github.com/Noratrieb/stuff.git
synced 2026-01-15 17:05:08 +01:00
Require StuffingStrategy::Other: Copy
This makes everything so much simpler and nicer
This commit is contained in:
parent
6ce36d0506
commit
7d95a7dfaf
2 changed files with 72 additions and 268 deletions
286
src/lib.rs
286
src/lib.rs
|
|
@ -32,7 +32,7 @@
|
||||||
//! # use std::convert::{TryFrom, TryInto};
|
//! # use std::convert::{TryFrom, TryInto};
|
||||||
//! use std::mem::ManuallyDrop;
|
//! use std::mem::ManuallyDrop;
|
||||||
//!
|
//!
|
||||||
//! use stuff::{StuffedPtr, StuffingStrategy, Either};
|
//! use stuff::{StuffedPtr, StuffingStrategy, Unstuffed};
|
||||||
//!
|
//!
|
||||||
//! // Create a unit struct for our strategy
|
//! // Create a unit struct for our strategy
|
||||||
//! struct NanBoxStrategy;
|
//! struct NanBoxStrategy;
|
||||||
|
|
@ -49,11 +49,11 @@
|
||||||
//! 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(data: u64) -> Either<usize, ManuallyDrop<f64>> {
|
//! fn extract(data: u64) -> Unstuffed<usize, f64> {
|
||||||
//! if (data & QNAN) != QNAN {
|
//! if (data & QNAN) != QNAN {
|
||||||
//! Either::Other(ManuallyDrop::new(f64::from_bits(data)))
|
//! Unstuffed::Other(f64::from_bits(data))
|
||||||
//! } else {
|
//! } else {
|
||||||
//! Either::Ptr((data & !(SIGN_BIT | QNAN)).try_into().unwrap())
|
//! Unstuffed::Ptr((data & !(SIGN_BIT | QNAN)).try_into().unwrap())
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
|
@ -70,16 +70,16 @@
|
||||||
//! type Value = StuffedPtr<Object, NanBoxStrategy, u64>;
|
//! type Value = StuffedPtr<Object, NanBoxStrategy, u64>;
|
||||||
//!
|
//!
|
||||||
//! let float: Value = StuffedPtr::new_other(123.5);
|
//! let float: Value = StuffedPtr::new_other(123.5);
|
||||||
//! assert_eq!(float.copy_other(), Some(123.5));
|
//! assert_eq!(float.other(), Some(123.5));
|
||||||
//!
|
//!
|
||||||
//! let object: Object = HashMap::from([("a".to_owned(), 457)]);
|
//! let object: Object = HashMap::from([("a".to_owned(), 457)]);
|
||||||
//! let boxed = Box::new(object);
|
//! let boxed = Box::new(object);
|
||||||
//! let ptr: Value = StuffedPtr::new_ptr(Box::into_raw(boxed));
|
//! let ptr: Value = StuffedPtr::new_ptr(Box::into_raw(boxed));
|
||||||
//!
|
//!
|
||||||
//! let object = unsafe { &*ptr.get_ptr().unwrap() };
|
//! let object = unsafe { &*ptr.ptr().unwrap() };
|
||||||
//! assert_eq!(object.get("a"), Some(&457));
|
//! assert_eq!(object.get("a"), Some(&457));
|
||||||
//!
|
//!
|
||||||
//! drop(unsafe { Box::from_raw(ptr.get_ptr().unwrap()) });
|
//! drop(unsafe { Box::from_raw(ptr.ptr().unwrap()) });
|
||||||
//!
|
//!
|
||||||
//! // be careful, `ptr` is a dangling pointer now!
|
//! // be careful, `ptr` is a dangling pointer now!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
@ -97,12 +97,11 @@ use core::{
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
mem::ManuallyDrop,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use sptr::Strict;
|
use sptr::Strict;
|
||||||
|
|
||||||
pub use crate::{backend::Backend, either::Either, guard::Guard, strategy::StuffingStrategy};
|
pub use crate::{backend::Backend, either::Unstuffed, 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
|
||||||
|
|
@ -114,13 +113,12 @@ pub use crate::{backend::Backend, either::Either, guard::Guard, strategy::Stuffi
|
||||||
///
|
///
|
||||||
/// For a usage example, view the crate level documentation.
|
/// For a usage example, view the crate level documentation.
|
||||||
///
|
///
|
||||||
/// This pointer does *not* drop `other` data, [`StuffedPtr::into_other`] can be used if that is required.
|
/// `StuffedPtr` implements most traits like `Hash` or `PartialEq` if the `other` type does.
|
||||||
///
|
/// It's also always `Copy`, and therefore requires the other type to be `Copy` as well.
|
||||||
/// `StuffedPtr` implements most traits like `Clone`, `PartialEq` or `Copy` if the `other` type does.
|
|
||||||
///
|
///
|
||||||
/// 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<Either<*mut T, S>>)
|
pub struct StuffedPtr<T, S, B = usize>(B::Stored, PhantomData<Unstuffed<*mut T, S>>)
|
||||||
where
|
where
|
||||||
B: Backend;
|
B: Backend;
|
||||||
|
|
||||||
|
|
@ -146,40 +144,23 @@ 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 ptr(&self) -> Option<*mut T> {
|
||||||
let (provenance, stored) = B::get_ptr(self.0);
|
let (provenance, stored) = B::get_ptr(self.0);
|
||||||
let addr = unsafe { S::extract(stored).ptr()? };
|
let addr = S::extract(stored).ptr()?;
|
||||||
Some(Strict::with_addr(provenance.cast::<T>(), addr))
|
Some(Strict::with_addr(provenance.cast::<T>(), addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get owned `other` data from this, or `None` if it contains pointer data
|
|
||||||
pub unsafe fn into_other(self) -> Option<S::Other> {
|
|
||||||
let this = ManuallyDrop::new(self);
|
|
||||||
// SAFETY: `self` is consumed and forgotten after this call
|
|
||||||
let other = this.get_other();
|
|
||||||
other.map(|md| ManuallyDrop::into_inner(md))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get `other` data from this, or `None` if it contains pointer data
|
/// Get `other` data from this, or `None` if it contains pointer data
|
||||||
pub unsafe fn get_other(&self) -> Option<ManuallyDrop<S::Other>> {
|
pub fn other(&self) -> Option<S::Other> {
|
||||||
let data = self.addr();
|
let data = self.addr();
|
||||||
unsafe { S::extract(data).other() }
|
S::extract(data).other()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> Either<*mut T, S::Other> {
|
/// Get out the unstuffed enum representation
|
||||||
|
pub fn unstuff(&self) -> Unstuffed<*mut T, S::Other> {
|
||||||
let (provenance, stored) = B::get_ptr(self.0);
|
let (provenance, stored) = B::get_ptr(self.0);
|
||||||
let either = unsafe { S::extract(stored) };
|
let either = S::extract(stored);
|
||||||
either
|
either.map_ptr(|addr| Strict::with_addr(provenance.cast::<T>(), addr))
|
||||||
.map_ptr(|addr| Strict::with_addr(provenance.cast::<T>(), addr))
|
|
||||||
.map_other(|other| ManuallyDrop::into_inner(other))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
|
@ -187,20 +168,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extra implementations if the `other` type is `Copy`
|
|
||||||
impl<T, S, B> StuffedPtr<T, S, B>
|
|
||||||
where
|
|
||||||
S: StuffingStrategy<B>,
|
|
||||||
S::Other: Copy,
|
|
||||||
B: Backend,
|
|
||||||
{
|
|
||||||
/// Get `other` data from this, or `None` if it's pointer data
|
|
||||||
pub fn copy_other(&self) -> Option<S::Other> {
|
|
||||||
// SAFETY: `S::Other: Copy`
|
|
||||||
unsafe { self.get_other().map(|other| *other) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, B> Debug for StuffedPtr<T, S, B>
|
impl<T, S, B> Debug for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
|
|
@ -208,9 +175,9 @@ where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
match self.get() {
|
match self.unstuff() {
|
||||||
Either::Ptr(ptr) => f.debug_tuple("Ptr").field(&ptr).finish(),
|
Unstuffed::Ptr(ptr) => f.debug_tuple("Ptr").field(&ptr).finish(),
|
||||||
Either::Other(other) => f.debug_tuple("Other").field(other.inner()).finish(),
|
Unstuffed::Other(other) => f.debug_tuple("Other").field(&other).finish(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -218,24 +185,16 @@ where
|
||||||
impl<T, S, B> Clone for StuffedPtr<T, S, B>
|
impl<T, S, B> Clone for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Clone,
|
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self.get() {
|
*self
|
||||||
Either::Ptr(ptr) => StuffedPtr::new_ptr(ptr),
|
|
||||||
Either::Other(other) => {
|
|
||||||
let cloned_other = other.inner().clone();
|
|
||||||
Self::new_other(cloned_other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S, B> Copy for StuffedPtr<T, S, B>
|
impl<T, S, B> Copy for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Other: Copy,
|
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -247,9 +206,9 @@ where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
match (self.get(), other.get()) {
|
match (self.unstuff(), other.unstuff()) {
|
||||||
(Either::Ptr(a), Either::Ptr(b)) => core::ptr::eq(a, b),
|
(Unstuffed::Ptr(a), Unstuffed::Ptr(b)) => core::ptr::eq(a, b),
|
||||||
(Either::Other(a), Either::Other(b)) => a.inner() == b.inner(),
|
(Unstuffed::Other(a), Unstuffed::Other(b)) => a == b,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -270,25 +229,29 @@ where
|
||||||
B: Backend,
|
B: Backend,
|
||||||
{
|
{
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
match self.get() {
|
match self.unstuff() {
|
||||||
Either::Ptr(ptr) => {
|
Unstuffed::Ptr(ptr) => {
|
||||||
ptr.hash(state);
|
ptr.hash(state);
|
||||||
}
|
}
|
||||||
Either::Other(other) => {
|
Unstuffed::Other(other) => {
|
||||||
other.inner().hash(state);
|
other.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod either {
|
mod either {
|
||||||
|
/// The enum representation of a `StuffedPtr`
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Either<P, O> {
|
pub enum Unstuffed<P, O> {
|
||||||
|
/// The pointer or pointer address
|
||||||
Ptr(P),
|
Ptr(P),
|
||||||
|
/// The other type
|
||||||
Other(O),
|
Other(O),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Copy, O> Either<P, O> {
|
impl<P: Copy, O> Unstuffed<P, O> {
|
||||||
|
/// Get the pointer, or `None` if it's the other
|
||||||
pub fn ptr(&self) -> Option<P> {
|
pub fn ptr(&self) -> Option<P> {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Ptr(ptr) => Some(ptr),
|
Self::Ptr(ptr) => Some(ptr),
|
||||||
|
|
@ -296,6 +259,7 @@ mod either {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the other type, or `None` if it's the pointer
|
||||||
pub fn other(self) -> Option<O> {
|
pub fn other(self) -> Option<O> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ptr(_) => None,
|
Self::Ptr(_) => None,
|
||||||
|
|
@ -303,126 +267,13 @@ mod either {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_ptr<U>(self, f: impl FnOnce(P) -> U) -> Either<U, O> {
|
/// Maps the pointer type if it's a pointer, or does nothing if it's other
|
||||||
|
pub fn map_ptr<U>(self, f: impl FnOnce(P) -> U) -> Unstuffed<U, O> {
|
||||||
match self {
|
match self {
|
||||||
Self::Ptr(ptr) => Either::Ptr(f(ptr)),
|
Self::Ptr(ptr) => Unstuffed::Ptr(f(ptr)),
|
||||||
Self::Other(other) => Either::Other(other),
|
Self::Other(other) => Unstuffed::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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,19 +281,15 @@ mod guard {
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use core::mem;
|
|
||||||
use std::{boxed::Box, format, println};
|
use std::{boxed::Box, format, println};
|
||||||
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
strategy::test_strategies::{EmptyInMax, HasDebug, PanicsInDrop},
|
strategy::test_strategies::{EmptyInMax, HasDebug},
|
||||||
Backend, StuffedPtr, StuffingStrategy,
|
Backend, StuffedPtr, StuffingStrategy,
|
||||||
};
|
};
|
||||||
|
|
||||||
// note: the tests mostly use the `PanicsInDrop` type and strategy, to make sure that no
|
|
||||||
// `other` is ever dropped accidentally.
|
|
||||||
|
|
||||||
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>,
|
||||||
|
|
@ -459,7 +306,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().unwrap();
|
let ptr = stuffed_ptr.ptr().unwrap();
|
||||||
let boxed = Box::from_raw(ptr);
|
let boxed = Box::from_raw(ptr);
|
||||||
assert_eq!(*boxed, 1);
|
assert_eq!(*boxed, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -469,8 +316,8 @@ 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!(unsafe { stuffed_ptr.get_other() }.is_some());
|
assert!(stuffed_ptr.other().is_some());
|
||||||
assert!(matches!(stuffed_ptr.copy_other(), Some(EmptyInMax)));
|
assert!(matches!(stuffed_ptr.other(), Some(EmptyInMax)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -480,7 +327,7 @@ mod tests {
|
||||||
println!("{stuffed_ptr:?}");
|
println!("{stuffed_ptr:?}");
|
||||||
assert!(format!("{stuffed_ptr:?}").starts_with("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.ptr().unwrap()) });
|
||||||
|
|
||||||
let other = HasDebug;
|
let other = HasDebug;
|
||||||
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_other(other);
|
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_other(other);
|
||||||
|
|
@ -494,13 +341,11 @@ mod tests {
|
||||||
#[allow(clippy::redundant_clone)]
|
#[allow(clippy::redundant_clone)]
|
||||||
fn [<clone__ $backend>]() {
|
fn [<clone__ $backend>]() {
|
||||||
let mut unit = ();
|
let mut unit = ();
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr1: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
let _ = stuffed_ptr1.clone();
|
let _ = stuffed_ptr1.clone();
|
||||||
|
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
let stuffed_ptr1: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_other(EmptyInMax);
|
||||||
let stuffed_ptr2 = stuffed_ptr1.clone();
|
let _ = stuffed_ptr1.clone();
|
||||||
|
|
||||||
mem::forget((stuffed_ptr1, stuffed_ptr2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -508,42 +353,17 @@ mod tests {
|
||||||
fn [<eq__ $backend>]() {
|
fn [<eq__ $backend>]() {
|
||||||
// two pointers
|
// two pointers
|
||||||
let mut unit = ();
|
let mut unit = ();
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr1: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr2: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
|
|
||||||
assert_eq!(stuffed_ptr1, stuffed_ptr2);
|
assert_eq!(stuffed_ptr1, stuffed_ptr2);
|
||||||
|
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr1: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
let stuffed_ptr2: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_other(EmptyInMax);
|
||||||
|
|
||||||
assert_ne!(stuffed_ptr1, stuffed_ptr2);
|
assert_ne!(stuffed_ptr1, stuffed_ptr2);
|
||||||
mem::forget(stuffed_ptr2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn [<dont_drop_other_when_pointer__ $backend>]() {
|
|
||||||
let mut unit = ();
|
|
||||||
let stuffed_ptr: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
|
||||||
// the panicking drop needs not to be called here!
|
|
||||||
drop(stuffed_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn [<some_traits_dont_drop__ $backend>]() {
|
|
||||||
// make sure that other is never dropped twice
|
|
||||||
|
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
|
||||||
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
|
||||||
|
|
||||||
// PartialEq
|
|
||||||
assert_eq!(stuffed_ptr1, stuffed_ptr2);
|
|
||||||
// Debug
|
|
||||||
let _ = format!("{stuffed_ptr1:?}");
|
|
||||||
|
|
||||||
mem::forget((stuffed_ptr1, stuffed_ptr2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use core::{convert::TryInto, mem::ManuallyDrop};
|
use core::convert::TryInto;
|
||||||
|
|
||||||
use crate::{Backend, Either};
|
use crate::{Backend, Unstuffed};
|
||||||
|
|
||||||
/// 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.
|
||||||
///
|
///
|
||||||
|
|
@ -16,14 +16,11 @@ use crate::{Backend, Either};
|
||||||
/// If [`StuffingStrategy::is_other`] returns true for a value, then
|
/// If [`StuffingStrategy::is_other`] returns true for a value, then
|
||||||
/// [`StuffingStrategy::extract_other`] *must* return a valid `Other` for that same value.
|
/// [`StuffingStrategy::extract_other`] *must* return a valid `Other` for that same value.
|
||||||
///
|
///
|
||||||
/// [`StuffingStrategy::stuff_other`] *must* consume `inner` and make sure that it's not dropped
|
|
||||||
/// if it isn't `Copy`.
|
|
||||||
///
|
|
||||||
/// 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<B> {
|
pub unsafe trait StuffingStrategy<B> {
|
||||||
/// The type of the other.
|
/// The type of the other.
|
||||||
type Other;
|
type Other: Copy;
|
||||||
|
|
||||||
/// 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.
|
||||||
|
|
@ -32,7 +29,7 @@ pub unsafe trait StuffingStrategy<B> {
|
||||||
/// Extract the pointer data or other 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(data: B) -> Either<usize, ManuallyDrop<Self::Other>>;
|
fn extract(data: B) -> Unstuffed<usize, Self::Other>;
|
||||||
|
|
||||||
/// Stuff a pointer address into the pointer sized integer.
|
/// Stuff a pointer address into the pointer sized integer.
|
||||||
///
|
///
|
||||||
|
|
@ -54,8 +51,8 @@ where
|
||||||
B::default()
|
B::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract(data: B) -> Either<usize, ManuallyDrop<Self::Other>> {
|
fn extract(data: B) -> Unstuffed<usize, Self::Other> {
|
||||||
Either::Ptr(
|
Unstuffed::Ptr(
|
||||||
data.try_into()
|
data.try_into()
|
||||||
// note: this can't happen 🤔
|
// note: this can't happen 🤔
|
||||||
.unwrap_or_else(|_| panic!("Pointer value too big for usize")),
|
.unwrap_or_else(|_| panic!("Pointer value too big for usize")),
|
||||||
|
|
@ -70,13 +67,10 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod test_strategies {
|
pub(crate) mod test_strategies {
|
||||||
use core::{
|
use core::fmt::{Debug, Formatter};
|
||||||
fmt::{Debug, Formatter},
|
|
||||||
mem::ManuallyDrop,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::StuffingStrategy;
|
use super::StuffingStrategy;
|
||||||
use crate::Either;
|
use crate::Unstuffed;
|
||||||
|
|
||||||
macro_rules! impl_usize_max_zst {
|
macro_rules! impl_usize_max_zst {
|
||||||
($ty:ident) => {
|
($ty:ident) => {
|
||||||
|
|
@ -90,10 +84,10 @@ pub(crate) mod test_strategies {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract(data: usize) -> Either<usize, ManuallyDrop<Self::Other>> {
|
fn extract(data: usize) -> Unstuffed<usize, Self::Other> {
|
||||||
match data == usize::MAX {
|
match data == usize::MAX {
|
||||||
true => Either::Other(ManuallyDrop::new($ty)),
|
true => Unstuffed::Other($ty),
|
||||||
false => Either::Ptr(data),
|
false => Unstuffed::Ptr(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,10 +105,10 @@ pub(crate) mod test_strategies {
|
||||||
u64::MAX
|
u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract(data: u64) -> Either<usize, ManuallyDrop<Self::Other>> {
|
fn extract(data: u64) -> Unstuffed<usize, Self::Other> {
|
||||||
match data == u64::MAX {
|
match data == u64::MAX {
|
||||||
true => Either::Other(ManuallyDrop::new($ty)),
|
true => Unstuffed::Other($ty),
|
||||||
false => Either::Ptr(data as usize),
|
false => Unstuffed::Ptr(data as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,10 +126,10 @@ pub(crate) mod test_strategies {
|
||||||
u128::MAX
|
u128::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract(data: u128) -> Either<usize, ManuallyDrop<Self::Other>> {
|
fn extract(data: u128) -> Unstuffed<usize, Self::Other> {
|
||||||
match data == u128::MAX {
|
match data == u128::MAX {
|
||||||
true => Either::Other(ManuallyDrop::new($ty)),
|
true => Unstuffed::Other($ty),
|
||||||
false => Either::Ptr(data as usize),
|
false => Unstuffed::Ptr(data as usize),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,11 +140,12 @@ pub(crate) mod test_strategies {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct EmptyInMax;
|
pub struct EmptyInMax;
|
||||||
|
|
||||||
impl_usize_max_zst!(EmptyInMax);
|
impl_usize_max_zst!(EmptyInMax);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub struct HasDebug;
|
pub struct HasDebug;
|
||||||
|
|
||||||
impl Debug for HasDebug {
|
impl Debug for HasDebug {
|
||||||
|
|
@ -160,15 +155,4 @@ pub(crate) mod test_strategies {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_usize_max_zst!(HasDebug);
|
impl_usize_max_zst!(HasDebug);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct PanicsInDrop;
|
|
||||||
|
|
||||||
impl Drop for PanicsInDrop {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
panic!("oh no!!!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_usize_max_zst!(PanicsInDrop);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue