mirror of
https://github.com/Noratrieb/stuff.git
synced 2026-01-14 16:35:08 +01:00
rename extra to other
this is a breaking change
This commit is contained in:
parent
299e1613ff
commit
bde74a68ba
4 changed files with 167 additions and 160 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "stuff"
|
name = "stuff"
|
||||||
version = "0.1.4"
|
version = "0.2.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Stuffing things into pointers."
|
description = "Stuffing things into pointers."
|
||||||
readme = "./README.md"
|
readme = "./README.md"
|
||||||
|
|
|
||||||
49
README.md
49
README.md
|
|
@ -20,10 +20,10 @@ the user, allowing the user to do their bit stuffing only on integers (pointer a
|
||||||
|
|
||||||
`StuffedPtr` is the main type of this crate. It's a type whose size depends on the
|
`StuffedPtr` is the main type of this crate. 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
|
choice of `Backend` (defaults to `usize`, `u64` and `u128` are also possible). It can store a
|
||||||
pointer or some extra data.
|
pointer or some `other` data.
|
||||||
|
|
||||||
You can choose any arbitrary bitstuffing depending on the `StuffingStrategy`, an unsafe trait that governs
|
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,
|
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.
|
it's a lot safer than doing everything by hand.
|
||||||
|
|
||||||
# Example: NaN-Boxing
|
# Example: NaN-Boxing
|
||||||
|
|
@ -36,59 +36,62 @@ for more details.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use stuff::{StuffedPtr, StuffingStrategy};
|
use stuff::{StuffedPtr, StuffingStrategy};
|
||||||
|
|
||||||
// Create a unit struct for our strategy
|
// Create a unit struct for our strategy
|
||||||
struct NanBoxStrategy;
|
struct NanBoxStrategy;
|
||||||
|
|
||||||
const QNAN: u64 = 0x7ffc000000000000; // implementation detail of NaN boxing, a quiet NaN mask
|
// implementation detail of NaN boxing, a quiet NaN mask
|
||||||
|
const QNAN: u64 = 0x7ffc000000000000;
|
||||||
const SIGN_BIT: u64 = 0x8000000000000000; // implementation detail of NaN boxing, the sign bit of an f64
|
// implementation detail of NaN boxing, the sign bit of an f64
|
||||||
|
const SIGN_BIT: u64 = 0x8000000000000000;
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u64> for NanBoxStrategy {
|
unsafe impl StuffingStrategy<u64> for NanBoxStrategy {
|
||||||
type Extra = f64;
|
type Other = f64;
|
||||||
|
|
||||||
fn is_extra(data: u64) -> bool {
|
fn is_other(data: u64) -> bool {
|
||||||
(data & QNAN) != QNAN
|
(data & QNAN) != QNAN
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_extra(inner: Self::Extra) -> 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_extra(data: u64) -> Self::Extra {
|
unsafe fn extract_other(data: u64) -> Self::Other {
|
||||||
std::mem::transmute(data) // both are 64 bit POD's
|
std::mem::transmute(data) // both are 64 bit POD's
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
fn extract_ptr(inner: u64) -> usize {
|
||||||
// keep everything except for QNAN and SIGN_BIT
|
// keep everything except for QNAN and SIGN_BIT
|
||||||
(inner & !(SIGN_BIT | QNAN)).try_into().unwrap()
|
(inner & !(SIGN_BIT | QNAN)).try_into().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Object = HashMap<String, u32>; // a very, very crude representation of an object
|
// a very, very crude representation of an object
|
||||||
|
type Object = HashMap<String, u32>;
|
||||||
|
|
||||||
type Value = StuffedPtr<Object, NanBoxStrategy, u64>; // our value type
|
// our value type
|
||||||
|
type Value = StuffedPtr<Object, NanBoxStrategy, u64>;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let float: Value = StuffedPtr::new_extra(123.5);
|
let float: Value = StuffedPtr::new_other(123.5);
|
||||||
assert_eq!(float.copy_extra(), Some(123.5));
|
assert_eq!(float.copy_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.get_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.get_ptr().unwrap()) });
|
||||||
// `ptr` is a dangling pointer now!
|
// be careful, `ptr` is a dangling pointer now!
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
200
src/lib.rs
200
src/lib.rs
|
|
@ -13,10 +13,10 @@
|
||||||
//!
|
//!
|
||||||
//! [`StuffedPtr`] is the main type of this crate. You it's a type whose size depends on the
|
//! [`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
|
//! choice of [`Backend`] (defaults to `usize`, `u64` and `u128` are also possible). It can store a
|
||||||
//! pointer or some extra data.
|
//! pointer or some `other` data.
|
||||||
//!
|
//!
|
||||||
//! You can choose any arbitrary bitstuffing depending on the [`StuffingStrategy`], an unsafe trait that governs
|
//! 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,
|
//! 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.
|
//! it's a lot safer than doing everything by hand.
|
||||||
//!
|
//!
|
||||||
//! # Example: NaN-Boxing
|
//! # Example: NaN-Boxing
|
||||||
|
|
@ -35,21 +35,23 @@
|
||||||
//! // Create a unit struct for our strategy
|
//! // Create a unit struct for our strategy
|
||||||
//! struct NanBoxStrategy;
|
//! struct NanBoxStrategy;
|
||||||
//!
|
//!
|
||||||
//! const QNAN: u64 = 0x7ffc000000000000; // implementation detail of NaN boxing, a quiet NaN mask
|
//! // implementation detail of NaN boxing, a quiet NaN mask
|
||||||
//! const SIGN_BIT: u64 = 0x8000000000000000; // implementation detail of NaN boxing, the sign bit of an f64
|
//! 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 {
|
//! unsafe impl StuffingStrategy<u64> for NanBoxStrategy {
|
||||||
//! type Extra = f64;
|
//! type Other = f64;
|
||||||
//!
|
//!
|
||||||
//! fn is_extra(data: u64) -> bool {
|
//! fn is_other(data: u64) -> bool {
|
||||||
//! (data & QNAN) != QNAN
|
//! (data & QNAN) != QNAN
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn stuff_extra(inner: Self::Extra) -> 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_extra(data: u64) -> Self::Extra {
|
//! unsafe fn extract_other(data: u64) -> Self::Other {
|
||||||
//! std::mem::transmute(data) // both are 64 bit POD's
|
//! std::mem::transmute(data) // both are 64 bit POD's
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
|
|
@ -64,12 +66,14 @@
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! type Object = HashMap<String, u32>; // a very, very crude representation of an object
|
//! // a very, very crude representation of an object
|
||||||
|
//! type Object = HashMap<String, u32>;
|
||||||
//!
|
//!
|
||||||
//! type Value = StuffedPtr<Object, NanBoxStrategy, u64>; // our value type
|
//! // our value type
|
||||||
|
//! type Value = StuffedPtr<Object, NanBoxStrategy, u64>;
|
||||||
//!
|
//!
|
||||||
//! let float: Value = StuffedPtr::new_extra(123.5);
|
//! let float: Value = StuffedPtr::new_other(123.5);
|
||||||
//! assert_eq!(float.copy_extra(), Some(123.5));
|
//! assert_eq!(float.copy_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);
|
||||||
|
|
@ -80,7 +84,7 @@
|
||||||
//!
|
//!
|
||||||
//! drop(unsafe { Box::from_raw(ptr.get_ptr().unwrap()) });
|
//! drop(unsafe { Box::from_raw(ptr.get_ptr().unwrap()) });
|
||||||
//!
|
//!
|
||||||
//! // `ptr` is a dangling pointer now!
|
//! // be careful, `ptr` is a dangling pointer now!
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -101,7 +105,7 @@ use sptr::Strict;
|
||||||
|
|
||||||
pub use crate::{backend::Backend, strategy::StuffingStrategy};
|
pub use crate::{backend::Backend, strategy::StuffingStrategy};
|
||||||
|
|
||||||
/// A union of a pointer or some extra 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
|
||||||
/// by this crate. You can also provide your own [`Backend`] implementation
|
/// by this crate. You can also provide your own [`Backend`] implementation
|
||||||
///
|
///
|
||||||
|
|
@ -111,9 +115,9 @@ pub use crate::{backend::Backend, strategy::StuffingStrategy};
|
||||||
///
|
///
|
||||||
/// For a usage example, view the crate level documentation.
|
/// 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.
|
/// This pointer does *not* drop `other` data, [`StuffedPtr::into_other`] can be used if that is required.
|
||||||
///
|
///
|
||||||
/// `StuffedPtr` implements most traits like `Clone`, `PartialEq` or `Copy` if the extra type does.
|
/// `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)]
|
||||||
|
|
@ -134,20 +138,20 @@ where
|
||||||
StuffedPtr(B::set_ptr(ptr, stuffed), PhantomData)
|
StuffedPtr(B::set_ptr(ptr, stuffed), PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `StuffPtr` from extra data
|
/// Create a new `StuffPtr` from `other` data
|
||||||
pub fn new_extra(extra: S::Extra) -> Self {
|
pub fn new_other(other: S::Other) -> Self {
|
||||||
// this doesn't have any provenance, which is ok, since it's never a pointer anyways.
|
// this doesn't have any provenance, which is ok, since it's never a pointer anyways.
|
||||||
// if the user calls `set_ptr` it will use the new provenance from that ptr
|
// if the user calls `set_ptr` it will use the new provenance from that ptr
|
||||||
let ptr = core::ptr::null_mut();
|
let ptr = core::ptr::null_mut();
|
||||||
let extra = S::stuff_extra(extra);
|
let other = S::stuff_other(other);
|
||||||
StuffedPtr(B::set_ptr(ptr, extra), PhantomData)
|
StuffedPtr(B::set_ptr(ptr, other), PhantomData)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the pointer data, or `None` if it contains extra 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_extra().not() {
|
match self.is_other().not() {
|
||||||
true => {
|
true => {
|
||||||
// SAFETY: We have done a check that it's not extra
|
// SAFETY: We have done a check that it's not other
|
||||||
unsafe { Some(self.get_ptr_unchecked()) }
|
unsafe { Some(self.get_ptr_unchecked()) }
|
||||||
}
|
}
|
||||||
false => None,
|
false => None,
|
||||||
|
|
@ -158,103 +162,103 @@ where
|
||||||
/// contains pointer data.
|
/// contains pointer data.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `StuffedPtr` must contain pointer data and not extra data
|
/// `StuffedPtr` must contain pointer data and not `other` data
|
||||||
pub unsafe fn get_ptr_unchecked(&self) -> *mut T {
|
pub unsafe fn get_ptr_unchecked(&self) -> *mut T {
|
||||||
let (provenance, addr) = B::get_ptr(self.0);
|
let (provenance, addr) = B::get_ptr(self.0);
|
||||||
let addr = S::extract_ptr(addr);
|
let addr = S::extract_ptr(addr);
|
||||||
Strict::with_addr(provenance, addr)
|
Strict::with_addr(provenance, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get owned extra 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_extra(self) -> Option<S::Extra> {
|
pub fn into_other(self) -> Option<S::Other> {
|
||||||
match self.is_extra() {
|
match self.is_other() {
|
||||||
true => {
|
true => {
|
||||||
// SAFETY: We checked that it contains an extra above
|
// SAFETY: We checked that it contains an other above
|
||||||
unsafe { Some(self.into_extra_unchecked()) }
|
unsafe { Some(self.into_other_unchecked()) }
|
||||||
}
|
}
|
||||||
false => None,
|
false => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn this pointer into extra data.
|
/// Turn this pointer into `other` data.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `StuffedPtr` must contain extra data and not pointer
|
/// `StuffedPtr` must contain `other` data and not pointer
|
||||||
pub unsafe fn into_extra_unchecked(self) -> S::Extra {
|
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 extra = self.get_extra_unchecked();
|
let other = self.get_other_unchecked();
|
||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
extra
|
other
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get extra data from this, or `None` if it contains pointer data
|
/// Get `other` data from this, or `None` if it contains pointer data
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// The caller must guarantee that only ever on `Extra` exists if `Extra: !Copy`
|
/// The caller must guarantee that only ever on `Other` exists if `Other: !Copy`
|
||||||
pub unsafe fn get_extra(&self) -> Option<S::Extra> {
|
pub unsafe fn get_other(&self) -> Option<S::Other> {
|
||||||
match self.is_extra() {
|
match self.is_other() {
|
||||||
true => {
|
true => {
|
||||||
// SAFETY: We checked that it contains extra above, the caller guarantees the rest
|
// SAFETY: We checked that it contains other above, the caller guarantees the rest
|
||||||
Some(self.get_extra_unchecked())
|
Some(self.get_other_unchecked())
|
||||||
}
|
}
|
||||||
false => None,
|
false => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get extra data from this
|
/// Get `other` data from this
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Must contain extra data and not pointer data,
|
/// Must contain `other` data and not pointer data,
|
||||||
/// and the caller must guarantee that only ever on `Extra` exists if `Extra: !Copy`
|
/// and the caller must guarantee that only ever on `Other` exists if `Other: !Copy`
|
||||||
pub unsafe fn get_extra_unchecked(&self) -> S::Extra {
|
pub unsafe fn get_other_unchecked(&self) -> S::Other {
|
||||||
let data = self.addr();
|
let data = self.addr();
|
||||||
S::extract_extra(data)
|
S::extract_other(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addr(&self) -> B {
|
fn addr(&self) -> B {
|
||||||
B::get_int(self.0)
|
B::get_int(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_extra(&self) -> bool {
|
fn is_other(&self) -> bool {
|
||||||
S::is_extra(self.addr())
|
S::is_other(self.addr())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extra implementations if the extra type is `Copy`
|
/// Extra implementations if the `other` type is `Copy`
|
||||||
impl<T, S, B> StuffedPtr<T, S, B>
|
impl<T, S, B> StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Extra: Copy,
|
S::Other: Copy,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
/// Get extra 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_extra(&self) -> Option<S::Extra> {
|
pub fn copy_other(&self) -> Option<S::Other> {
|
||||||
// SAFETY: `S::Extra: Copy`
|
// SAFETY: `S::Other: Copy`
|
||||||
unsafe { self.get_extra() }
|
unsafe { self.get_other() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get extra data from this
|
/// Get `other` data from this
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Must contain extra data and not pointer data,
|
/// Must contain `other` data and not pointer data,
|
||||||
pub unsafe fn copy_extra_unchecked(&self) -> S::Extra {
|
pub unsafe fn copy_other_unchecked(&self) -> S::Other {
|
||||||
// SAFETY: `S::Extra: Copy`, and the caller guarantees that it's extra
|
// SAFETY: `S::Other: Copy`, and the caller guarantees that it's other
|
||||||
self.get_extra_unchecked()
|
self.get_other_unchecked()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>,
|
||||||
S::Extra: Debug,
|
S::Other: Debug,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
// SAFETY:
|
// SAFETY:
|
||||||
// If S::Extra: !Copy, we can't just copy it out and call it a day
|
// If S::Other: !Copy, we can't just copy it out and call it a day
|
||||||
// For example, if it's a Box, not forgetting it here would lead to a double free
|
// For example, if it's a Box, not forgetting it here would lead to a double free
|
||||||
// So we just format it and forget it afterwards
|
// So we just format it and forget it afterwards
|
||||||
if let Some(extra) = unsafe { self.get_extra() } {
|
if let Some(other) = unsafe { self.get_other() } {
|
||||||
f.debug_struct("StuffedPtr::Extra")
|
f.debug_struct("StuffedPtr::Other")
|
||||||
.field("extra", &extra)
|
.field("other", &other)
|
||||||
.finish()?;
|
.finish()?;
|
||||||
mem::forget(extra);
|
mem::forget(other);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: Checked above
|
// SAFETY: Checked above
|
||||||
|
|
@ -269,15 +273,15 @@ 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::Extra: Clone,
|
S::Other: Clone,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// SAFETY: We forget that `extra` ever existed after taking the reference and cloning it
|
// SAFETY: We forget that `other` ever existed after taking the reference and cloning it
|
||||||
if let Some(extra) = unsafe { self.get_extra() } {
|
if let Some(other) = unsafe { self.get_other() } {
|
||||||
let cloned_extra = extra.clone();
|
let cloned_other = other.clone();
|
||||||
mem::forget(extra);
|
mem::forget(other);
|
||||||
Self::new_extra(cloned_extra)
|
Self::new_other(cloned_other)
|
||||||
} else {
|
} else {
|
||||||
// just copy the pointer
|
// just copy the pointer
|
||||||
StuffedPtr(self.0, PhantomData)
|
StuffedPtr(self.0, PhantomData)
|
||||||
|
|
@ -288,7 +292,7 @@ where
|
||||||
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::Extra: Copy,
|
S::Other: Copy,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -296,17 +300,17 @@ where
|
||||||
impl<T, S, B> PartialEq for StuffedPtr<T, S, B>
|
impl<T, S, B> PartialEq for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Extra: PartialEq,
|
S::Other: PartialEq,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
// SAFETY: We forget them after
|
// SAFETY: We forget them after
|
||||||
let extras = unsafe { (self.get_extra(), other.get_extra()) };
|
let others = unsafe { (self.get_other(), other.get_other()) };
|
||||||
|
|
||||||
let eq = match &extras {
|
let eq = match &others {
|
||||||
(Some(extra1), Some(extra2)) => extra1.eq(extra2),
|
(Some(other1), Some(other2)) => other1.eq(other2),
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
// SAFETY: `get_extra` returned `None`, so it must be a ptr
|
// SAFETY: `get_other` returned `None`, so it must be a ptr
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr1 = self.get_ptr_unchecked();
|
let ptr1 = self.get_ptr_unchecked();
|
||||||
let ptr2 = self.get_ptr_unchecked();
|
let ptr2 = self.get_ptr_unchecked();
|
||||||
|
|
@ -316,7 +320,7 @@ where
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
mem::forget(extras);
|
mem::forget(others);
|
||||||
|
|
||||||
eq
|
eq
|
||||||
}
|
}
|
||||||
|
|
@ -325,7 +329,7 @@ where
|
||||||
impl<T, S, B> Eq for StuffedPtr<T, S, B>
|
impl<T, S, B> Eq for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Extra: PartialEq + Eq,
|
S::Other: PartialEq + Eq,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -333,14 +337,14 @@ where
|
||||||
impl<T, S, B> Hash for StuffedPtr<T, S, B>
|
impl<T, S, B> Hash for StuffedPtr<T, S, B>
|
||||||
where
|
where
|
||||||
S: StuffingStrategy<B>,
|
S: StuffingStrategy<B>,
|
||||||
S::Extra: Hash,
|
S::Other: Hash,
|
||||||
B: Backend<T>,
|
B: Backend<T>,
|
||||||
{
|
{
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
// SAFETY: We forget that `extra` ever existed after taking the reference and cloning it
|
// SAFETY: We forget that `other` ever existed after taking the reference and cloning it
|
||||||
if let Some(extra) = unsafe { self.get_extra() } {
|
if let Some(other) = unsafe { self.get_other() } {
|
||||||
extra.hash(state);
|
other.hash(state);
|
||||||
mem::forget(extra);
|
mem::forget(other);
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: Checked above
|
// SAFETY: Checked above
|
||||||
let ptr = unsafe { self.get_ptr_unchecked() };
|
let ptr = unsafe { self.get_ptr_unchecked() };
|
||||||
|
|
@ -364,7 +368,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
// note: the tests mostly use the `PanicsInDrop` type and strategy, to make sure that no
|
// note: the tests mostly use the `PanicsInDrop` type and strategy, to make sure that no
|
||||||
// extra is ever dropped accidentally.
|
// `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
|
||||||
|
|
@ -378,7 +382,7 @@ mod tests {
|
||||||
($backend:ident) => {
|
($backend:ident) => {
|
||||||
paste! {
|
paste! {
|
||||||
#[test]
|
#[test]
|
||||||
fn [<set_get_ptr_no_extra__ $backend>]() {
|
fn [<set_get_ptr_no_other__ $backend>]() {
|
||||||
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);
|
||||||
|
|
@ -390,10 +394,10 @@ mod tests {
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn [<get_extra__ $backend>]() {
|
fn [<get_other__ $backend>]() {
|
||||||
let stuffed_ptr: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_extra(EmptyInMax);
|
let stuffed_ptr: StuffedPtr<(), EmptyInMax, $backend> = StuffedPtr::new_other(EmptyInMax);
|
||||||
assert!(stuffed_ptr.is_extra());
|
assert!(stuffed_ptr.is_other());
|
||||||
assert!(matches!(stuffed_ptr.copy_extra(), Some(EmptyInMax)));
|
assert!(matches!(stuffed_ptr.copy_other(), Some(EmptyInMax)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -404,11 +408,11 @@ mod tests {
|
||||||
|
|
||||||
drop(unsafe { Box::from_raw(stuffed_ptr.get_ptr().unwrap()) });
|
drop(unsafe { Box::from_raw(stuffed_ptr.get_ptr().unwrap()) });
|
||||||
|
|
||||||
let extra = HasDebug;
|
let other = HasDebug;
|
||||||
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_extra(extra);
|
let stuffed_ptr: StuffedPtr<i32, HasDebug, $backend> = StuffedPtr::new_other(other);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{stuffed_ptr:?}"),
|
format!("{stuffed_ptr:?}"),
|
||||||
"StuffedPtr::Extra { extra: hello! }"
|
"StuffedPtr::Other { other: hello! }"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -419,7 +423,7 @@ mod tests {
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
let _ = stuffed_ptr1.clone();
|
let _ = stuffed_ptr1.clone();
|
||||||
|
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_extra(PanicsInDrop);
|
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
||||||
let stuffed_ptr2 = stuffed_ptr1.clone();
|
let stuffed_ptr2 = stuffed_ptr1.clone();
|
||||||
|
|
||||||
mem::forget((stuffed_ptr1, stuffed_ptr2));
|
mem::forget((stuffed_ptr1, stuffed_ptr2));
|
||||||
|
|
@ -436,7 +440,7 @@ mod tests {
|
||||||
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<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_extra(PanicsInDrop);
|
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
||||||
|
|
||||||
assert_ne!(stuffed_ptr1, stuffed_ptr2);
|
assert_ne!(stuffed_ptr1, stuffed_ptr2);
|
||||||
mem::forget(stuffed_ptr2);
|
mem::forget(stuffed_ptr2);
|
||||||
|
|
@ -444,7 +448,7 @@ mod tests {
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn [<dont_drop_extra_when_pointer__ $backend>]() {
|
fn [<dont_drop_other_when_pointer__ $backend>]() {
|
||||||
let mut unit = ();
|
let mut unit = ();
|
||||||
let stuffed_ptr: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
let stuffed_ptr: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_ptr(&mut unit);
|
||||||
// the panicking drop needs not to be called here!
|
// the panicking drop needs not to be called here!
|
||||||
|
|
@ -454,10 +458,10 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn [<some_traits_dont_drop__ $backend>]() {
|
fn [<some_traits_dont_drop__ $backend>]() {
|
||||||
// make sure that extra is never dropped twice
|
// make sure that other is never dropped twice
|
||||||
|
|
||||||
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_extra(PanicsInDrop);
|
let stuffed_ptr1: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
||||||
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_extra(PanicsInDrop);
|
let stuffed_ptr2: StuffedPtr<(), PanicsInDrop, $backend> = StuffedPtr::new_other(PanicsInDrop);
|
||||||
|
|
||||||
// PartialEq
|
// PartialEq
|
||||||
assert_eq!(stuffed_ptr1, stuffed_ptr2);
|
assert_eq!(stuffed_ptr1, stuffed_ptr2);
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,38 @@
|
||||||
/// A trait that describes how to stuff extras and pointers into the pointer sized object.
|
/// A trait that describes how to stuff others and pointers into the pointer sized object.
|
||||||
///
|
///
|
||||||
/// This trait is what a user of this crate is expected to implement to use the crate for their own
|
/// This trait is what a user of this crate is expected to implement to use the crate for their own
|
||||||
/// pointer stuffing. It's usually implemented on ZSTs that only serve as stuffing strategies, but
|
/// pointer stuffing. It's usually implemented on ZSTs that only serve as stuffing strategies, but
|
||||||
/// it's also completely possible to implement it on the type in [`StuffingStrategy::Extra`] directly
|
/// it's also completely possible to implement it on the type in [`StuffingStrategy::Other`] directly
|
||||||
/// if possible.
|
/// if possible.
|
||||||
///
|
///
|
||||||
/// The generic parameter `B` stands for the [`Backend`](`crate::Backend`) used by the strategy.
|
/// The generic parameter `B` stands for the [`Backend`](`crate::Backend`) used by the strategy.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// If [`StuffingStrategy::is_extra`] returns true for a value, then
|
/// If [`StuffingStrategy::is_other`] returns true for a value, then
|
||||||
/// [`StuffingStrategy::extract_extra`] *must* return a valid `Extra` for that same value.
|
/// [`StuffingStrategy::extract_other`] *must* return a valid `Other` for that same value.
|
||||||
///
|
///
|
||||||
/// [`StuffingStrategy::stuff_extra`] *must* consume `inner` and make sure that it's not dropped
|
/// [`StuffingStrategy::stuff_other`] *must* consume `inner` and make sure that it's not dropped
|
||||||
/// if it isn't `Copy`.
|
/// 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 extra.
|
/// The type of the other.
|
||||||
type Extra;
|
type Other;
|
||||||
|
|
||||||
/// Checks whether the `StufferPtr` data value contains an extra value. The result of this
|
/// Checks whether the `StufferPtr` data value contains an other value. The result of this
|
||||||
/// function can be trusted.
|
/// function can be trusted.
|
||||||
fn is_extra(data: B) -> bool;
|
fn is_other(data: B) -> bool;
|
||||||
|
|
||||||
/// Stuff extra 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_extra(inner: Self::Extra) -> B;
|
fn stuff_other(inner: Self::Other) -> B;
|
||||||
|
|
||||||
/// Extract extra data from the data.
|
/// Extract other data from the data.
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// `data` must contain data created by [`StuffingStrategy::stuff_extra`].
|
/// `data` must contain data created by [`StuffingStrategy::stuff_other`].
|
||||||
unsafe fn extract_extra(data: B) -> Self::Extra;
|
unsafe fn extract_other(data: B) -> Self::Other;
|
||||||
|
|
||||||
/// Stuff a pointer address into the pointer sized integer.
|
/// Stuff a pointer address into the pointer sized integer.
|
||||||
///
|
///
|
||||||
|
|
@ -49,17 +49,17 @@ pub unsafe trait StuffingStrategy<B> {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<usize> for () {
|
unsafe impl StuffingStrategy<usize> for () {
|
||||||
type Extra = ();
|
type Other = ();
|
||||||
|
|
||||||
fn is_extra(_data: usize) -> bool {
|
fn is_other(_data: usize) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_extra(_inner: Self::Extra) -> usize {
|
fn stuff_other(_inner: Self::Other) -> usize {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: usize) -> Self::Extra {}
|
unsafe fn extract_other(_data: usize) -> Self::Other {}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> usize {
|
fn stuff_ptr(addr: usize) -> usize {
|
||||||
addr
|
addr
|
||||||
|
|
@ -71,17 +71,17 @@ unsafe impl StuffingStrategy<usize> for () {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u64> for () {
|
unsafe impl StuffingStrategy<u64> for () {
|
||||||
type Extra = ();
|
type Other = ();
|
||||||
|
|
||||||
fn is_extra(_data: u64) -> bool {
|
fn is_other(_data: u64) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_extra(_inner: Self::Extra) -> u64 {
|
fn stuff_other(_inner: Self::Other) -> u64 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: u64) -> Self::Extra {}
|
unsafe fn extract_other(_data: u64) -> Self::Other {}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> u64 {
|
fn stuff_ptr(addr: usize) -> u64 {
|
||||||
addr as u64
|
addr as u64
|
||||||
|
|
@ -93,17 +93,17 @@ unsafe impl StuffingStrategy<u64> for () {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u128> for () {
|
unsafe impl StuffingStrategy<u128> for () {
|
||||||
type Extra = ();
|
type Other = ();
|
||||||
|
|
||||||
fn is_extra(_data: u128) -> bool {
|
fn is_other(_data: u128) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stuff_extra(_inner: Self::Extra) -> u128 {
|
fn stuff_other(_inner: Self::Other) -> u128 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: u128) -> Self::Extra {}
|
unsafe fn extract_other(_data: u128) -> Self::Other {}
|
||||||
|
|
||||||
fn stuff_ptr(addr: usize) -> u128 {
|
fn stuff_ptr(addr: usize) -> u128 {
|
||||||
addr as u128
|
addr as u128
|
||||||
|
|
@ -124,19 +124,19 @@ pub(crate) mod test_strategies {
|
||||||
($ty:ident) => {
|
($ty:ident) => {
|
||||||
// this one lives in usize::MAX
|
// this one lives in usize::MAX
|
||||||
unsafe impl StuffingStrategy<usize> for $ty {
|
unsafe impl StuffingStrategy<usize> for $ty {
|
||||||
type Extra = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_extra(data: usize) -> bool {
|
fn is_other(data: usize) -> bool {
|
||||||
data == usize::MAX
|
data == usize::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_extra(inner: Self::Extra) -> usize {
|
fn stuff_other(inner: Self::Other) -> usize {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
usize::MAX
|
usize::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: usize) -> Self::Extra {
|
unsafe fn extract_other(_data: usize) -> Self::Other {
|
||||||
$ty
|
$ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,19 +149,19 @@ pub(crate) mod test_strategies {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe impl StuffingStrategy<u64> for $ty {
|
unsafe impl StuffingStrategy<u64> for $ty {
|
||||||
type Extra = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_extra(data: u64) -> bool {
|
fn is_other(data: u64) -> bool {
|
||||||
data == u64::MAX
|
data == u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_extra(inner: Self::Extra) -> u64 {
|
fn stuff_other(inner: Self::Other) -> u64 {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
u64::MAX
|
u64::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: u64) -> Self::Extra {
|
unsafe fn extract_other(_data: u64) -> Self::Other {
|
||||||
$ty
|
$ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,19 +175,19 @@ pub(crate) mod test_strategies {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl StuffingStrategy<u128> for $ty {
|
unsafe impl StuffingStrategy<u128> for $ty {
|
||||||
type Extra = Self;
|
type Other = Self;
|
||||||
|
|
||||||
fn is_extra(data: u128) -> bool {
|
fn is_other(data: u128) -> bool {
|
||||||
data == u128::MAX
|
data == u128::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::forget_copy)]
|
#[allow(clippy::forget_copy)]
|
||||||
fn stuff_extra(inner: Self::Extra) -> u128 {
|
fn stuff_other(inner: Self::Other) -> u128 {
|
||||||
core::mem::forget(inner);
|
core::mem::forget(inner);
|
||||||
u128::MAX
|
u128::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn extract_extra(_data: u128) -> Self::Extra {
|
unsafe fn extract_other(_data: u128) -> Self::Other {
|
||||||
$ty
|
$ty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue