use super::node::NodeId;
const HEIGHT_SHIFT: u32 = 64;
const TXN_SHIFT: u32 = 80;
const HEIGHT_BITS: u32 = 16;
const TXN_BITS: u32 = 48;
const HEIGHT_MASK: u64 = (1u64 << HEIGHT_BITS) - 1; pub const TXN_MAX: u64 = (1u64 << TXN_BITS) - 1;
pub const HEIGHT_MAX: u64 = HEIGHT_MASK;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PackedMeta(u128);
impl PackedMeta {
#[inline]
pub fn new(root_node_id: NodeId, height: u64, txn_id: u64) -> Self {
debug_assert!(height <= HEIGHT_MAX, "height {height} exceeds {HEIGHT_MAX}");
debug_assert!(txn_id <= TXN_MAX, "txn_id {txn_id} exceeds {TXN_MAX}");
let w = (root_node_id as u128)
| (((height & HEIGHT_MASK) as u128) << HEIGHT_SHIFT)
| (((txn_id & TXN_MAX) as u128) << TXN_SHIFT);
PackedMeta(w)
}
#[inline]
pub fn from_raw(word: u128) -> Self {
PackedMeta(word)
}
#[inline]
pub fn to_raw(self) -> u128 {
self.0
}
#[inline]
pub fn root_node_id(self) -> NodeId {
self.0 as u64
}
#[inline]
pub fn height(self) -> u64 {
((self.0 >> HEIGHT_SHIFT) as u64) & HEIGHT_MASK
}
#[inline]
pub fn txn_id(self) -> u64 {
((self.0 >> TXN_SHIFT) as u64) & TXN_MAX
}
#[inline]
pub fn with_next_txn(self, root_node_id: NodeId, height: u64) -> Self {
let next = self.txn_id() + 1;
debug_assert!(next <= TXN_MAX, "txn_id counter wrapped");
PackedMeta::new(root_node_id, height, next)
}
}
impl core::fmt::Debug for PackedMeta {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PackedMeta")
.field("root_node_id", &self.root_node_id())
.field("height", &self.height())
.field("txn_id", &self.txn_id())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_each_field() {
let p = PackedMeta::new(0xDEAD_BEEF_1234_5678, 7, 42);
assert_eq!(p.root_node_id(), 0xDEAD_BEEF_1234_5678);
assert_eq!(p.height(), 7);
assert_eq!(p.txn_id(), 42);
}
#[test]
fn raw_round_trip_is_identity() {
let p = PackedMeta::new(123, 4, 99);
assert_eq!(PackedMeta::from_raw(p.to_raw()), p);
}
#[test]
fn fields_are_independent() {
let p = PackedMeta::new(u64::MAX, HEIGHT_MAX, TXN_MAX);
assert_eq!(p.root_node_id(), u64::MAX);
assert_eq!(p.height(), HEIGHT_MAX);
assert_eq!(p.txn_id(), TXN_MAX);
let z = PackedMeta::new(0, 0, 0);
assert_eq!(z.to_raw(), 0);
assert_eq!(z.root_node_id(), 0);
assert_eq!(z.height(), 0);
assert_eq!(z.txn_id(), 0);
}
#[test]
fn aba_property_distinct_txn_distinct_word() {
let a = PackedMeta::new(500, 3, 10);
let b = PackedMeta::new(500, 3, 11);
assert_ne!(a.to_raw(), b.to_raw());
assert_ne!(a, b);
}
#[test]
fn with_next_txn_advances_only_the_counter() {
let a = PackedMeta::new(500, 3, 10);
let b = a.with_next_txn(600, 4);
assert_eq!(b.txn_id(), 11);
assert_eq!(b.root_node_id(), 600);
assert_eq!(b.height(), 4);
assert_ne!(a.to_raw(), b.to_raw());
}
#[test]
fn boundary_values_do_not_overlap() {
let p = PackedMeta::new(0, 0, TXN_MAX);
assert_eq!(p.root_node_id(), 0);
assert_eq!(p.height(), 0);
assert_eq!(p.txn_id(), TXN_MAX);
let h = PackedMeta::new(0, HEIGHT_MAX, 0);
assert_eq!(h.to_raw(), (HEIGHT_MASK as u128) << HEIGHT_SHIFT);
assert_eq!(h.height(), HEIGHT_MAX);
assert_eq!(h.txn_id(), 0);
}
#[test]
#[should_panic]
fn debug_rejects_out_of_range_height() {
let _ = PackedMeta::new(1, HEIGHT_MAX + 1, 0);
}
}