use std::sync::Arc;
pub trait MergeOperator: Send + Sync {
fn combine_operands(&self, key: &[u8], left: &[u8], right: &[u8]) -> Vec<u8>;
fn apply(&self, key: &[u8], base: Option<&[u8]>, operand: &[u8]) -> Option<Vec<u8>>;
}
#[derive(Default, Clone, Copy, Debug)]
pub struct U64AddOperator;
impl U64AddOperator {
fn decode(raw: &[u8]) -> u64 {
if raw.len() == size_of::<u64>() {
u64::from_le_bytes(raw.try_into().expect("exact u64 slice"))
} else {
log::error!(
"uint64 value corruption, size: {} > {}",
raw.len(),
size_of::<u64>()
);
0
}
}
fn add(left: u64, right: u64) -> Vec<u8> {
left.wrapping_add(right).to_le_bytes().to_vec()
}
}
impl MergeOperator for U64AddOperator {
fn combine_operands(&self, _key: &[u8], left: &[u8], right: &[u8]) -> Vec<u8> {
Self::add(Self::decode(left), Self::decode(right))
}
fn apply(&self, _key: &[u8], base: Option<&[u8]>, operand: &[u8]) -> Option<Vec<u8>> {
let base = base.map(Self::decode).unwrap_or(0);
Some(Self::add(base, Self::decode(operand)))
}
}
pub fn u64_add_operator() -> Option<Arc<dyn MergeOperator>> {
Some(Arc::new(U64AddOperator))
}
#[cfg(test)]
mod tests {
use super::{MergeOperator, U64AddOperator};
fn decode(raw: &[u8]) -> u64 {
u64::from_le_bytes(raw.try_into().unwrap())
}
#[test]
fn u64_add_operator_folds_onto_base_and_absence() {
let op = U64AddOperator;
let v = op.apply(b"k", None, &5u64.to_le_bytes()).unwrap();
assert_eq!(decode(&v), 5);
let v = op
.apply(b"k", Some(&10u64.to_le_bytes()), &5u64.to_le_bytes())
.unwrap();
assert_eq!(decode(&v), 15);
let v = op.combine_operands(b"k", &5u64.to_le_bytes(), &7u64.to_le_bytes());
assert_eq!(decode(&v), 12);
let v = op
.apply(b"k", Some(&u64::MAX.to_le_bytes()), &2u64.to_le_bytes())
.unwrap();
assert_eq!(decode(&v), 1);
}
#[test]
fn u64_add_operator_treats_malformed_payload_as_zero() {
let op = U64AddOperator;
let v = op
.apply(b"k", Some(&10u64.to_le_bytes()), b"short")
.unwrap();
assert_eq!(decode(&v), 10);
let v = op.apply(b"k", Some(b"bad"), &5u64.to_le_bytes()).unwrap();
assert_eq!(decode(&v), 5);
let v = op.apply(b"k", None, &0u64.to_le_bytes()).unwrap();
assert_eq!(decode(&v), 0);
}
}