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