#![expect(clippy::indexing_slicing, reason = "Safety notes are provided inline")]
use std::fmt::{self as StdFmt, Formatter};
use std::{cmp::Ordering, fmt::Debug};
#[cfg(debug_assertions)]
use std::fmt::Display;
use crate::key::{IKEY_SIZE, MAX_KEY_LENGTH};
pub const UNSHIFT_SENTINEL_LEN: usize = IKEY_SIZE + 1;
#[derive(Clone)]
pub struct CursorKey {
buf: [u8; MAX_KEY_LENGTH],
offset: usize,
len: usize,
ikey: u64,
}
#[expect(clippy::missing_fields_in_debug, reason = "don't print buffer")]
impl Debug for CursorKey {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
f.debug_struct("CursorKey")
.field("offset", &self.offset)
.field("len", &self.len)
.field("ikey", &format_args!("{:#018x}", self.ikey))
.field("full_key", &self.full_key())
.finish()
}
}
impl Default for CursorKey {
fn default() -> Self {
Self::empty()
}
}
impl CursorKey {
#[must_use]
#[inline]
pub fn from_slice(data: &[u8]) -> Self {
assert!(
data.len() <= MAX_KEY_LENGTH,
"key length {} exceeds maximum {}",
data.len(),
MAX_KEY_LENGTH
);
let mut buf: [u8; MAX_KEY_LENGTH] = [0u8; MAX_KEY_LENGTH];
buf[..data.len()].copy_from_slice(data);
let ikey: u64 = Self::read_ikey_from_buf(&buf, 0, data.len());
Self {
buf,
offset: 0,
len: data.len(),
ikey,
}
}
#[must_use]
#[inline(always)]
pub const fn empty() -> Self {
Self {
buf: [0u8; MAX_KEY_LENGTH],
offset: 0,
len: 0,
ikey: 0,
}
}
#[must_use]
#[inline]
pub fn for_reverse_scan(end: &super::iterator::RangeBound<'_>) -> Self {
use super::iterator::RangeBound;
match end {
RangeBound::Unbounded => Self {
buf: [0xFF; MAX_KEY_LENGTH],
offset: 0,
len: UNSHIFT_SENTINEL_LEN,
ikey: u64::MAX,
},
RangeBound::Included(k) | RangeBound::Excluded(k) => Self::from_slice(k),
}
}
#[must_use]
#[inline(always)]
pub const fn current_ikey(&self) -> u64 {
self.ikey
}
#[must_use]
#[inline(always)]
pub fn full_key(&self) -> &[u8] {
let end: usize = self.offset + self.len;
&self.buf[..end]
}
#[must_use]
#[inline(always)]
pub unsafe fn full_key_unchecked(&self) -> &[u8] {
let end: usize = self.offset + self.len;
debug_assert!(
end <= MAX_KEY_LENGTH,
"CursorKey invariant violated: offset({}) + len({}) = {} > MAX_KEY_LENGTH({})",
self.offset,
self.len,
end,
MAX_KEY_LENGTH
);
unsafe { self.buf.get_unchecked(..end) }
}
#[must_use]
#[inline]
pub fn suffix(&self) -> &[u8] {
if self.len > IKEY_SIZE {
let suffix_start: usize = self.offset + IKEY_SIZE;
let suffix_end: usize = self.offset + self.len;
&self.buf[suffix_start..suffix_end]
} else {
&[]
}
}
#[must_use]
#[inline(always)]
pub const fn has_suffix(&self) -> bool {
self.len > IKEY_SIZE
}
#[must_use]
#[inline(always)]
pub const fn current_len(&self) -> usize {
self.len
}
#[must_use]
#[inline(always)]
pub const fn offset(&self) -> usize {
self.offset
}
#[must_use]
#[inline(always)]
pub const fn is_at_root_layer(&self) -> bool {
self.offset == 0
}
#[must_use]
#[inline(always)]
#[cfg(any(test, debug_assertions))]
pub const fn layer_depth(&self) -> usize {
self.offset / IKEY_SIZE
}
#[inline]
pub fn shift(&mut self) {
debug_assert!(self.has_suffix(), "shift() called without suffix");
self.offset += IKEY_SIZE;
self.len = self.len.saturating_sub(IKEY_SIZE);
self.ikey = Self::read_ikey_from_buf(&self.buf, self.offset, self.len);
}
#[inline]
pub fn shift_clear_reverse(&mut self) {
debug_assert!(
self.offset + IKEY_SIZE <= MAX_KEY_LENGTH,
"shift_clear_reverse: would exceed MAX_KEY_LENGTH"
);
self.offset += IKEY_SIZE;
self.ikey = u64::MAX;
self.buf[self.offset..self.offset + IKEY_SIZE].copy_from_slice(&u64::MAX.to_be_bytes());
self.len = UNSHIFT_SENTINEL_LEN;
}
#[inline]
pub fn shift_clear(&mut self) {
self.offset += IKEY_SIZE;
self.len = 0;
self.ikey = 0;
#[cfg(debug_assertions)]
{
let clear_start: usize = self.offset;
let clear_end: usize = (self.offset + IKEY_SIZE).min(MAX_KEY_LENGTH);
self.buf[clear_start..clear_end].fill(0);
}
}
#[inline(always)]
pub fn unshift(&mut self) {
debug_assert!(self.offset >= IKEY_SIZE, "unshift() called at root layer");
self.offset -= IKEY_SIZE;
self.ikey = Self::read_ikey_from_buf(&self.buf, self.offset, IKEY_SIZE);
self.len = UNSHIFT_SENTINEL_LEN;
}
#[inline(always)]
pub const fn is_at_empty_root(&self) -> bool {
self.offset == 0 && self.len == 0
}
#[inline(always)]
pub fn assign_store_ikey(&mut self, ikey: u64) {
self.ikey = ikey;
let bytes: [u8; 8] = ikey.to_be_bytes();
let start: usize = self.offset;
let end: usize = start + IKEY_SIZE;
self.buf[start..end].copy_from_slice(&bytes);
}
#[inline]
pub fn assign_store_suffix(&mut self, suffix: &[u8]) -> usize {
let suffix_start: usize = self.offset + IKEY_SIZE;
let suffix_end: usize = suffix_start + suffix.len();
debug_assert!(
suffix_end <= MAX_KEY_LENGTH,
"suffix would overflow buffer: offset={}, suffix.len={}",
self.offset,
suffix.len()
);
self.buf[suffix_start..suffix_end].copy_from_slice(suffix);
IKEY_SIZE + suffix.len()
}
#[inline(always)]
pub fn assign_store_length(&mut self, len: usize) {
debug_assert!(
len <= MAX_KEY_LENGTH - self.offset,
"len {} would overflow at offset {}",
len,
self.offset
);
self.len = len;
}
#[inline(always)]
#[expect(clippy::unused_self, reason = "C++ API compatibility")]
pub const fn mark_key_complete(&self) {}
#[must_use]
#[inline(always)]
pub fn compare(&self, other_ikey: u64, keylenx: usize) -> Ordering {
match self.ikey.cmp(&other_ikey) {
Ordering::Equal => {}
ord => return ord,
}
if self.len > IKEY_SIZE {
if keylenx <= IKEY_SIZE {
Ordering::Greater
} else {
Ordering::Equal
}
} else {
self.len.cmp(&keylenx)
}
}
#[must_use]
#[inline(always)]
pub fn compare_suffix(&self, stored_suffix: &[u8]) -> Ordering {
self.suffix().cmp(stored_suffix)
}
#[must_use]
#[cfg(debug_assertions)]
pub fn debug_state(&self) -> CursorDebugState {
CursorDebugState {
offset: self.offset,
len: self.len,
ikey: self.ikey,
full_key: self.full_key().to_vec(),
layer_depth: self.layer_depth(),
}
}
#[inline]
fn read_ikey_from_buf(buf: &[u8; MAX_KEY_LENGTH], offset: usize, len: usize) -> u64 {
if len == 0 {
return 0;
}
let available: usize = len.min(IKEY_SIZE);
let start: usize = offset;
let end: usize = offset + available;
if available == IKEY_SIZE {
#[expect(clippy::expect_used, reason = "infallible: guarded by available == 8")]
let arr: [u8; 8] = buf[start..end]
.try_into()
.expect("slice is exactly 8 bytes");
return u64::from_be_bytes(arr);
}
let mut bytes: [u8; 8] = [0u8; 8];
bytes[..available].copy_from_slice(&buf[start..end]);
u64::from_be_bytes(bytes)
}
}
#[cfg(debug_assertions)]
#[derive(Clone, Debug)]
pub struct CursorDebugState {
pub offset: usize,
pub len: usize,
pub ikey: u64,
pub full_key: Vec<u8>,
pub layer_depth: usize,
}
#[cfg(debug_assertions)]
impl Display for CursorDebugState {
fn fmt(&self, f: &mut Formatter<'_>) -> StdFmt::Result {
write!(
f,
"CursorState {{ offset: {}, len: {}, ikey: {:016x}, layer: {}, key: {:?} }}",
self.offset,
self.len,
self.ikey,
self.layer_depth,
String::from_utf8_lossy(&self.full_key)
)
}
}
#[cfg(test)]
mod unit_tests;