use core::{
marker::PhantomData,
num::{NonZeroI8, NonZeroI16, NonZeroI32, NonZeroU8, NonZeroU16, NonZeroU32},
ptr::NonNull,
};
pub unsafe trait AsPackedValue: Sized {
const MIN_BIT_WIDTH: usize;
fn encode(zelf: Self) -> TruncatedU64<Self>;
unsafe fn decode(raw: TruncatedU64<Self>) -> Self;
fn is_rt_safe() -> bool {
true
}
}
#[repr(transparent)]
#[derive(Debug, PartialEq, Eq)]
pub struct TruncatedU64<T> {
v: u64,
_phantom: PhantomData<T>,
}
impl<T> Clone for TruncatedU64<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for TruncatedU64<T> {}
impl<T> TruncatedU64<T> {
pub fn read(&self) -> u64 {
self.v
}
}
impl<T: AsPackedValue> TruncatedU64<T> {
pub fn new(mut value: u64) -> Self {
if T::MIN_BIT_WIDTH < 64 {
value = unpack!((value): T::MIN_BIT_WIDTH).1;
}
Self {
v: value,
_phantom: PhantomData,
}
}
}
macro_rules! atomic_encode_primitive {
($type:ty) => {
unsafe impl $crate::core::AsPackedValue for $type {
const MIN_BIT_WIDTH: usize = size_of::<$type>() * 8;
fn encode(zelf: Self) -> $crate::core::TruncatedU64<Self> {
$crate::core::TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: $crate::core::TruncatedU64<Self>) -> Self {
(raw.read()) as Self
}
}
};
}
macro_rules! atomic_encode_non_zero_primitive {
($type:ty, $raw:ty) => {
unsafe impl $crate::core::AsPackedValue for $type {
const MIN_BIT_WIDTH: usize = size_of::<$type>() * 8;
fn encode(zelf: Self) -> $crate::core::TruncatedU64<Self> {
$crate::core::TruncatedU64::new(zelf.get() as u64)
}
unsafe fn decode(raw: $crate::core::TruncatedU64<Self>) -> Self {
Self::new(raw.read() as $raw)
.expect("trying to construct a NonZero from a zero value")
}
}
};
}
atomic_encode_primitive!(u32);
atomic_encode_primitive!(u16);
atomic_encode_primitive!(u8);
atomic_encode_primitive!(i32);
atomic_encode_primitive!(i16);
atomic_encode_primitive!(i8);
atomic_encode_non_zero_primitive!(NonZeroU32, u32);
atomic_encode_non_zero_primitive!(NonZeroU16, u16);
atomic_encode_non_zero_primitive!(NonZeroU8, u8);
atomic_encode_non_zero_primitive!(NonZeroI32, i32);
atomic_encode_non_zero_primitive!(NonZeroI16, i16);
atomic_encode_non_zero_primitive!(NonZeroI8, i8);
unsafe impl AsPackedValue for () {
const MIN_BIT_WIDTH: usize = 0;
fn encode(_zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(0)
}
unsafe fn decode(_raw: TruncatedU64<Self>) -> Self {}
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
mod x86_64 {
use super::*;
fn assert_ptr_safety() {
let dummy = 42;
let raw = &dummy as *const i32;
let addr = raw as u64;
let top_16 = addr >> 48;
let bit_47 = (addr >> 47) & 1;
assert!(
(bit_47 == 0 && top_16 == 0) || (bit_47 == 1 && top_16 == 0xFFFF),
"Pointer {:p} exceeds 48-bit address space! AsPackedValue is unsafe here. Consider using a PooledQueue or a Tagged128 Slot.",
raw
);
}
unsafe impl<T> AsPackedValue for *const T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
crate::utils::sign_extend(raw.read()) as *const T
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for *mut T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
crate::utils::sign_extend(raw.read()) as *mut T
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for NonNull<T>
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf.as_ptr() as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
Self::new(crate::utils::sign_extend(raw.read()) as *mut T)
.expect("tried to recosntruct a NonNull from 0")
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for &'static T {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *const T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &*(crate::utils::sign_extend(raw.read()) as *const T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for &'static mut T {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *mut T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &mut *(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
#[cfg(any(feature = "alloc", test))]
mod alloc_ {
use alloc::{
boxed::Box,
rc::{self, Rc},
sync::{self, Arc},
};
use super::*;
unsafe impl<T> AsPackedValue for Box<T> {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Box::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Box::from_raw(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for Rc<T> {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Rc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Rc::from_raw(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for Arc<T> {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Arc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Arc::from_raw(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for rc::Weak<T> {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = rc::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { rc::Weak::from_raw(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
unsafe impl<T> AsPackedValue for sync::Weak<T> {
const MIN_BIT_WIDTH: usize = 48;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = sync::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { sync::Weak::from_raw(crate::utils::sign_extend(raw.read()) as *mut T) }
}
fn is_rt_safe() -> bool {
assert_ptr_safety();
true
}
}
}
}
#[cfg(all(not(target_arch = "x86_64"), target_pointer_width = "64"))]
mod full_bit64 {
use super::*;
unsafe impl<T> AsPackedValue for *const T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
raw.read() as *const T
}
}
unsafe impl<T> AsPackedValue for *mut T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
raw.read() as *mut T
}
}
unsafe impl<T> AsPackedValue for NonNull<T>
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf.as_ptr() as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
Self::new(raw.read() as *mut T).expect("tried to recosntruct a NonNull from 0")
}
}
unsafe impl<T> AsPackedValue for &'static T {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *const T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &*(raw.read() as *const T) }
}
}
unsafe impl<T> AsPackedValue for &'static mut T {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *mut T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &mut *(raw.read() as *mut T) }
}
}
#[cfg(any(feature = "alloc", test))]
mod alloc_ {
use alloc::{
boxed::Box,
rc::{self, Rc},
sync::{self, Arc},
};
use super::*;
unsafe impl<T> AsPackedValue for Box<T> {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(Box::into_raw(zelf) as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Box::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for Rc<T> {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Rc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Rc::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for Arc<T> {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Arc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Arc::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for rc::Weak<T> {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = rc::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { rc::Weak::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for sync::Weak<T> {
const MIN_BIT_WIDTH: usize = 64;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = sync::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { sync::Weak::from_raw(raw.read() as *mut T) }
}
}
}
}
#[cfg(not(target_pointer_width = "64"))]
mod bit32 {
use core::num::NonZeroUsize;
use super::*;
unsafe impl<T> AsPackedValue for *const T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
raw.read() as *const T
}
}
unsafe impl<T> AsPackedValue for *mut T
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
raw.read() as *mut T
}
}
unsafe impl<T> AsPackedValue for NonNull<T>
where
T: Sized,
{
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf.as_ptr() as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
Self::new(raw.read() as *mut T)
.expect("Constructing a NonNull form a null ptr wich was not obtained from encode")
}
}
unsafe impl<T> AsPackedValue for &'static T {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *const T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &*(raw.read() as *const T) }
}
}
unsafe impl<T> AsPackedValue for &'static mut T {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(zelf as *mut T as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { &mut *(raw.read() as *mut T) }
}
}
#[cfg(feature = "alloc")]
mod alloc_ {
use alloc::{
boxed::Box,
rc::{self, Rc},
sync::{self, Arc},
};
use super::*;
unsafe impl<T> AsPackedValue for Box<T> {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
TruncatedU64::new(Box::into_raw(zelf) as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Box::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for Rc<T> {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Rc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Rc::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for Arc<T> {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = Arc::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { Arc::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for rc::Weak<T> {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = rc::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { rc::Weak::from_raw(raw.read() as *mut T) }
}
}
unsafe impl<T> AsPackedValue for sync::Weak<T> {
const MIN_BIT_WIDTH: usize = size_of::<usize>() * 8;
fn encode(zelf: Self) -> TruncatedU64<Self> {
let raw = sync::Weak::into_raw(zelf);
TruncatedU64::new(raw as u64)
}
unsafe fn decode(raw: TruncatedU64<Self>) -> Self {
unsafe { sync::Weak::from_raw(raw.read() as *mut T) }
}
}
}
atomic_encode_primitive!(usize);
atomic_encode_non_zero_primitive!(NonZeroUsize, usize);
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! generate_test {
($name:ident: $constructor:expr, $type:ty, $deref:expr) => {
#[test]
fn $name() {
#[allow(dead_code)]
static VALUE: i32 = 42;
const WIDTH: usize = <$type as AsPackedValue>::MIN_BIT_WIDTH;
let ptr1 = $constructor;
let expected = $deref(&ptr1);
let mut encoded = AsPackedValue::encode(ptr1);
if WIDTH < 64 {
let packed_encoded = pack!((!0, encoded.read()): WIDTH);
encoded = TruncatedU64::new(packed_encoded);
}
let decoded = unsafe { AsPackedValue::decode(encoded) };
assert_eq!($deref(&decoded), expected);
}
};
($name:ident: $constructor:expr, $type:ty) => {
generate_test!($name: $constructor, $type, |x: &$type| x.clone());
};
}
generate_test!(raw: &VALUE as *const i32, *const i32);
generate_test!(raw_mut: &VALUE as *const i32 as *mut i32, *mut i32);
generate_test!(r#ref: &VALUE, &'static i32);
generate_test!(nonnull: NonNull::new(&VALUE as *const i32 as *mut i32).unwrap(), NonNull<i32>);
generate_test!(primitive_u32: 42, u32);
generate_test!(primitive_nonzero_u32: NonZeroU32::new(42).unwrap(), NonZeroU32);
generate_test!(unit: (), ());
#[cfg(feature = "alloc")]
mod alloc_ {
use alloc::{
boxed::Box,
rc::{self, Rc},
sync::{self, Arc},
};
use super::*;
generate_test!(r#box: Box::new(VALUE), Box<i32>);
generate_test!(r#arc: Arc::new(VALUE), Arc<i32>);
generate_test!(r#rc: Rc::new(VALUE), Rc<i32>);
generate_test!(weak_rc: Rc::downgrade(&Rc::new(VALUE)), rc::Weak<i32>, |x: &rc::Weak<i32>| x.as_ptr());
generate_test!(weak_arc: Arc::downgrade(&Arc::new(VALUE)), sync::Weak<i32>, |x: &sync::Weak<i32>| x.as_ptr());
}
}