use crate::error::{DbxError, DbxResult};
use std::convert::TryInto;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VersionedKey {
pub user_key: Vec<u8>,
pub commit_ts: u64,
}
impl PartialOrd for VersionedKey {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for VersionedKey {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.user_key.cmp(&other.user_key) {
std::cmp::Ordering::Equal => {
other.commit_ts.cmp(&self.commit_ts)
}
ord => ord,
}
}
}
impl VersionedKey {
pub fn new(user_key: Vec<u8>, commit_ts: u64) -> Self {
Self {
user_key,
commit_ts,
}
}
pub fn encode(&self) -> Vec<u8> {
let mut bytes = Vec::with_capacity(self.user_key.len() + 10);
bytes.extend_from_slice(&self.user_key);
let inverted_ts = !self.commit_ts;
bytes.extend_from_slice(&inverted_ts.to_be_bytes());
bytes.extend_from_slice(&[0xDB, 0x58]);
bytes
}
pub fn decode(bytes: &[u8]) -> DbxResult<Self> {
if bytes.len() < 10 {
return Err(DbxError::Storage("Invalid versioned key length".into()));
}
if bytes[bytes.len() - 2..] != [0xDB, 0x58] {
return Err(DbxError::Storage("Missing MVCC magic suffix".into()));
}
let split_idx = bytes.len() - 10;
let user_key = bytes[..split_idx].to_vec();
let ts_bytes: [u8; 8] = bytes[split_idx..split_idx + 8].try_into().unwrap(); let inverted_ts = u64::from_be_bytes(ts_bytes);
let commit_ts = !inverted_ts;
Ok(Self {
user_key,
commit_ts,
})
}
pub fn extract_user_key(bytes: &[u8]) -> Option<&[u8]> {
if bytes.len() < 10 || bytes[bytes.len() - 2..] != [0xDB, 0x58] {
None
} else {
Some(&bytes[..bytes.len() - 10])
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_versioned_key_encoding() {
let key = b"key1".to_vec();
let ts = 100;
let vk = VersionedKey::new(key.clone(), ts);
let encoded = vk.encode();
assert_eq!(encoded.len(), key.len() + 10);
let decoded = VersionedKey::decode(&encoded).unwrap();
assert_eq!(decoded, vk);
}
#[test]
fn test_version_ordering() {
let key = b"key".to_vec();
let v2 = VersionedKey::new(key.clone(), 200);
let v1 = VersionedKey::new(key.clone(), 100);
assert!(v2.encode() < v1.encode());
}
}