pub type Word = u64;
pub const TAG_REF: u64 = 0; pub const TAG_ATOM: u64 = 1; pub const TAG_INT: u64 = 2; pub const TAG_STR: u64 = 3; pub const TAG_LST: u64 = 4; pub const TAG_FLT: u64 = 5; pub const TAG_BIG: u64 = 6;
pub const INT_MAX: i64 = (1 << 60) - 1;
pub const INT_MIN: i64 = -(1 << 60);
#[inline]
pub fn make(tag: u64, payload: u64) -> Word {
(payload << 3) | tag
}
#[inline]
pub fn tag_of(w: Word) -> u64 {
w & 7
}
#[inline]
pub fn payload(w: Word) -> u64 {
w >> 3
}
#[inline]
pub fn make_atom(id: u32) -> Word {
make(TAG_ATOM, id as u64)
}
#[inline]
pub fn make_int(n: i64) -> Word {
debug_assert!((INT_MIN..=INT_MAX).contains(&n));
((n as u64) << 3) | TAG_INT
}
#[inline]
pub fn int_value(w: Word) -> i64 {
(w as i64) >> 3
}
#[inline]
pub fn make_ref(idx: usize) -> Word {
make(TAG_REF, idx as u64)
}
#[inline]
pub fn atom_id(w: Word) -> u32 {
payload(w) as u32
}
#[inline]
pub fn pack_functor(functor: u32, arity: u32) -> u64 {
((functor as u64) << 32) | arity as u64
}
#[inline]
pub fn unpack_functor(cell: u64) -> (u32, u32) {
((cell >> 32) as u32, cell as u32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn int_roundtrip_preserves_sign() {
for n in [0i64, 1, -1, 42, -42, INT_MAX, INT_MIN] {
assert_eq!(int_value(make_int(n)), n, "roundtrip {n}");
assert_eq!(tag_of(make_int(n)), TAG_INT);
}
}
#[test]
fn functor_packing() {
let cell = pack_functor(7, 3);
assert_eq!(unpack_functor(cell), (7, 3));
}
#[test]
fn tags_distinct() {
assert_eq!(tag_of(make_atom(5)), TAG_ATOM);
assert_eq!(tag_of(make_ref(9)), TAG_REF);
assert_eq!(atom_id(make_atom(5)), 5);
}
}