use std::fmt::{self as StdFmt, Debug, Formatter};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr::{self as StdPtr, NonNull};
use arrayvec::ArrayVec;
use crate::leaf_trait::TreeLeafNode;
use crate::leaf15::LeafNode15;
use crate::ordering::READ_ORD;
use crate::policy::LeafPolicy;
use crate::policy::atomic_read_value;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScanState {
Emit = 0,
FindNext = 1,
Down = 2,
Up = 3,
Retry = 4,
}
impl ScanState {
#[inline(always)]
#[allow(dead_code, reason = "Scan state API")]
pub const fn is_emit(self) -> bool {
matches!(self, Self::Emit)
}
#[inline(always)]
#[allow(dead_code, reason = "Scan state API")]
pub const fn is_layer_transition(self) -> bool {
matches!(self, Self::Down | Self::Up)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepResult {
Continue,
Ready,
Exhausted,
}
pub struct ScanStackElement<P>
where
P: LeafPolicy,
{
root: *const u8,
leaf: Option<NonNull<LeafNode15<P>>>,
version: u32,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm,
ki: usize,
last_ikey: u64,
_marker: PhantomData<P>,
}
impl<P> Clone for ScanStackElement<P>
where
P: LeafPolicy,
{
fn clone(&self) -> Self {
Self {
root: self.root,
leaf: self.leaf,
version: self.version,
perm: self.perm,
ki: self.ki,
last_ikey: self.last_ikey,
_marker: PhantomData,
}
}
}
#[expect(
clippy::missing_fields_in_debug,
reason = "Intentionally omit PhantomData and show perm.size() instead of full permutation"
)]
impl<P> Debug for ScanStackElement<P>
where
P: LeafPolicy,
{
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
f.debug_struct("ScanStackElement")
.field("root", &self.root)
.field("leaf", &self.leaf)
.field("version", &self.version)
.field("ki", &self.ki)
.field("perm_size", &self.perm.size())
.finish()
}
}
impl<P> ScanStackElement<P>
where
P: LeafPolicy,
{
#[must_use]
#[expect(clippy::missing_const_for_fn, reason = "Perm::empty() is not const")]
pub fn new(root: *const u8) -> Self {
Self {
root,
leaf: None,
version: 0,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm::empty(),
ki: 0,
last_ikey: 0,
_marker: PhantomData,
}
}
#[must_use]
#[allow(dead_code, reason = "Scan stack element API")]
pub const fn with_leaf(
root: *const u8,
leaf: *mut LeafNode15<P>,
version: u32,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm,
ki: usize,
) -> Self {
Self {
root,
leaf: NonNull::new(leaf),
version,
perm,
ki,
last_ikey: 0,
_marker: PhantomData,
}
}
#[inline(always)]
pub const fn root(&self) -> *const u8 {
self.root
}
#[inline(always)]
pub const fn leaf_ptr(&self) -> *mut LeafNode15<P> {
match self.leaf {
Some(nn) => nn.as_ptr(),
None => StdPtr::null_mut(),
}
}
#[inline(always)]
pub unsafe fn leaf_ref(&self) -> &LeafNode15<P> {
debug_assert!(self.leaf.is_some(), "leaf_ref called on null leaf");
unsafe { self.leaf.unwrap_unchecked().as_ref() }
}
#[inline(always)]
#[allow(dead_code, reason = "Scan stack element API")]
pub unsafe fn try_leaf_ref(&self) -> Option<&LeafNode15<P>> {
self.leaf.map(|nn| unsafe { nn.as_ref() })
}
#[inline(always)]
pub const fn version(&self) -> u32 {
self.version
}
#[inline(always)]
pub const fn perm(&self) -> <LeafNode15<P> as TreeLeafNode<P>>::Perm {
self.perm
}
#[inline(always)]
pub const fn ki(&self) -> usize {
self.ki
}
#[inline]
#[expect(
clippy::missing_const_for_fn,
reason = "perm.size()/get() are not const"
)]
pub fn kp(&self) -> Option<usize> {
if self.ki < self.perm.size() {
Some(self.perm.get(self.ki))
} else {
None
}
}
#[inline(always)]
#[allow(dead_code, reason = "Scan stack element API")]
#[expect(clippy::missing_const_for_fn, reason = "perm.size() is not const")]
pub fn is_exhausted(&self) -> bool {
self.ki >= self.perm.size()
}
#[inline(always)]
pub const fn is_null(&self) -> bool {
self.leaf.is_none()
}
#[inline(always)]
pub const fn set_root(&mut self, root: *const u8) {
self.root = root;
}
#[inline(always)]
pub const fn set_leaf(&mut self, leaf: *mut LeafNode15<P>) {
self.leaf = NonNull::new(leaf);
}
#[inline(always)]
#[allow(dead_code, reason = "Scan stack element API")]
pub const fn set_version(&mut self, version: u32) {
self.version = version;
}
#[inline(always)]
#[allow(dead_code, reason = "Scan stack element API")]
pub const fn set_perm(&mut self, perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm) {
self.perm = perm;
}
#[inline(always)]
#[allow(dead_code, reason = "Scan stack element API")]
pub const fn set_ki(&mut self, ki: usize) {
self.ki = ki;
}
#[inline(always)]
pub const fn last_ikey(&self) -> u64 {
self.last_ikey
}
#[inline(always)]
pub const fn set_last_ikey(&mut self, ikey: u64) {
self.last_ikey = ikey;
}
#[inline(always)]
pub const fn next(&mut self) {
self.ki += 1;
}
#[inline]
pub const fn update_state(
&mut self,
version: u32,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm,
ki: usize,
) {
self.version = version;
self.perm = perm;
self.ki = ki;
}
#[allow(dead_code, reason = "Scan stack element API")]
pub unsafe fn refresh_from_leaf(&mut self, ki: usize) {
debug_assert!(self.leaf.is_some(), "refresh_from_leaf called on null leaf");
let leaf: &LeafNode15<P> = unsafe { self.leaf.unwrap_unchecked().as_ref() };
self.version = leaf.version().stable();
self.perm = leaf.permutation();
self.ki = ki;
}
}
#[derive(Clone, Copy)]
pub struct LayerContext<P: LeafPolicy> {
pub root: *const u8,
pub leaf: NonNull<LeafNode15<P>>,
}
impl<P: LeafPolicy> Debug for LayerContext<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
f.debug_struct("LayerContext")
.field("root", &self.root)
.field("leaf", &self.leaf)
.finish()
}
}
impl<P: LeafPolicy> LayerContext<P> {
#[track_caller]
#[inline(always)]
#[expect(clippy::expect_used, reason = "Infallible")]
pub const fn new(root: *const u8, leaf: *mut LeafNode15<P>) -> Self {
Self {
root,
leaf: NonNull::new(leaf).expect("LayerContext requires non-null leaf"),
}
}
#[inline(always)]
pub const fn leaf_ptr(&self) -> *mut LeafNode15<P> {
self.leaf.as_ptr()
}
}
pub struct LayerStack<P: LeafPolicy> {
inline: ArrayVec<LayerContext<P>, 6>,
overflow: Option<Vec<LayerContext<P>>>,
}
impl<P: LeafPolicy> LayerStack<P> {
#[inline(always)]
pub const fn new() -> Self {
Self {
inline: ArrayVec::new_const(),
overflow: None,
}
}
#[inline]
pub fn push(&mut self, ctx: LayerContext<P>) {
if let Some(ref mut overflow) = self.overflow {
overflow.push(ctx);
} else if let Err(err) = self.inline.try_push(ctx) {
let mut heap = Vec::with_capacity(8);
heap.extend(self.inline.drain(..));
heap.push(err.element());
self.overflow = Some(heap);
}
}
#[inline]
pub fn pop(&mut self) -> Option<LayerContext<P>> {
if let Some(ref mut overflow) = self.overflow {
let result = overflow.pop();
if overflow.is_empty() {
self.overflow = None;
}
result
} else {
self.inline.pop()
}
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
match self.overflow {
Some(ref overflow) => overflow.is_empty(),
None => self.inline.is_empty(),
}
}
#[inline]
#[allow(dead_code, reason = "API completeness")]
pub fn clear(&mut self) {
self.overflow = None;
self.inline.clear();
}
#[cfg(test)]
pub const fn len(&self) -> usize {
match self.overflow.as_ref() {
Some(v) => v.len(),
None => self.inline.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct ScanSnapshot<P: LeafPolicy> {
pub value: P::Output,
#[allow(dead_code, reason = "Used in tests and part of snapshot API")]
pub key_len: usize,
}
#[derive(Debug, Clone, Copy)]
pub struct ScanSnapshotPtr<V> {
pub value_ptr: *const V,
#[allow(dead_code, reason = "Used in tests")]
pub key_len: usize,
}
#[allow(dead_code, reason = "Zero-copy scan snapshot API")]
impl<V> ScanSnapshotPtr<V> {
#[inline(always)]
pub const fn new(value_ptr: *const V, key_len: usize) -> Self {
Self { value_ptr, key_len }
}
#[inline(always)]
pub const fn from_raw(ptr: *const u8, key_len: usize) -> Self {
Self {
value_ptr: ptr.cast::<V>(),
key_len,
}
}
#[inline(always)]
pub const unsafe fn value_ref(&self) -> &V {
unsafe { &*self.value_ptr }
}
#[inline(always)]
pub unsafe fn value_copy(&self) -> V {
unsafe { atomic_read_value::<V>(self.value_ptr.cast(), READ_ORD) }
}
#[inline(always)]
pub unsafe fn resolve_value_ref<'a, P: LeafPolicy<Value = V>>(
&self,
scratch: &'a mut MaybeUninit<V>,
) -> &'a V {
if P::CAN_WRITE_THROUGH {
*scratch = MaybeUninit::new(unsafe { self.value_copy() });
unsafe { scratch.assume_init_ref() }
} else {
unsafe { &*self.value_ptr }
}
}
}
impl<P: LeafPolicy> ScanSnapshot<P> {
#[inline(always)]
pub const fn new(value: P::Output, key_len: usize) -> Self {
Self { value, key_len }
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ScanStateBack {
Emit = 0,
#[default]
FindPrev = 1,
Down = 2,
Up = 3,
Retry = 4,
}
impl ScanStateBack {
#[inline(always)]
pub const fn is_emit(self) -> bool {
matches!(self, Self::Emit)
}
}
pub struct BackStackElement<P>
where
P: LeafPolicy,
{
root: *const u8,
leaf: Option<NonNull<LeafNode15<P>>>,
version: u32,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm,
ki: isize,
last_ikey: u64,
_marker: PhantomData<P>,
}
impl<P> BackStackElement<P>
where
P: LeafPolicy,
{
#[inline(always)]
#[expect(clippy::missing_const_for_fn, reason = "Perm::empty() is not const")]
pub fn new(root: *const u8) -> Self {
Self {
root,
leaf: None,
version: 0,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm::empty(),
ki: 0,
last_ikey: u64::MAX,
_marker: PhantomData,
}
}
#[inline(always)]
pub const fn get_root(&self) -> *const u8 {
self.root
}
#[inline(always)]
pub const fn set_root(&mut self, root: *const u8) {
self.root = root;
}
#[inline(always)]
pub fn get_leaf_ptr(&self) -> *mut LeafNode15<P> {
self.leaf.map_or(StdPtr::null_mut(), NonNull::as_ptr)
}
#[inline(always)]
pub const fn set_leaf(&mut self, leaf: *mut LeafNode15<P>) {
self.leaf = NonNull::new(leaf);
}
#[inline(always)]
pub const fn update_state(
&mut self,
version: u32,
perm: <LeafNode15<P> as TreeLeafNode<P>>::Perm,
ki: isize,
) {
self.version = version;
self.perm = perm;
self.ki = ki;
}
#[inline(always)]
pub const fn get_version(&self) -> u32 {
self.version
}
#[inline(always)]
pub const fn get_perm_ref(&self) -> &<LeafNode15<P> as TreeLeafNode<P>>::Perm {
&self.perm
}
#[inline(always)]
pub const fn get_ki(&self) -> isize {
self.ki
}
#[inline(always)]
pub const fn set_ki(&mut self, ki: isize) {
self.ki = ki;
}
#[inline(always)]
pub const fn last_ikey(&self) -> u64 {
self.last_ikey
}
#[inline(always)]
pub const fn set_last_ikey(&mut self, ikey: u64) {
self.last_ikey = ikey;
}
}
impl<P> Default for BackStackElement<P>
where
P: LeafPolicy,
{
fn default() -> Self {
Self::new(StdPtr::null())
}
}
impl<P> Clone for BackStackElement<P>
where
P: LeafPolicy,
{
fn clone(&self) -> Self {
Self {
root: self.root,
leaf: self.leaf,
version: self.version,
perm: self.perm,
ki: self.ki,
last_ikey: self.last_ikey,
_marker: PhantomData,
}
}
}
impl<P> Debug for BackStackElement<P>
where
P: LeafPolicy,
{
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
f.debug_struct("BackStackElement")
.field("root", &self.root)
.field("leaf", &self.leaf)
.field("version", &self.version)
.field("ki", &self.ki)
.field("last_ikey", &self.last_ikey)
.field("perm_size", &self.perm.size())
.finish()
}
}
#[cfg(test)]
mod unit_tests;