use core::{
convert::TryFrom,
fmt::{Debug, Formatter, Pointer},
hash::Hash,
marker::PhantomData,
};
use crate::{config::PtrCfg, error::PackedPtrError, Packable, PackedPtr};
#[repr(transparent)]
pub struct TypedPackedPtr<T, C: PtrCfg, D: Packable>(PackedPtr<T, C>, PhantomData<D>);
impl<T, C: PtrCfg, D: Packable> TypedPackedPtr<T, C, D> {
pub fn new(ptr: *const T, data: D, cfg: C) -> Result<Self, PackedPtrError> {
if <D as Packable>::MAX_BITS > PackedPtr::<T, C>::bits() {
return Err(PackedPtrError::DataOverflow);
}
let data = data.pack();
let ptr = PackedPtr::new(ptr, data, cfg)?;
Ok(Self(ptr, PhantomData))
}
pub unsafe fn new_unchecked(ptr: *const T, data: D) -> Self {
let data = data.pack();
let ptr = PackedPtr::new_unchecked(ptr, data);
Self(ptr, PhantomData)
}
#[must_use]
pub fn ptr(self) -> *const T {
self.get().0
}
#[must_use]
pub fn data(self) -> D {
self.get().1
}
#[must_use]
pub fn get(self) -> (*const T, D) {
(self.0.ptr(), unsafe { D::unpack(self.0.data()) })
}
pub fn set_data(&mut self, data: D) {
*self = unsafe { Self::new_unchecked(self.ptr(), data) };
}
}
impl<T, C: PtrCfg, D: Packable> Clone for TypedPackedPtr<T, C, D> {
fn clone(&self) -> Self {
*self
}
}
impl<T, C: PtrCfg, D: Packable> Copy for TypedPackedPtr<T, C, D> {}
impl<T, C: PtrCfg, D: Packable + Debug> Debug for TypedPackedPtr<T, C, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("TypedPackedPtr")
.field(&self.ptr())
.field(&self.data())
.finish()
}
}
impl<T, C: PtrCfg, D: Packable> Pointer for TypedPackedPtr<T, C, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
Pointer::fmt(&self.ptr(), f)
}
}
impl<T, C: PtrCfg, D: Packable + PartialEq> PartialEq for TypedPackedPtr<T, C, D> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl<T, C: PtrCfg, D: Packable + Eq> Eq for TypedPackedPtr<T, C, D> {}
impl<T, C: PtrCfg, D: Packable + Hash> Hash for TypedPackedPtr<T, C, D> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.get().hash(state);
}
}
impl<T, C: PtrCfg, D: Packable + Default> TryFrom<*const T> for TypedPackedPtr<T, C, D> {
type Error = PackedPtrError;
fn try_from(value: *const T) -> Result<Self, Self::Error> {
Self::new(value, D::default(), C::default())
}
}
impl<T, C: PtrCfg, D: Packable> TryFrom<(*const T, D)> for TypedPackedPtr<T, C, D> {
type Error = PackedPtrError;
fn try_from(value: (*const T, D)) -> Result<Self, Self::Error> {
Self::new(value.0, value.1, C::default())
}
}
impl<T, C: PtrCfg, D: Packable> From<TypedPackedPtr<T, C, D>> for *const T {
fn from(value: TypedPackedPtr<T, C, D>) -> Self {
value.ptr()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::AlignOnly;
#[test]
fn round_trip() {
let data = 0xdead_beef_u32;
let packed = (true, false);
let ptr = TypedPackedPtr::new(&data, packed, AlignOnly).unwrap();
assert_eq!(data, unsafe { *ptr.ptr() });
assert_eq!(packed, ptr.data());
}
#[test]
fn new() {
let data = 0xdead_beef_u32;
let packed = 5u32;
let overflow = TypedPackedPtr::new(&data, packed, AlignOnly);
assert!(overflow.is_err());
assert!(matches!(
overflow.unwrap_err(),
PackedPtrError::DataOverflow
));
let packed = true;
let ok = TypedPackedPtr::new(&data, packed, AlignOnly);
assert!(ok.is_ok());
}
#[test]
fn array() {
let data = 0xdead_beef_u32;
let packed = [true, false];
let ptr = TypedPackedPtr::new(&data, packed, AlignOnly).unwrap();
assert_eq!(data, unsafe { *ptr.ptr() });
assert_eq!(packed, ptr.data());
}
#[test]
fn set_data() {
let data = 0xdead_beef_u32;
let packed = (true, false);
let mut ptr = TypedPackedPtr::new(&data, packed, AlignOnly).unwrap();
assert_eq!(data, unsafe { *ptr.ptr() });
assert_eq!(packed, ptr.data());
let new_packed = (false, true);
ptr.set_data(new_packed);
assert_eq!(data, unsafe { *ptr.ptr() });
assert_eq!(new_packed, ptr.data());
}
}