use std::marker::PhantomData;
pub const ADDR_MASK: u64 = (1u64 << 53) - 1;
pub const SHAPE_SHIFT: u32 = 53;
pub const SHAPE_MASK: u64 = 0b111 << SHAPE_SHIFT;
pub const TYPE_SHIFT: u32 = 56;
pub const TYPE_MASK: u64 = 0xFFu64 << TYPE_SHIFT;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum LayoutShape {
Scalar = 0,
FixedArray = 1,
RaggedArray = 2,
Tree = 3,
Graph = 4,
HashBucket = 5,
Sparse = 6,
UserDefined = 7,
}
impl LayoutShape {
pub fn from_bits(b: u8) -> Self {
match b & 0b111 {
0 => Self::Scalar,
1 => Self::FixedArray,
2 => Self::RaggedArray,
3 => Self::Tree,
4 => Self::Graph,
5 => Self::HashBucket,
6 => Self::Sparse,
_ => Self::UserDefined,
}
}
}
#[repr(transparent)]
pub struct SelfDescPointer<T> {
raw: u64,
_phantom: PhantomData<*const T>,
}
unsafe impl<T: Send> Send for SelfDescPointer<T> {}
unsafe impl<T: Sync> Sync for SelfDescPointer<T> {}
impl<T> SelfDescPointer<T> {
pub const SIGNATURE: subetha_core::AxisMask = subetha_core::AxisMask::from_axes(
&[subetha_core::Axis::TypeTag],
);
pub unsafe fn from_raw(target: *const T, type_id: u8, shape: LayoutShape) -> Self {
let addr = target as u64;
debug_assert!(
addr & !ADDR_MASK == 0,
"address {addr:#x} has bits set above 53-bit boundary"
);
let raw = ((type_id as u64) << TYPE_SHIFT)
| ((shape as u64) << SHAPE_SHIFT)
| (addr & ADDR_MASK);
Self { raw, _phantom: PhantomData }
}
#[inline]
pub fn as_raw(&self) -> *const T {
(self.raw & ADDR_MASK) as *const T
}
#[inline]
pub const fn type_id(&self) -> u8 {
(self.raw >> TYPE_SHIFT) as u8
}
#[inline]
pub fn layout_shape(&self) -> LayoutShape {
LayoutShape::from_bits(((self.raw >> SHAPE_SHIFT) & 0b111) as u8)
}
#[inline]
pub const fn raw(&self) -> u64 { self.raw }
pub fn set_type_id(&mut self, new_id: u8) {
self.raw = (self.raw & !TYPE_MASK) | ((new_id as u64) << TYPE_SHIFT);
}
pub fn set_layout_shape(&mut self, new_shape: LayoutShape) {
self.raw = (self.raw & !SHAPE_MASK) | ((new_shape as u64) << SHAPE_SHIFT);
}
}
impl<T> Clone for SelfDescPointer<T> {
fn clone(&self) -> Self { *self }
}
impl<T> Copy for SelfDescPointer<T> {}
impl<T> std::fmt::Debug for SelfDescPointer<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SelfDescPointer {{ addr: {:#x}, type_id: {}, shape: {:?} }}",
self.raw & ADDR_MASK, self.type_id(), self.layout_shape())
}
}
impl<T> PartialEq for SelfDescPointer<T> {
fn eq(&self, other: &Self) -> bool { self.raw == other.raw }
}
impl<T> Eq for SelfDescPointer<T> {}
impl<T> std::hash::Hash for SelfDescPointer<T> {
fn hash<H: std::hash::Hasher>(&self, s: &mut H) { self.raw.hash(s); }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layout_is_8_bytes() {
assert_eq!(std::mem::size_of::<SelfDescPointer<u64>>(), 8);
assert_eq!(std::mem::align_of::<SelfDescPointer<u64>>(), 8);
}
#[test]
fn type_id_round_trips() {
let p = unsafe {
SelfDescPointer::<u64>::from_raw(0x1FFF_FFFF as *const u64, 42, LayoutShape::Scalar)
};
assert_eq!(p.type_id(), 42);
assert_eq!(p.layout_shape(), LayoutShape::Scalar);
}
#[test]
fn address_round_trips_under_53_bit_mask() {
let addr: *const u64 = 0x0001_2345_6789_ABCD as *const u64;
let addr_u64 = addr as u64;
assert_eq!(addr_u64 & !ADDR_MASK, 0, "test address fits in 53 bits");
let p = unsafe { SelfDescPointer::from_raw(addr, 7, LayoutShape::Tree) };
assert_eq!(p.as_raw(), addr);
assert_eq!(p.type_id(), 7);
assert_eq!(p.layout_shape(), LayoutShape::Tree);
}
#[test]
fn each_layout_shape_round_trips() {
for shape in [
LayoutShape::Scalar,
LayoutShape::FixedArray,
LayoutShape::RaggedArray,
LayoutShape::Tree,
LayoutShape::Graph,
LayoutShape::HashBucket,
LayoutShape::Sparse,
LayoutShape::UserDefined,
] {
let p = unsafe {
SelfDescPointer::<u64>::from_raw(std::ptr::dangling::<u64>(), 0, shape)
};
assert_eq!(p.layout_shape(), shape, "shape {shape:?} must round-trip");
}
}
#[test]
fn set_type_id_preserves_address_and_shape() {
let mut p = unsafe {
SelfDescPointer::<u64>::from_raw(0xCAFE as *const u64, 10, LayoutShape::HashBucket)
};
p.set_type_id(99);
assert_eq!(p.type_id(), 99);
assert_eq!(p.layout_shape(), LayoutShape::HashBucket);
assert_eq!(p.as_raw() as u64, 0xCAFE);
}
#[test]
fn set_layout_preserves_address_and_type() {
let mut p = unsafe {
SelfDescPointer::<u64>::from_raw(0xBEEF as *const u64, 33, LayoutShape::Scalar)
};
p.set_layout_shape(LayoutShape::Graph);
assert_eq!(p.layout_shape(), LayoutShape::Graph);
assert_eq!(p.type_id(), 33);
assert_eq!(p.as_raw() as u64, 0xBEEF);
}
#[test]
fn heterogeneous_dispatch_without_vtable() {
let pointers = vec![
unsafe { SelfDescPointer::<u8>::from_raw(std::ptr::dangling::<u8>(), 1, LayoutShape::Scalar) },
unsafe { SelfDescPointer::<u8>::from_raw(0x2 as *const u8, 2, LayoutShape::FixedArray) },
unsafe { SelfDescPointer::<u8>::from_raw(0x3 as *const u8, 1, LayoutShape::Scalar) },
unsafe { SelfDescPointer::<u8>::from_raw(0x4 as *const u8, 3, LayoutShape::Tree) },
];
let mut t1 = 0;
let mut t2 = 0;
let mut t3 = 0;
for p in &pointers {
match p.type_id() {
1 => t1 += 1,
2 => t2 += 1,
3 => t3 += 1,
_ => {}
}
}
assert_eq!((t1, t2, t3), (2, 1, 1));
}
}