use std::cmp;
use tempest_core::utils::PrettyBytes;
pub trait Comparer: Default + Clone + 'static {
fn split(key: &[u8]) -> usize;
fn compare_prefix(a: &[u8], b: &[u8]) -> cmp::Ordering;
fn compare_suffix(a: &[u8], b: &[u8]) -> cmp::Ordering;
fn split_up(key: &[u8]) -> (&[u8], &[u8]) {
let knon = Self::split(key);
key.split_at(knon)
}
fn compare_logical(a: &[u8], b: &[u8]) -> cmp::Ordering {
let anon = Self::split(a);
let bnon = Self::split(b);
Self::compare_prefix(&a[..anon], &b[..bnon])
}
fn compare_physical(a: &[u8], b: &[u8]) -> cmp::Ordering {
let anon = Self::split(a);
let bnon = Self::split(b);
match Self::compare_prefix(&a[..anon], &b[..bnon]) {
cmp::Ordering::Equal => Self::compare_suffix(&a[anon..], &b[bnon..]),
ord => ord,
}
}
fn format(key: &[u8]) -> String;
}
#[derive(Debug, Default, Clone)]
pub struct DefaultComparer;
impl Comparer for DefaultComparer {
fn split(key: &[u8]) -> usize {
key.len()
}
fn compare_prefix(a: &[u8], b: &[u8]) -> cmp::Ordering {
a.cmp(b)
}
fn compare_suffix(_a: &[u8], _b: &[u8]) -> cmp::Ordering {
cmp::Ordering::Equal
}
fn format(key: &[u8]) -> String {
format!("{:?}", PrettyBytes(key))
}
}
#[derive(Default, Clone)]
pub struct AssertComparer<C: Comparer>(C);
impl<C: Comparer> Comparer for AssertComparer<C> {
fn split(key: &[u8]) -> usize {
C::split(key)
}
fn compare_prefix(a: &[u8], b: &[u8]) -> cmp::Ordering {
C::compare_prefix(a, b)
}
fn compare_suffix(a: &[u8], b: &[u8]) -> cmp::Ordering {
C::compare_suffix(a, b)
}
fn compare_physical(a: &[u8], b: &[u8]) -> cmp::Ordering {
let res = C::compare_physical(a, b);
debug_assert_eq!(
res,
C::compare_physical(b, a).reverse(),
"anti-symmetry violation: compare(a,b) != reverse(compare(b,a))"
);
let split_a = C::split(a);
let split_b = C::split(b);
let prefix_cmp = C::compare_prefix(&a[..split_a], &b[..split_b]);
match prefix_cmp {
cmp::Ordering::Less => {
debug_assert_eq!(
res,
cmp::Ordering::Less,
"consistency violation: prefix(a) < prefix(b) but a >= b"
)
}
cmp::Ordering::Greater => {
debug_assert_eq!(
res,
cmp::Ordering::Greater,
"consistency violation: prefix(a) > prefix(b) but a <= b"
)
}
cmp::Ordering::Equal => {
}
}
res
}
fn format(key: &[u8]) -> String {
C::format(key)
}
}
#[derive(Default, Clone)]
pub struct FixedSuffixComparer<const N: usize>;
impl<const N: usize> Comparer for FixedSuffixComparer<N> {
fn split(key: &[u8]) -> usize {
key.len().saturating_sub(N)
}
fn compare_prefix(a: &[u8], b: &[u8]) -> cmp::Ordering {
a.cmp(b)
}
fn compare_suffix(a: &[u8], b: &[u8]) -> cmp::Ordering {
a.cmp(b)
}
fn format(key: &[u8]) -> String {
let (prefix, suffix) = Self::split_up(key);
format!("{:?} | {:?}", PrettyBytes(prefix), PrettyBytes(suffix))
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use super::*;
use crate::base::InternalKey;
#[test]
fn test_assert_comparer() {
type C = AssertComparer<DefaultComparer>;
let keys: &[InternalKey<C>] = &[
InternalKey::test(1),
InternalKey::test(2),
InternalKey::test(3),
InternalKey::test(4),
InternalKey::test(5),
];
for (a, b) in keys.iter().tuple_windows() {
assert!(b > a);
}
}
}