use core::fmt;
pub const NODE_RECORD_SIZE: usize = 24;
pub const KIND_OFFSET: usize = 0;
pub const COMMON_DATA_OFFSET: usize = 1;
pub const PADDING_OFFSET: usize = 2;
pub const POS_OFFSET: usize = 4;
pub const END_OFFSET: usize = 8;
pub const NODE_DATA_OFFSET: usize = 12;
pub const PARENT_INDEX_OFFSET: usize = 16;
pub const NEXT_SIBLING_OFFSET: usize = 20;
pub const COMMON_DATA_MASK: u8 = 0b0011_1111;
pub const COMMON_DATA_RESERVED_MASK: u8 = 0b1100_0000;
pub const COMMON_DATA_BITS: u8 = 6;
pub const TYPE_TAG_SHIFT: u32 = 30;
pub const TYPE_TAG_MASK: u32 = 0b11;
pub const PAYLOAD_MASK: u32 = 0x3FFF_FFFF;
pub const PAYLOAD_MAX: u32 = PAYLOAD_MASK;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeTag {
Children = 0b00,
String = 0b01,
Extended = 0b10,
StringInline = 0b11,
}
pub const STRING_INLINE_OFFSET_BITS: u32 = 22;
pub const STRING_INLINE_LENGTH_BITS: u32 = 8;
pub const STRING_INLINE_OFFSET_MAX: u32 = (1u32 << STRING_INLINE_OFFSET_BITS) - 1;
pub const STRING_INLINE_LENGTH_MAX: u32 = (1u32 << STRING_INLINE_LENGTH_BITS) - 1;
pub const STRING_INLINE_LENGTH_MASK: u32 = STRING_INLINE_LENGTH_MAX;
#[inline]
#[must_use]
pub const fn pack_string_inline(offset: u32, length: u8) -> u32 {
debug_assert!(offset <= STRING_INLINE_OFFSET_MAX);
(offset << STRING_INLINE_LENGTH_BITS) | (length as u32)
}
#[inline]
#[must_use]
pub const fn unpack_string_inline(payload: u32) -> (u32, u8) {
let offset = payload >> STRING_INLINE_LENGTH_BITS;
let length = (payload & STRING_INLINE_LENGTH_MASK) as u8;
(offset, length)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidTypeTag(pub u32);
impl fmt::Display for InvalidTypeTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid Node Data type tag 0b{:02b}", self.0)
}
}
impl TypeTag {
#[inline]
pub const fn from_u32(value: u32) -> Result<Self, InvalidTypeTag> {
match value {
0b00 => Ok(TypeTag::Children),
0b01 => Ok(TypeTag::String),
0b10 => Ok(TypeTag::Extended),
0b11 => Ok(TypeTag::StringInline),
other => Err(InvalidTypeTag(other)),
}
}
}
pub const STRING_PAYLOAD_NONE_SENTINEL: u32 = PAYLOAD_MAX;
#[inline]
#[must_use]
pub const fn pack_node_data(tag: TypeTag, payload: u32) -> u32 {
((tag as u32) << TYPE_TAG_SHIFT) | (payload & PAYLOAD_MASK)
}
#[inline]
#[must_use]
pub const fn type_tag_bits(node_data: u32) -> u32 {
(node_data >> TYPE_TAG_SHIFT) & TYPE_TAG_MASK
}
#[inline]
#[must_use]
pub const fn payload(node_data: u32) -> u32 {
node_data & PAYLOAD_MASK
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_record_size_is_24() {
assert_eq!(NODE_RECORD_SIZE, 24);
}
#[test]
fn field_offsets_partition_24_bytes() {
let layout: &[(usize, usize, &str)] = &[
(KIND_OFFSET, 1, "kind"),
(COMMON_DATA_OFFSET, 1, "common_data"),
(PADDING_OFFSET, 2, "padding"),
(POS_OFFSET, 4, "pos"),
(END_OFFSET, 4, "end"),
(NODE_DATA_OFFSET, 4, "node_data"),
(PARENT_INDEX_OFFSET, 4, "parent_index"),
(NEXT_SIBLING_OFFSET, 4, "next_sibling"),
];
let mut cursor = 0usize;
for (offset, size, name) in layout {
assert_eq!(*offset, cursor, "{name} starts at expected offset");
cursor += size;
}
assert_eq!(cursor, NODE_RECORD_SIZE, "fields cover all 24 bytes");
}
#[test]
fn common_data_masks_are_complementary() {
assert_eq!(COMMON_DATA_MASK | COMMON_DATA_RESERVED_MASK, 0xFF);
assert_eq!(COMMON_DATA_MASK & COMMON_DATA_RESERVED_MASK, 0x00);
assert_eq!(COMMON_DATA_MASK.count_ones(), COMMON_DATA_BITS as u32);
}
#[test]
fn node_data_pack_unpack_round_trip() {
for tag in [
TypeTag::Children,
TypeTag::String,
TypeTag::Extended,
TypeTag::StringInline,
] {
for &payload_value in &[0u32, 1, 0xABCD, PAYLOAD_MAX, PAYLOAD_MAX - 1] {
let nd = pack_node_data(tag, payload_value);
let extracted_tag = TypeTag::from_u32(type_tag_bits(nd)).unwrap();
let extracted_payload = payload(nd);
assert_eq!(extracted_tag, tag, "tag round trip");
assert_eq!(
extracted_payload, payload_value,
"payload round trip for {tag:?} / 0x{payload_value:08X}"
);
}
}
}
#[test]
fn payload_truncation_when_value_exceeds_30_bits() {
let nd = pack_node_data(TypeTag::String, 0xFFFF_FFFF);
assert_eq!(payload(nd), PAYLOAD_MAX);
assert_eq!(type_tag_bits(nd), TypeTag::String as u32);
}
#[test]
fn payload_max_is_2_pow_30_minus_1() {
assert_eq!(PAYLOAD_MAX, (1u32 << 30) - 1);
assert_eq!(STRING_PAYLOAD_NONE_SENTINEL, PAYLOAD_MAX);
}
#[test]
fn type_tag_from_u32_rejects_out_of_range() {
assert!(TypeTag::from_u32(4).is_err());
assert_eq!(TypeTag::from_u32(99).unwrap_err().0, 99);
}
#[test]
fn pack_keeps_payload_within_30_bits_when_in_range() {
let nd = pack_node_data(TypeTag::Children, PAYLOAD_MAX);
assert_eq!(type_tag_bits(nd), TypeTag::Children as u32);
assert_eq!(payload(nd), PAYLOAD_MAX);
}
}