reifydb_codec/log/
index.rs1use reifydb_value::{byte_size::ByteSize, reifydb_assertions, value::datetime::DateTime};
5
6use crate::log::{LogIndex, LogVersion, Position};
7
8pub const HEADER_BYTES: usize = 36;
9
10pub const ENTRY_BYTES: usize = 12;
11
12pub const MAGIC: u32 = u32::from_le_bytes(*b"RIDX");
13
14pub const DEFAULT_INTERVAL: ByteSize = ByteSize::from_kib(4);
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct TimestampRange {
18 pub min: DateTime,
19 pub max: DateTime,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct Header {
24 pub magic: u32,
25 pub base_version: LogVersion,
26 pub base_index: LogIndex,
27 pub timestamps: Option<TimestampRange>,
28}
29
30impl Header {
31 pub fn new(base_version: LogVersion, base_index: LogIndex) -> Self {
32 Self {
33 magic: MAGIC,
34 base_version,
35 base_index,
36 timestamps: None,
37 }
38 }
39
40 pub fn decode(buf: &[u8; HEADER_BYTES]) -> Self {
41 let min = DateTime::from_bits(u64::from_le_bytes(buf[20..28].try_into().unwrap()));
42 let max = DateTime::from_bits(u64::from_le_bytes(buf[28..36].try_into().unwrap()));
43 Self {
44 magic: u32::from_le_bytes(buf[0..4].try_into().unwrap()),
45 base_version: LogVersion::new(u64::from_le_bytes(buf[4..12].try_into().unwrap())),
46 base_index: LogIndex::new(u64::from_le_bytes(buf[12..20].try_into().unwrap())),
47 timestamps: (min <= max).then_some(TimestampRange {
48 min,
49 max,
50 }),
51 }
52 }
53
54 pub fn encode(&self) -> [u8; HEADER_BYTES] {
55 let (min, max) = match self.timestamps {
56 Some(range) => (range.min, range.max),
57 None => (DateTime::MAX, DateTime::EPOCH),
58 };
59 let mut out = [0u8; HEADER_BYTES];
60 out[0..4].copy_from_slice(&self.magic.to_le_bytes());
61 out[4..12].copy_from_slice(&self.base_version.as_u64().to_le_bytes());
62 out[12..20].copy_from_slice(&self.base_index.as_u64().to_le_bytes());
63 out[20..28].copy_from_slice(&min.to_bits().to_le_bytes());
64 out[28..36].copy_from_slice(&max.to_bits().to_le_bytes());
65 out
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct Entry {
71 pub version: LogVersion,
72 pub index: LogIndex,
73 pub position: Position,
74}
75
76pub fn encode_entry(header: &Header, entry: Entry) -> [u8; ENTRY_BYTES] {
77 reifydb_assertions! {
78 assert!(
79 entry.version >= header.base_version,
80 "version {} is below the base version {} the index was created with, and the delta wraps \
81 silently into an entry no lookup can follow",
82 entry.version,
83 header.base_version
84 );
85 assert!(
86 entry.version.as_u64() - header.base_version.as_u64() <= u32::MAX as u64,
87 "a delta of {} overflows the four byte delta field (limit={})",
88 entry.version.as_u64() - header.base_version.as_u64(),
89 u32::MAX
90 );
91 assert!(
92 entry.index >= header.base_index,
93 "index {} is below the base index {} the index was created with, and the delta wraps \
94 silently into an entry no lookup can follow",
95 entry.index,
96 header.base_index
97 );
98 assert!(
99 entry.index.as_u64() - header.base_index.as_u64() <= u32::MAX as u64,
100 "an index delta of {} overflows the four byte delta field (limit={})",
101 entry.index.as_u64() - header.base_index.as_u64(),
102 u32::MAX
103 );
104 assert!(
105 entry.position.as_u64() <= u32::MAX as u64,
106 "a position of {} overflows the four byte position field, so the segment must stay under \
107 {} bytes",
108 entry.position,
109 u32::MAX
110 );
111 }
112 let mut out = [0u8; ENTRY_BYTES];
113 out[0..4].copy_from_slice(&((entry.version.as_u64() - header.base_version.as_u64()) as u32).to_le_bytes());
114 out[4..8].copy_from_slice(&((entry.index.as_u64() - header.base_index.as_u64()) as u32).to_le_bytes());
115 out[8..12].copy_from_slice(&(entry.position.as_u64() as u32).to_le_bytes());
116 out
117}
118
119pub fn decode_entry(header: &Header, buf: &[u8; ENTRY_BYTES]) -> Entry {
120 Entry {
121 version: LogVersion::new(
122 header.base_version.as_u64() + u32::from_le_bytes(buf[0..4].try_into().unwrap()) as u64,
123 ),
124 index: LogIndex::new(
125 header.base_index.as_u64() + u32::from_le_bytes(buf[4..8].try_into().unwrap()) as u64,
126 ),
127 position: Position::new(u32::from_le_bytes(buf[8..12].try_into().unwrap()) as u64),
128 }
129}