reifydb_codec/row/envelope/
mod.rs1use reifydb_value::{
5 encoding::LeBytes,
6 error::{Error as ValueError, TypeError},
7 util::cowvec::CowVec,
8 value::datetime::DateTime,
9};
10use thiserror::Error;
11
12use crate::row::{bytes::EncodedBytes, pod::EncodedPodRow, shape::fingerprint::RowShapeFingerprint};
13
14pub const HAS_CREATED_AT: u8 = 1 << 0;
15
16pub const HAS_UPDATED_AT: u8 = 1 << 1;
17
18pub const HAS_TIME: u8 = 1 << 2;
19
20pub const HAS_FINGERPRINT: u8 = 1 << 3;
21
22pub const ENVELOPE_FLAGS_SIZE: usize = 1;
23
24pub const ENVELOPE_FIELD_SIZE: usize = DateTime::ENCODED_SIZE;
25
26#[inline]
27pub const fn header_size(flags: u8) -> usize {
28 ENVELOPE_FLAGS_SIZE + ENVELOPE_FIELD_SIZE * flags.count_ones() as usize
29}
30
31#[inline]
32const fn field_offset(flags: u8, bit: u8) -> usize {
33 ENVELOPE_FLAGS_SIZE + ENVELOPE_FIELD_SIZE * (flags & (bit - 1)).count_ones() as usize
34}
35
36#[derive(Debug, Error, PartialEq)]
37pub enum EnvelopeError {
38 #[error("envelope row is {len} bytes, too short for the {required} byte header its flags declare")]
39 Truncated {
40 len: usize,
41 required: usize,
42 },
43}
44
45impl From<EnvelopeError> for ValueError {
46 fn from(err: EnvelopeError) -> Self {
47 TypeError::SerdeDeserialize {
48 message: err.to_string(),
49 }
50 .into()
51 }
52}
53
54#[repr(transparent)]
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Envelope(EncodedBytes);
57
58impl Envelope {
59 pub fn try_view(row: &EncodedPodRow) -> Result<&Self, EnvelopeError> {
60 let bytes = row.bytes();
61 let len = bytes.len();
62 if len < ENVELOPE_FLAGS_SIZE {
63 return Err(EnvelopeError::Truncated {
64 len,
65 required: ENVELOPE_FLAGS_SIZE,
66 });
67 }
68 let required = header_size(bytes[0]);
69 if len < required {
70 return Err(EnvelopeError::Truncated {
71 len,
72 required,
73 });
74 }
75 Ok(unsafe { &*(bytes as *const EncodedBytes as *const Self) })
78 }
79
80 #[inline]
81 pub fn flags(&self) -> u8 {
82 self.0[0]
83 }
84
85 #[inline]
86 pub fn header_size(&self) -> usize {
87 header_size(self.flags())
88 }
89
90 #[inline]
91 pub fn created_at(&self) -> Option<DateTime> {
92 self.field(HAS_CREATED_AT).map(DateTime::from_le_bytes)
93 }
94
95 #[inline]
96 pub fn updated_at(&self) -> Option<DateTime> {
97 self.field(HAS_UPDATED_AT).map(DateTime::from_le_bytes)
98 }
99
100 #[inline]
101 pub fn time(&self) -> Option<DateTime> {
102 self.field(HAS_TIME).map(DateTime::from_le_bytes)
103 }
104
105 #[inline]
106 pub fn fingerprint(&self) -> Option<RowShapeFingerprint> {
107 self.field(HAS_FINGERPRINT).map(RowShapeFingerprint::from_le_bytes)
108 }
109
110 #[inline]
111 pub fn body(&self) -> &[u8] {
112 &self.0[self.header_size()..]
113 }
114
115 #[inline]
116 fn field(&self, bit: u8) -> Option<[u8; ENVELOPE_FIELD_SIZE]> {
117 let flags = self.flags();
118 if flags & bit == 0 {
119 return None;
120 }
121 let offset = field_offset(flags, bit);
122 Some(self.0[offset..offset + ENVELOPE_FIELD_SIZE]
123 .try_into()
124 .expect("the envelope header is length-checked"))
125 }
126}
127
128#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
129pub struct EnvelopeBuilder {
130 created_at: Option<DateTime>,
131 updated_at: Option<DateTime>,
132 time: Option<DateTime>,
133 fingerprint: Option<RowShapeFingerprint>,
134}
135
136impl EnvelopeBuilder {
137 pub fn new() -> Self {
138 Self::default()
139 }
140
141 pub fn created_at(mut self, created_at: DateTime) -> Self {
142 self.created_at = Some(created_at);
143 self
144 }
145
146 pub fn updated_at(mut self, updated_at: DateTime) -> Self {
147 self.updated_at = Some(updated_at);
148 self
149 }
150
151 pub fn time(mut self, time: DateTime) -> Self {
152 self.time = Some(time);
153 self
154 }
155
156 pub fn fingerprint(mut self, fingerprint: RowShapeFingerprint) -> Self {
157 self.fingerprint = Some(fingerprint);
158 self
159 }
160
161 pub fn flags(&self) -> u8 {
162 let mut flags = 0u8;
163 if self.created_at.is_some() {
164 flags |= HAS_CREATED_AT;
165 }
166 if self.updated_at.is_some() {
167 flags |= HAS_UPDATED_AT;
168 }
169 if self.time.is_some() {
170 flags |= HAS_TIME;
171 }
172 if self.fingerprint.is_some() {
173 flags |= HAS_FINGERPRINT;
174 }
175 flags
176 }
177
178 pub fn build(self, body: &[u8]) -> EncodedPodRow {
179 let flags = self.flags();
180 let mut buffer = Vec::with_capacity(header_size(flags) + body.len());
181 buffer.push(flags);
182 for stamp in [self.created_at, self.updated_at, self.time].into_iter().flatten() {
183 buffer.extend_from_slice(&stamp.to_le_bytes());
184 }
185 if let Some(fingerprint) = self.fingerprint {
186 buffer.extend_from_slice(&fingerprint.to_le_bytes());
187 }
188 buffer.extend_from_slice(body);
189 EncodedPodRow::from(EncodedBytes(CowVec::new(buffer)))
190 }
191}