use std::array as StdArray;
use std::sync::atomic::{Ordering as AtomicOrdering, fence};
use seize::Guard;
use crate::{
internode::{InternodeNode, WIDTH},
nodeversion::NodeVersion,
ordering::{READ_ORD, RELAXED, WRITE_ORD},
prefetch::prefetch_read,
};
impl InternodeNode {
#[must_use]
#[inline(always)]
pub const fn version(&self) -> &NodeVersion {
&self.version
}
#[inline(always)]
pub const fn version_mut(&mut self) -> &mut NodeVersion {
&mut self.version
}
#[must_use]
#[inline(always)]
pub fn nkeys(&self) -> usize {
self.nkeys.load(READ_ORD) as usize
}
#[must_use]
#[inline(always)]
pub fn size(&self) -> usize {
self.nkeys()
}
#[must_use]
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.nkeys() == 0
}
#[must_use]
#[inline(always)]
pub fn is_full(&self) -> bool {
self.nkeys() >= WIDTH
}
#[must_use]
#[inline(always)]
pub(crate) fn nkeys_relaxed(&self) -> usize {
self.nkeys.load(RELAXED) as usize
}
#[must_use]
#[inline(always)]
#[expect(clippy::indexing_slicing, reason = "bounds checked via debug_assert")]
pub fn ikey(&self, i: usize) -> u64 {
debug_assert!(i < WIDTH, "ikey: index {i} out of bounds (WIDTH={WIDTH})");
self.ikey0[i].load(READ_ORD)
}
#[must_use]
#[inline(always)]
#[expect(clippy::indexing_slicing, reason = "i < WIDTH guaranteed by from_fn")]
pub fn load_all_ikeys(&self) -> [u64; WIDTH] {
let ikeys: [u64; WIDTH] = StdArray::from_fn(|i| self.ikey0[i].load(RELAXED));
fence(AtomicOrdering::Acquire);
ikeys
}
#[inline(always)]
#[expect(clippy::indexing_slicing, reason = "bounds checked via debug_assert")]
pub fn set_ikey(&self, i: usize, ikey: u64) {
debug_assert!(i < WIDTH, "set_ikey: index {i} out of bounds");
self.ikey0[i].store(ikey, WRITE_ORD);
}
#[inline(always)]
#[expect(clippy::indexing_slicing, reason = "bounds checked by caller")]
pub(super) fn set_ikey_relaxed(&self, i: usize, ikey: u64) {
self.ikey0[i].store(ikey, RELAXED);
}
#[must_use]
#[inline(always)]
pub const fn height(&self) -> u32 {
self.height as u32
}
#[must_use]
#[inline(always)]
pub const fn children_are_leaves(&self) -> bool {
self.height == 0
}
#[must_use]
#[inline(always)]
#[expect(
clippy::indexing_slicing,
reason = "bounds checked via debug_assert; i <= WIDTH (16 children)"
)]
pub fn child(&self, i: usize, guard: &impl Guard) -> *mut u8 {
debug_assert!(i <= WIDTH, "child: index {i} out of bounds");
guard.protect(&self.child[i], READ_ORD)
}
#[must_use]
#[inline(always)]
#[expect(
clippy::indexing_slicing,
reason = "bounds checked via debug_assert; i <= WIDTH (16 children)"
)]
pub unsafe fn child_unguarded(&self, i: usize) -> *mut u8 {
debug_assert!(i <= WIDTH, "child_unguarded: index {i} out of bounds");
self.child[i].load(READ_ORD)
}
#[must_use]
#[inline]
#[expect(clippy::indexing_slicing, reason = "bounds checked via debug_assert")]
pub fn child_with_prefetch(&self, i: usize, nkeys: usize, guard: &impl Guard) -> *mut u8 {
debug_assert!(i <= WIDTH, "child_with_prefetch: index out of bounds");
let ptr = guard.protect(&self.child[i], READ_ORD);
if i < nkeys {
let next_child_ptr = self.child[i + 1].load(RELAXED);
prefetch_read(next_child_ptr);
prefetch_read(next_child_ptr.wrapping_add(64));
}
ptr
}
#[must_use]
#[inline]
#[expect(clippy::indexing_slicing, reason = "bounds checked via debug_assert")]
pub fn child_with_depth_prefetch(&self, i: usize, guard: &impl Guard) -> *mut u8 {
debug_assert!(i <= WIDTH, "child_with_depth_prefetch: index out of bounds");
let ptr: *mut u8 = guard.protect(&self.child[i], READ_ORD);
Self::prefetch_child_internal(ptr);
ptr
}
#[must_use]
#[inline]
#[expect(clippy::indexing_slicing, reason = "bounds checked via debug_assert")]
pub fn child_with_full_prefetch(&self, i: usize, guard: &impl Guard) -> *mut u8 {
debug_assert!(i <= WIDTH, "child_with_full_prefetch: index out of bounds");
let ptr: *mut u8 = guard.protect(&self.child[i], READ_ORD);
Self::prefetch_child_full(ptr);
ptr
}
#[inline(always)]
pub(super) fn prefetch_child_internal(ptr: *mut u8) {
prefetch_read(ptr);
prefetch_read(ptr.wrapping_add(64));
}
#[inline(always)]
pub(super) fn prefetch_child_full(ptr: *mut u8) {
prefetch_read(ptr);
prefetch_read(ptr.wrapping_add(64));
prefetch_read(ptr.wrapping_add(128));
}
#[inline(always)]
#[expect(
clippy::indexing_slicing,
reason = "bounds checked via debug_assert; i <= WIDTH (16 children)"
)]
pub fn set_child(&self, i: usize, child: *mut u8) {
debug_assert!(i <= WIDTH, "set_child: index {i} out of bounds");
self.child[i].store(child, WRITE_ORD);
}
#[inline(always)]
pub fn assign(&self, p: usize, ikey: u64, right_child: *mut u8) {
debug_assert!(p < WIDTH, "assign: position {p} out of bounds");
self.set_ikey(p, ikey);
self.set_child(p + 1, right_child);
}
#[inline(always)]
pub fn set_nkeys(&self, n: u8) {
debug_assert!((n as usize) <= WIDTH, "set_nkeys: count {n} out of bounds");
self.nkeys.store(n, WRITE_ORD);
}
#[inline(always)]
pub fn inc_nkeys(&self) {
let current: u8 = self.nkeys.load(RELAXED);
debug_assert!((current as usize) < WIDTH, "inc_nkeys: would overflow");
self.nkeys.store(current.wrapping_add(1), WRITE_ORD);
}
}