use super::{Pointer, Tag};
use crate::stable_hasher::{HashStable, StableHasher};
use std::fmt;
use std::marker::PhantomData;
use std::num::NonZeroUsize;
pub struct CopyTaggedPtr<P, T, const COMPARE_PACKED: bool>
where
P: Pointer,
T: Tag,
{
packed: NonZeroUsize,
data: PhantomData<(P, T)>,
}
impl<P, T, const COMPARE_PACKED: bool> Copy for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer,
T: Tag,
P: Copy,
{
}
impl<P, T, const COMPARE_PACKED: bool> Clone for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer,
T: Tag,
P: Copy,
{
fn clone(&self) -> Self {
*self
}
}
impl<P, T, const COMPARE_PACKED: bool> CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer,
T: Tag,
{
const TAG_BIT_SHIFT: usize = usize::BITS as usize - T::BITS;
const ASSERTION: () = {
assert!(T::BITS <= P::BITS);
assert!(std::mem::size_of::<&P::Target>() == std::mem::size_of::<usize>());
};
pub fn new(pointer: P, tag: T) -> Self {
let () = Self::ASSERTION;
let packed_tag = tag.into_usize() << Self::TAG_BIT_SHIFT;
Self {
packed: unsafe {
NonZeroUsize::new_unchecked((P::into_usize(pointer) >> T::BITS) | packed_tag)
},
data: PhantomData,
}
}
pub(super) fn pointer_raw(&self) -> usize {
self.packed.get() << T::BITS
}
pub fn pointer(self) -> P
where
P: Copy,
{
unsafe { P::from_usize(self.pointer_raw()) }
}
pub fn pointer_ref(&self) -> &P::Target {
unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
}
pub fn pointer_mut(&mut self) -> &mut P::Target
where
P: std::ops::DerefMut,
{
unsafe { std::mem::transmute_copy(&self.pointer_raw()) }
}
#[inline]
pub fn tag(&self) -> T {
unsafe { T::from_usize(self.packed.get() >> Self::TAG_BIT_SHIFT) }
}
#[inline]
pub fn set_tag(&mut self, tag: T) {
let mut packed = self.packed.get();
let new_tag = T::into_usize(tag) << Self::TAG_BIT_SHIFT;
let tag_mask = (1 << T::BITS) - 1;
packed &= !(tag_mask << Self::TAG_BIT_SHIFT);
packed |= new_tag;
self.packed = unsafe { NonZeroUsize::new_unchecked(packed) };
}
}
impl<P, T, const COMPARE_PACKED: bool> std::ops::Deref for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer,
T: Tag,
{
type Target = P::Target;
fn deref(&self) -> &Self::Target {
self.pointer_ref()
}
}
impl<P, T, const COMPARE_PACKED: bool> std::ops::DerefMut for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer + std::ops::DerefMut,
T: Tag,
{
fn deref_mut(&mut self) -> &mut Self::Target {
self.pointer_mut()
}
}
impl<P, T, const COMPARE_PACKED: bool> fmt::Debug for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer,
P::Target: fmt::Debug,
T: Tag + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CopyTaggedPtr")
.field("pointer", &self.pointer_ref())
.field("tag", &self.tag())
.finish()
}
}
impl<P, T> PartialEq for CopyTaggedPtr<P, T, true>
where
P: Pointer,
T: Tag,
{
fn eq(&self, other: &Self) -> bool {
self.packed == other.packed
}
}
impl<P, T> Eq for CopyTaggedPtr<P, T, true>
where
P: Pointer,
T: Tag,
{
}
impl<P, T> std::hash::Hash for CopyTaggedPtr<P, T, true>
where
P: Pointer,
T: Tag,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.packed.hash(state);
}
}
impl<P, T, HCX, const COMPARE_PACKED: bool> HashStable<HCX> for CopyTaggedPtr<P, T, COMPARE_PACKED>
where
P: Pointer + HashStable<HCX>,
T: Tag + HashStable<HCX>,
{
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
unsafe {
Pointer::with_ref(self.pointer_raw(), |p: &P| p.hash_stable(hcx, hasher));
}
self.tag().hash_stable(hcx, hasher);
}
}