use super::*;
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Default)]
#[repr(transparent)]
#[must_use]
pub struct Port(pub u64);
bi_enum! {
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Tag {
Red = 0,
Var = 1,
Ref = 2,
Int = 3,
F32 = 4,
Op = 5,
Mat = 6,
Ctr = 7,
}
}
impl fmt::Debug for Port {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:016x?} ", self.0)?;
match *self {
Port::ERA => write!(f, "[ERA]"),
Port::FREE => write!(f, "[FREE]"),
Port::GONE => write!(f, "[GONE]"),
Port::LOCK => write!(f, "[LOCK]"),
_ => match self.tag() {
Int => write!(f, "[Int {}]", self.int()),
F32 => write!(f, "[F32 {:?}]", self.float()),
Var | Red | Mat => write!(f, "[{:?} {:?}]", self.tag(), self.addr()),
Op | Ctr | Ref => write!(f, "[{:?} {:?} {:?}]", self.tag(), self.lab(), self.addr()),
},
}
}
}
impl Port {
pub const ERA: Port = Port(Ref as _);
pub const FREE: Port = Port(0x8000_0000_0000_0000);
pub const LOCK: Port = Port(0xFFFF_FFFF_FFFF_FFF0);
pub const GONE: Port = Port(0xFFFF_FFFF_FFFF_FFFF);
#[inline(always)]
pub fn new(tag: Tag, lab: Lab, addr: Addr) -> Self {
Port(((lab as u64) << 48) | (addr.0 as u64) | (tag as u64))
}
#[inline(always)]
pub fn new_var(addr: Addr) -> Self {
Port::new(Var, 0, addr)
}
#[inline(always)]
pub fn new_int(val: i64) -> Self {
Port::new_num(Int, val as u64)
}
#[inline(always)]
pub fn new_float(val: f32) -> Self {
Port::new_num(F32, val.to_bits() as u64)
}
#[inline(always)]
pub fn new_num(tag: Tag, bits: u64) -> Self {
Port((bits << 4) | (tag as u64))
}
#[inline(always)]
pub fn new_ref(def: &Def) -> Port {
Port::new(Ref, def.labs.min_safe, Addr(def as *const _ as _))
}
#[inline(always)]
pub fn tag(&self) -> Tag {
unsafe { Tag::from_unchecked((self.0 & 0x7) as u8) }
}
#[inline(always)]
pub fn is(&self, tag: Tag) -> bool {
self.tag() == tag
}
#[inline(always)]
pub fn is_num(&self) -> bool {
self.tag() == Tag::Int || self.tag() == Tag::F32
}
#[inline(always)]
pub fn is_era(&self) -> bool {
matches!(*self, Port::ERA)
}
#[inline(always)]
pub const fn lab(&self) -> Lab {
(self.0 >> 48) as Lab
}
#[inline(always)]
pub const fn addr(&self) -> Addr {
Addr((self.0 & 0x0000_FFFF_FFFF_FFF8) as usize as _)
}
#[inline(always)]
pub fn op(&self) -> Op {
unsafe { self.lab().try_into().unwrap_unchecked() }
}
#[inline(always)]
pub const fn int(&self) -> i64 {
self.0 as i64 >> 4
}
#[inline(always)]
pub fn float(&self) -> f32 {
f32::from_bits(self.num() as u32)
}
#[inline(always)]
pub const fn num(&self) -> u64 {
((self.0 as i64) >> 4) as u64
}
#[inline(always)]
pub fn wire(&self) -> Wire {
Wire::new(self.addr())
}
#[inline(always)]
pub fn is_principal(&self) -> bool {
self.tag() >= Ref
}
#[inline(always)]
pub fn is_skippable(&self) -> bool {
self.is_num() || self.tag() == Ref && self.lab() != u16::MAX
}
#[inline(always)]
pub(super) fn redirect(&self) -> Port {
Port::new(Red, 0, self.addr())
}
#[inline(always)]
pub(super) fn unredirect(&self) -> Port {
Port::new(Var, 0, self.addr())
}
pub fn is_full_node(&self) -> bool {
self.tag() > F32
}
}