pub(in crate::tree) mod inline_key_search;
pub(crate) mod inline_keys;
pub(in crate::tree) mod internal;
pub(crate) mod latch;
pub(super) mod layout;
pub(crate) mod leaf;
mod length_tags;
pub(super) mod node_plan;
pub(in crate::tree) mod optimistic;
pub(crate) mod page;
pub(in crate::tree) mod root_page;
pub(in crate::tree) mod siblings;
mod slots;
#[cfg(test)]
mod tests;
mod type_identity;
mod vector_count;
use core::ptr::NonNull;
use self::inline_key_search::NodeInlineKeys;
use self::inline_keys::{Separator, Window};
use self::internal::Internal;
use self::leaf::Leaf;
use self::node_plan::NodePlan;
use self::page::{NodeKind, NodePtr, PageByType};
use self::siblings::Siblings;
use crate::inline_key::{InlineKeyStrategy, InlineWord};
pub(crate) enum Node<K, V, S> {
Leaf(Leaf<K, V, S>),
Internal(Internal<K, V, S>),
}
impl<K, V, S> Clone for Node<K, V, S> {
fn clone(&self) -> Self {
*self
}
}
impl<K, V, S> Copy for Node<K, V, S> {}
impl<K, V, S> Node<K, V, S> {
#[inline(always)]
pub(in crate::tree) fn at(node: NodePtr<K, V, S>) -> Self {
if node.is_leaf() {
Self::Leaf(Leaf::at(node))
} else {
Self::Internal(Internal::at(node))
}
}
#[inline(always)]
pub(in crate::tree) fn node_ptr(self) -> NodePtr<K, V, S> {
match self {
Self::Leaf(leaf) => leaf.node_ptr(),
Self::Internal(internal) => internal.node_ptr(),
}
}
#[inline(always)]
pub(in crate::tree) fn len(self) -> usize {
unsafe { self.node_ptr().len() }
}
}
impl<K, V, S: InlineKeyStrategy<K>> Node<K, V, S> {
pub(in crate::tree) unsafe fn free(self) -> PageByType<K, V, S> {
unsafe {
match self {
Self::Leaf(leaf) => leaf.free().into(),
Self::Internal(internal) => internal.free().into(),
}
}
}
#[expect(
clippy::type_complexity,
reason = "a refused absorb hands back the separator and the node it took"
)]
pub(in crate::tree) fn absorb(
self,
separator: Separator<K, S>,
right: Self,
) -> Result<PageByType<K, V, S>, (Separator<K, S>, Self)> {
match (self, right) {
(Self::Leaf(left), Self::Leaf(right)) => left
.absorb(separator, right)
.map_err(|(separator, right)| (separator, Self::Leaf(right))),
(Self::Internal(left), Self::Internal(right)) => left
.absorb(separator, right)
.map_err(|(separator, right)| (separator, Self::Internal(right))),
_ => panic!("a node absorbs a node of its own kind"),
}
}
pub(in crate::tree) fn prefix_len(self) -> usize {
match self {
Self::Leaf(leaf) => leaf.prefix_len(),
Self::Internal(internal) => internal.prefix_len(),
}
}
}
pub(in crate::tree) struct Split<K, S, Kind> {
pub(in crate::tree) separator: Separator<K, S>,
pub(in crate::tree) right: Kind,
pub(in crate::tree) inserted: InsertionSide,
}
pub(in crate::tree) enum Insertion<K, S, Kind> {
Fit(usize),
Split(Split<K, S, Kind>),
}
#[derive(Clone, Copy)]
pub(in crate::tree) enum InsertionSide {
Left(usize),
Right(usize),
}
impl InsertionSide {
pub(in crate::tree) fn index(self) -> usize {
match self {
Self::Left(index) | Self::Right(index) => index,
}
}
pub(in crate::tree) fn is_right(self) -> bool {
matches!(self, Self::Right(_))
}
#[inline(always)]
pub(in crate::tree) fn of_split(
capacity: usize,
insertion_index: usize,
minimum: usize,
) -> (usize, Self) {
let arriving_len = (2 * minimum).min(capacity / 2);
let separator_index = if insertion_index == 0 {
arriving_len - 1
} else if insertion_index == capacity {
capacity - arriving_len
} else {
capacity / 2
};
debug_assert!(separator_index < capacity);
let side = if insertion_index <= separator_index {
Self::Left(insertion_index)
} else {
Self::Right(insertion_index - separator_index - 1)
};
(separator_index, side)
}
}
pub(crate) struct EntrySlots<K, V> {
pub(in crate::tree) key: NonNull<K>,
pub(in crate::tree) value: NonNull<V>,
}
pub(in crate::tree) struct HeadSuccession<K, V, S> {
pub(in crate::tree) removed_key: Option<K>,
pub(in crate::tree) value: V,
pub(in crate::tree) successor: Separator<K, S>,
}
pub(in crate::tree) trait Child<K, V, S: InlineKeyStrategy<K>>:
NodeKind + Copy
{
fn child_of(parent: Internal<K, V, S>, index: usize) -> Self;
fn len(self) -> usize;
fn capacity() -> usize;
fn min_len() -> usize {
(Self::capacity() / 8).max(4)
}
fn prefix_len(self) -> usize;
fn has_first_trailing_slot(self) -> bool;
fn move_prefix_back_to(self, fill: Window);
unsafe fn free_page(self) -> PageByType<K, V, S>;
unsafe fn steal_left_unchecked(siblings: &Siblings<K, V, S, Self>, fill: Window);
unsafe fn steal_right_unchecked(siblings: &Siblings<K, V, S, Self>, fill: Window);
unsafe fn append(left: Self, fill: Window, separator: Separator<K, S>, right: Self);
#[expect(
clippy::type_complexity,
reason = "a refused absorb hands back the separator and the node it took"
)]
fn absorb(
self,
separator: Separator<K, S>,
right: Self,
) -> Result<PageByType<K, V, S>, (Separator<K, S>, Self)> {
if self.len() + 1 + right.len() > Self::capacity() {
return Err((separator, right));
}
assert!(
right.has_first_trailing_slot(),
"the leaf right of a separator has no head"
);
let fill = separator.window();
assert!(
fill.prefix_len() <= self.prefix_len().min(right.prefix_len()),
"a separator past the prefix of a node it joins"
);
self.move_prefix_back_to(fill);
right.move_prefix_back_to(fill);
Ok(unsafe {
Self::append(self, fill, separator, right);
right.free_page()
})
}
fn key<'a>(self, index: usize) -> &'a K;
fn inline_keys<'a>(self) -> Option<NodeInlineKeys<'a>>;
fn plan() -> NodePlan<K>;
#[cfg(any(test, fuzzing))]
fn length_tags<'a>(self) -> Option<&'a [u16]>;
fn allowed_prefix_len(self, prefix_len: usize) -> usize;
fn set_windows(self, prefix_len: usize, inline_keys: impl ExactSizeIterator<Item = InlineWord>);
fn keys_differ_past_prefix(self) -> bool {
let len = self.len();
self.inline_keys().is_some_and(|inline_keys| {
len > 0
&& (inline_keys.inline_key(0) ^ inline_keys.inline_key(len - 1)).leading_zeros()
< u8::BITS
})
}
fn refit_prefix(self, prefix_len: usize) {
let Some(inliner) = Self::plan().inliner() else {
return;
};
let prefix_len = self.allowed_prefix_len(prefix_len);
if prefix_len == self.prefix_len() {
return;
}
let windows =
(0..self.len()).map(|index| inliner.inline_key_at(self.key(index), prefix_len));
self.set_windows(prefix_len, windows);
}
fn shrink_prefix_to(self, prefix_len: usize) {
if self.prefix_len() > prefix_len {
self.refit_prefix(prefix_len);
}
}
}