use crate::key_buckets::BucketError;
use redb::{Key, Value};
use std::cmp::Ordering;
use std::fmt::Debug;
#[derive(Debug, Clone)]
pub struct KeyBuilder {
bucket_size: u64,
}
impl KeyBuilder {
pub fn new(bucket_size: u64) -> Result<Self, BucketError> {
if bucket_size == 0 {
return Err(BucketError::InvalidBucketSize(bucket_size));
}
Ok(Self { bucket_size })
}
pub fn bucketed_key<K: Key>(&self, base_key: K, sequence: u64) -> BucketedKey<K> {
let bucket = sequence / self.bucket_size;
BucketedKey { base_key, bucket }
}
pub fn bucket_size(&self) -> u64 {
self.bucket_size
}
}
#[derive(Debug, Clone)]
pub struct BucketedKey<K: Key> {
pub base_key: K,
pub bucket: u64,
}
impl<K: Key> BucketedKey<K> {
pub fn new(base_key: K, bucket: u64) -> Self {
Self { base_key, bucket }
}
pub fn base_key(&self) -> &K {
&self.base_key
}
pub fn bucket(&self) -> u64 {
self.bucket
}
}
impl Value for BucketedKey<u64> {
type SelfType<'a>
= BucketedKey<u64>
where
Self: 'a;
type AsBytes<'a>
= Vec<u8>
where
Self: 'a;
fn fixed_width() -> Option<usize> {
Some(16) }
fn from_bytes<'a>(data: &'a [u8]) -> Self::SelfType<'a>
where
Self: 'a,
{
if data.len() < 16 {
panic!(
"BucketedKey data too short: expected at least 16 bytes, got {}",
data.len()
);
}
let bucket = u64::from_le_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);
let base_key = u64::from_le_bytes([
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
]);
BucketedKey { base_key, bucket }
}
fn as_bytes<'a, 'b: 'a>(value: &'a Self::SelfType<'b>) -> Self::AsBytes<'a>
where
Self: 'a,
Self: 'b,
{
let bucket_bytes = value.bucket.to_le_bytes();
let base_key_bytes = value.base_key.to_le_bytes();
let mut result = Vec::with_capacity(16);
result.extend_from_slice(&bucket_bytes);
result.extend_from_slice(&base_key_bytes);
result
}
fn type_name() -> redb::TypeName {
redb::TypeName::new("redb_extras::key_buckets::BucketedKey<u64>")
}
}
impl Key for BucketedKey<u64> {
fn compare(data1: &[u8], data2: &[u8]) -> Ordering {
if data1.len() < 16 || data2.len() < 16 {
panic!("BucketedKey data too short for comparison");
}
let bucket1 = u64::from_le_bytes([
data1[0], data1[1], data1[2], data1[3], data1[4], data1[5], data1[6], data1[7],
]);
let bucket2 = u64::from_le_bytes([
data2[0], data2[1], data2[2], data2[3], data2[4], data2[5], data2[6], data2[7],
]);
match bucket1.cmp(&bucket2) {
Ordering::Equal => {
let base1 = u64::from_le_bytes([
data1[8], data1[9], data1[10], data1[11], data1[12], data1[13], data1[14],
data1[15],
]);
let base2 = u64::from_le_bytes([
data2[8], data2[9], data2[10], data2[11], data2[12], data2[13], data2[14],
data2[15],
]);
base1.cmp(&base2)
}
other => other,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_builder_creation() {
let builder = KeyBuilder::new(1000);
assert!(builder.is_ok());
assert_eq!(builder.unwrap().bucket_size(), 1000);
let builder = KeyBuilder::new(0);
assert!(builder.is_err());
}
#[test]
fn test_bucketed_key_creation() {
let builder = KeyBuilder::new(1000).unwrap();
let key1 = builder.bucketed_key(123u64, 500);
assert_eq!(key1.bucket(), 0);
assert_eq!(key1.base_key(), &123u64);
let key2 = builder.bucketed_key(123u64, 1500);
assert_eq!(key2.bucket(), 1);
assert_eq!(key2.base_key(), &123u64);
let key3 = builder.bucketed_key(123u64, 2500);
assert_eq!(key3.bucket(), 2);
}
#[test]
fn test_bucketed_key_serialization() {
let builder = KeyBuilder::new(1000).unwrap();
let key = builder.bucketed_key(123u64, 1500);
let bytes: Vec<u8> = BucketedKey::as_bytes(&key);
assert_eq!(bytes.len(), 16);
let deserialized: BucketedKey<u64> = BucketedKey::from_bytes(&bytes);
assert_eq!(deserialized.bucket(), 1);
assert_eq!(deserialized.base_key(), &123u64);
}
#[test]
fn test_bucketed_key_ordering() {
let builder = KeyBuilder::new(1000).unwrap();
let key1 = builder.bucketed_key(123u64, 500); let key2 = builder.bucketed_key(123u64, 1500); let key3 = builder.bucketed_key(456u64, 500);
let bytes1: Vec<u8> = BucketedKey::as_bytes(&key1);
let bytes2: Vec<u8> = BucketedKey::as_bytes(&key2);
let bytes3: Vec<u8> = BucketedKey::as_bytes(&key3);
assert_eq!(
BucketedKey::<u64>::compare(&bytes1, &bytes2),
Ordering::Less
);
assert_eq!(
BucketedKey::<u64>::compare(&bytes2, &bytes1),
Ordering::Greater
);
assert_eq!(
BucketedKey::<u64>::compare(&bytes1, &bytes3),
Ordering::Less
);
assert_eq!(
BucketedKey::<u64>::compare(&bytes3, &bytes1),
Ordering::Greater
);
}
}