use super::node::NodeType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct SlotEntryRaw(pub u32);
const OFFSET_DIV8_MASK: u32 = (1 << 17) - 1;
const TAG_SHIFT: u32 = 17;
#[derive(Debug, Clone, Copy)]
pub struct SlotEntry {
pub offset_div8: u32,
pub ntype_or_next_free: u16,
}
impl SlotEntry {
#[must_use]
pub fn live(ntype: NodeType, byte_offset: u32) -> Self {
debug_assert_eq!(byte_offset % 8, 0, "body must be 8-byte aligned");
debug_assert!(byte_offset < super::header::PAGE_SIZE, "offset out of blob");
Self {
offset_div8: byte_offset / 8,
ntype_or_next_free: ntype.as_u8() as u16,
}
}
#[must_use]
pub fn freed(next_free_slot: u16, byte_offset: u32) -> Self {
debug_assert_eq!(byte_offset % 8, 0);
debug_assert!(next_free_slot < (1 << 15));
Self {
offset_div8: byte_offset / 8,
ntype_or_next_free: next_free_slot,
}
}
#[must_use]
pub const fn byte_offset(self) -> u32 {
self.offset_div8 * 8
}
#[must_use]
pub fn node_type(self) -> Option<NodeType> {
NodeType::from_raw(self.ntype_or_next_free as u8)
}
#[must_use]
pub const fn next_free(self) -> u16 {
self.ntype_or_next_free
}
#[must_use]
pub const fn raw(self) -> SlotEntryRaw {
SlotEntryRaw((self.ntype_or_next_free as u32) << TAG_SHIFT | self.offset_div8)
}
}
impl SlotEntryRaw {
#[must_use]
pub const fn decode(self) -> SlotEntry {
SlotEntry {
offset_div8: self.0 & OFFSET_DIV8_MASK,
ntype_or_next_free: (self.0 >> TAG_SHIFT) as u16,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn live_round_trip() {
let e = SlotEntry::live(NodeType::Node4, 0xB000);
let raw: u32 = e.raw().0;
let expected: u32 = (4u32 << 17) | (0xB000u32 / 8);
assert_eq!(raw, expected);
let back = SlotEntryRaw(raw).decode();
assert_eq!(back.byte_offset(), 0xB000);
assert_eq!(back.node_type(), Some(NodeType::Node4));
}
#[test]
fn freed_chain_preserves_offset_and_next() {
let e = SlotEntry::freed(42, 0x1000);
assert_eq!(e.next_free(), 42);
assert_eq!(e.byte_offset(), 0x1000);
assert_eq!(e.node_type(), None);
}
#[test]
fn alignment_check_panics_in_debug_only() {
let _ = SlotEntry::live(NodeType::Leaf, 0xB008);
}
}