reifydb_core/key/
version_epoch.rs1use std::ops::Bound;
5
6use super::{EncodableKey, KeyKind};
7use crate::{
8 encoded::key::{EncodedKey, EncodedKeyRange},
9 util::encoding::keycode::{deserializer::KeyDeserializer, serializer::KeySerializer},
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct VersionEpochKey {
14 pub bucket_nanos: u64,
15}
16
17impl EncodableKey for VersionEpochKey {
18 const KIND: KeyKind = KeyKind::VersionEpoch;
19
20 fn encode(&self) -> EncodedKey {
21 let mut serializer = KeySerializer::with_capacity(9);
22 serializer.extend_u8(Self::KIND as u8).extend_u64(self.bucket_nanos);
23 serializer.to_encoded_key()
24 }
25
26 fn decode(key: &EncodedKey) -> Option<Self> {
27 let mut de = KeyDeserializer::from_bytes(key.as_slice());
28
29 let kind: KeyKind = de.read_u8().ok()?.try_into().ok()?;
30 if kind != Self::KIND {
31 return None;
32 }
33
34 let bucket_nanos = de.read_u64().ok()?;
35
36 Some(Self {
37 bucket_nanos,
38 })
39 }
40}
41
42impl VersionEpochKey {
43 pub fn encoded(bucket_nanos: u64) -> EncodedKey {
44 Self {
45 bucket_nanos,
46 }
47 .encode()
48 }
49
50 pub fn floor_scan(target_nanos: u64) -> EncodedKeyRange {
51 EncodedKeyRange::new(Bound::Included(Self::encoded(target_nanos)), Bound::Included(Self::encoded(0)))
52 }
53
54 pub fn older_than(cutoff_nanos: u64) -> EncodedKeyRange {
55 EncodedKeyRange::new(Bound::Excluded(Self::encoded(cutoff_nanos)), Bound::Included(Self::encoded(0)))
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use std::ops::Bound;
62
63 use super::{EncodableKey, VersionEpochKey};
64
65 #[test]
66 fn test_encode_decode() {
67 let key = VersionEpochKey {
68 bucket_nanos: 0x0123456789ABCDEF,
69 };
70 let encoded = key.encode();
71 let decoded = VersionEpochKey::decode(&encoded).unwrap();
72 assert_eq!(decoded.bucket_nanos, 0x0123456789ABCDEF);
73 }
74
75 #[test]
76 fn test_descending_order_so_newer_bucket_sorts_first() {
77 let older = VersionEpochKey::encoded(100);
78 let newer = VersionEpochKey::encoded(200);
79 assert!(
80 newer < older,
81 "a newer (larger) bucket must encode to smaller key bytes so floor_scan can take the first entry at-or-after the target"
82 );
83 }
84
85 #[test]
86 fn test_floor_scan_lower_bound_is_target_bucket() {
87 let target = 150u64;
88 let range = VersionEpochKey::floor_scan(target);
89 assert_eq!(range.start, Bound::Included(VersionEpochKey::encoded(target)));
90 assert_eq!(range.end, Bound::Included(VersionEpochKey::encoded(0)));
91 assert!(VersionEpochKey::encoded(target) >= VersionEpochKey::encoded(target));
93 assert!(VersionEpochKey::encoded(target + 1) < VersionEpochKey::encoded(target));
94 }
95}