use std::ops::Bound;
use crate::key::IKEY_SIZE;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RangeBound<'a> {
Unbounded,
Included(&'a [u8]),
Excluded(&'a [u8]),
}
impl<'a> RangeBound<'a> {
#[must_use]
#[inline(always)]
pub fn contains(&self, key: &[u8]) -> bool {
match self {
RangeBound::Unbounded => true,
RangeBound::Included(bound) => key <= *bound,
RangeBound::Excluded(bound) => key < *bound,
}
}
#[must_use]
#[inline(always)]
pub const fn to_start_params(&self) -> (&'a [u8], bool) {
match self {
RangeBound::Unbounded => (&[], true),
RangeBound::Included(k) => (*k, true),
RangeBound::Excluded(k) => (*k, false),
}
}
#[must_use]
#[inline(always)]
pub const fn is_unbounded(&self) -> bool {
matches!(self, RangeBound::Unbounded)
}
#[must_use]
#[inline(always)]
pub const fn key(&self) -> Option<&'a [u8]> {
match self {
RangeBound::Unbounded => None,
RangeBound::Included(k) | RangeBound::Excluded(k) => Some(*k),
}
}
#[must_use]
#[inline(always)]
pub fn contains_reverse(&self, key: &[u8]) -> bool {
match self {
RangeBound::Unbounded => true,
RangeBound::Included(bound) => key >= *bound,
RangeBound::Excluded(bound) => key > *bound,
}
}
#[must_use]
#[inline]
pub fn extract_ikey(&self) -> Option<u64> {
self.extract_ikey_at(0)
}
#[must_use]
#[inline]
pub fn extract_ikey_at(&self, offset: usize) -> Option<u64> {
match self {
RangeBound::Unbounded => None,
RangeBound::Included(bound) | RangeBound::Excluded(bound) => {
Some(Self::key_to_ikey_at(bound, offset))
}
}
}
#[inline]
#[expect(clippy::indexing_slicing, reason = "length is checked before indexing")]
fn key_to_ikey_at(key: &[u8], offset: usize) -> u64 {
if offset >= key.len() {
return 0;
}
let remaining = key.len() - offset;
if remaining >= IKEY_SIZE {
#[expect(clippy::expect_used, reason = "length is checked")]
u64::from_be_bytes(
key[offset..offset + IKEY_SIZE]
.try_into()
.expect("slice is 8 bytes"),
)
} else {
let mut bytes = [0u8; IKEY_SIZE];
bytes[..remaining].copy_from_slice(&key[offset..]);
u64::from_be_bytes(bytes)
}
}
}
impl<'a> From<Bound<&'a [u8]>> for RangeBound<'a> {
fn from(bound: Bound<&'a [u8]>) -> Self {
match bound {
Bound::Unbounded => RangeBound::Unbounded,
Bound::Included(k) => RangeBound::Included(k),
Bound::Excluded(k) => RangeBound::Excluded(k),
}
}
}