1use crate::error::{Result, TensogramError};
10
11pub const MAGIC: &[u8; 8] = b"TENSOGRM";
15pub const END_MAGIC: &[u8; 8] = b"39277777";
17pub const FRAME_MAGIC: &[u8; 2] = b"FR";
19pub const FRAME_END: &[u8; 4] = b"ENDF";
21
22pub const PREAMBLE_SIZE: usize = 24;
24pub const FRAME_HEADER_SIZE: usize = 16;
26pub const POSTAMBLE_SIZE: usize = 16;
28pub const DATA_OBJECT_FOOTER_SIZE: usize = 12;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35#[repr(u16)]
36pub enum FrameType {
37 HeaderMetadata = 1,
38 HeaderIndex = 2,
39 HeaderHash = 3,
40 DataObject = 4,
41 FooterHash = 5,
42 FooterIndex = 6,
43 FooterMetadata = 7,
44 PrecederMetadata = 8,
49}
50
51impl FrameType {
52 pub fn from_u16(v: u16) -> Result<Self> {
53 match v {
54 1 => Ok(FrameType::HeaderMetadata),
55 2 => Ok(FrameType::HeaderIndex),
56 3 => Ok(FrameType::HeaderHash),
57 4 => Ok(FrameType::DataObject),
58 5 => Ok(FrameType::FooterHash),
59 6 => Ok(FrameType::FooterIndex),
60 7 => Ok(FrameType::FooterMetadata),
61 8 => Ok(FrameType::PrecederMetadata),
62 _ => Err(TensogramError::Framing(format!("unknown frame type: {v}"))),
63 }
64 }
65}
66
67#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
71pub struct MessageFlags(u16);
72
73impl MessageFlags {
74 pub const HEADER_METADATA: u16 = 1 << 0;
75 pub const FOOTER_METADATA: u16 = 1 << 1;
76 pub const HEADER_INDEX: u16 = 1 << 2;
77 pub const FOOTER_INDEX: u16 = 1 << 3;
78 pub const HEADER_HASHES: u16 = 1 << 4;
79 pub const FOOTER_HASHES: u16 = 1 << 5;
80 pub const PRECEDER_METADATA: u16 = 1 << 6;
82
83 pub fn new(bits: u16) -> Self {
84 Self(bits)
85 }
86
87 pub fn bits(self) -> u16 {
88 self.0
89 }
90
91 pub fn has(self, flag: u16) -> bool {
92 self.0 & flag != 0
93 }
94
95 pub fn set(&mut self, flag: u16) {
96 self.0 |= flag;
97 }
98
99 pub fn has_metadata(self) -> bool {
101 self.has(Self::HEADER_METADATA) || self.has(Self::FOOTER_METADATA)
102 }
103}
104
105pub struct DataObjectFlags;
109
110impl DataObjectFlags {
111 pub const CBOR_AFTER_PAYLOAD: u16 = 1 << 0;
113}
114
115#[derive(Debug, Clone)]
119pub struct Preamble {
120 pub version: u16,
121 pub flags: MessageFlags,
122 pub reserved: u32,
123 pub total_length: u64,
126}
127
128impl Preamble {
129 pub fn read_from(buf: &[u8]) -> Result<Self> {
130 if buf.len() < PREAMBLE_SIZE {
131 return Err(TensogramError::Framing(format!(
132 "buffer too short for preamble: {} < {PREAMBLE_SIZE}",
133 buf.len()
134 )));
135 }
136 if &buf[0..8] != MAGIC {
137 return Err(TensogramError::Framing("invalid magic bytes".to_string()));
138 }
139 let version = read_u16_be(buf, 8);
140 if version < 2 {
141 return Err(TensogramError::Framing(format!(
142 "unsupported message version {version} (versions 0 and 1 are deprecated, minimum is 2)"
143 )));
144 }
145 Ok(Preamble {
146 version,
147 flags: MessageFlags::new(read_u16_be(buf, 10)),
148 reserved: read_u32_be(buf, 12),
149 total_length: read_u64_be(buf, 16),
150 })
151 }
152
153 pub fn write_to(&self, out: &mut Vec<u8>) {
154 out.extend_from_slice(MAGIC);
155 out.extend_from_slice(&self.version.to_be_bytes());
156 out.extend_from_slice(&self.flags.bits().to_be_bytes());
157 out.extend_from_slice(&self.reserved.to_be_bytes());
158 out.extend_from_slice(&self.total_length.to_be_bytes());
159 }
160}
161
162#[derive(Debug, Clone)]
166pub struct FrameHeader {
167 pub frame_type: FrameType,
168 pub version: u16,
169 pub flags: u16,
170 pub total_length: u64,
172}
173
174impl FrameHeader {
175 pub fn read_from(buf: &[u8]) -> Result<Self> {
176 if buf.len() < FRAME_HEADER_SIZE {
177 return Err(TensogramError::Framing(format!(
178 "buffer too short for frame header: {} < {FRAME_HEADER_SIZE}",
179 buf.len()
180 )));
181 }
182 if &buf[0..2] != FRAME_MAGIC {
183 return Err(TensogramError::Framing(format!(
184 "invalid frame magic: {:?}",
185 &buf[0..2]
186 )));
187 }
188 let type_val = read_u16_be(buf, 2);
189 let frame_type = FrameType::from_u16(type_val)?;
190 Ok(FrameHeader {
191 frame_type,
192 version: read_u16_be(buf, 4),
193 flags: read_u16_be(buf, 6),
194 total_length: read_u64_be(buf, 8),
195 })
196 }
197
198 pub fn write_to(&self, out: &mut Vec<u8>) {
199 out.extend_from_slice(FRAME_MAGIC);
200 out.extend_from_slice(&(self.frame_type as u16).to_be_bytes());
201 out.extend_from_slice(&self.version.to_be_bytes());
202 out.extend_from_slice(&self.flags.to_be_bytes());
203 out.extend_from_slice(&self.total_length.to_be_bytes());
204 }
205}
206
207#[derive(Debug, Clone)]
211pub struct Postamble {
212 pub first_footer_offset: u64,
215}
216
217impl Postamble {
218 pub fn read_from(buf: &[u8]) -> Result<Self> {
219 if buf.len() < POSTAMBLE_SIZE {
220 return Err(TensogramError::Framing(format!(
221 "buffer too short for postamble: {} < {POSTAMBLE_SIZE}",
222 buf.len()
223 )));
224 }
225 let first_footer_offset = read_u64_be(buf, 0);
226 if &buf[8..16] != END_MAGIC {
227 return Err(TensogramError::Framing(
228 "invalid end magic in postamble".to_string(),
229 ));
230 }
231 Ok(Postamble {
232 first_footer_offset,
233 })
234 }
235
236 pub fn write_to(&self, out: &mut Vec<u8>) {
237 out.extend_from_slice(&self.first_footer_offset.to_be_bytes());
238 out.extend_from_slice(END_MAGIC);
239 }
240}
241
242pub(crate) fn read_u16_be(buf: &[u8], offset: usize) -> u16 {
250 let mut bytes = [0u8; 2];
251 bytes.copy_from_slice(&buf[offset..offset + 2]);
252 u16::from_be_bytes(bytes)
253}
254
255pub(crate) fn read_u32_be(buf: &[u8], offset: usize) -> u32 {
258 let mut bytes = [0u8; 4];
259 bytes.copy_from_slice(&buf[offset..offset + 4]);
260 u32::from_be_bytes(bytes)
261}
262
263pub(crate) fn read_u64_be(buf: &[u8], offset: usize) -> u64 {
266 let mut bytes = [0u8; 8];
267 bytes.copy_from_slice(&buf[offset..offset + 8]);
268 u64::from_be_bytes(bytes)
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_preamble_round_trip() {
277 let preamble = Preamble {
278 version: 2,
279 flags: MessageFlags::new(MessageFlags::HEADER_METADATA | MessageFlags::HEADER_INDEX),
280 reserved: 0,
281 total_length: 4096,
282 };
283 let mut buf = Vec::new();
284 preamble.write_to(&mut buf);
285 assert_eq!(buf.len(), PREAMBLE_SIZE);
286
287 let parsed = Preamble::read_from(&buf).unwrap();
288 assert_eq!(parsed.version, 2);
289 assert!(parsed.flags.has(MessageFlags::HEADER_METADATA));
290 assert!(parsed.flags.has(MessageFlags::HEADER_INDEX));
291 assert!(!parsed.flags.has(MessageFlags::FOOTER_INDEX));
292 assert_eq!(parsed.total_length, 4096);
293 }
294
295 #[test]
296 fn test_frame_header_round_trip() {
297 let fh = FrameHeader {
298 frame_type: FrameType::DataObject,
299 version: 1,
300 flags: DataObjectFlags::CBOR_AFTER_PAYLOAD,
301 total_length: 1024,
302 };
303 let mut buf = Vec::new();
304 fh.write_to(&mut buf);
305 assert_eq!(buf.len(), FRAME_HEADER_SIZE);
306
307 let parsed = FrameHeader::read_from(&buf).unwrap();
308 assert_eq!(parsed.frame_type, FrameType::DataObject);
309 assert_eq!(parsed.version, 1);
310 assert_eq!(parsed.flags, DataObjectFlags::CBOR_AFTER_PAYLOAD);
311 assert_eq!(parsed.total_length, 1024);
312 }
313
314 #[test]
315 fn test_postamble_round_trip() {
316 let pa = Postamble {
317 first_footer_offset: 8192,
318 };
319 let mut buf = Vec::new();
320 pa.write_to(&mut buf);
321 assert_eq!(buf.len(), POSTAMBLE_SIZE);
322
323 let parsed = Postamble::read_from(&buf).unwrap();
324 assert_eq!(parsed.first_footer_offset, 8192);
325 }
326
327 #[test]
328 fn test_invalid_magic() {
329 let buf = vec![0u8; PREAMBLE_SIZE];
330 assert!(Preamble::read_from(&buf).is_err());
331 }
332
333 #[test]
334 fn test_invalid_frame_magic() {
335 let buf = vec![0u8; FRAME_HEADER_SIZE];
336 assert!(FrameHeader::read_from(&buf).is_err());
337 }
338
339 #[test]
340 fn test_invalid_end_magic() {
341 let mut buf = vec![0u8; POSTAMBLE_SIZE];
342 buf[0..8].copy_from_slice(&100u64.to_be_bytes());
344 assert!(Postamble::read_from(&buf).is_err());
345 }
346
347 #[test]
348 fn test_frame_type_parse() {
349 assert_eq!(FrameType::from_u16(1).unwrap(), FrameType::HeaderMetadata);
350 assert_eq!(FrameType::from_u16(4).unwrap(), FrameType::DataObject);
351 assert_eq!(FrameType::from_u16(7).unwrap(), FrameType::FooterMetadata);
352 assert_eq!(FrameType::from_u16(8).unwrap(), FrameType::PrecederMetadata);
353 assert!(FrameType::from_u16(0).is_err());
354 assert!(FrameType::from_u16(9).is_err());
355 }
356
357 #[test]
358 fn test_message_flags() {
359 let mut flags = MessageFlags::default();
360 assert!(!flags.has_metadata());
361
362 flags.set(MessageFlags::HEADER_METADATA);
363 assert!(flags.has_metadata());
364 assert!(flags.has(MessageFlags::HEADER_METADATA));
365 assert!(!flags.has(MessageFlags::FOOTER_METADATA));
366
367 flags.set(MessageFlags::FOOTER_INDEX);
368 assert!(flags.has(MessageFlags::FOOTER_INDEX));
369 }
370
371 #[test]
372 fn test_preceder_metadata_flag() {
373 let mut flags = MessageFlags::default();
374 assert!(!flags.has(MessageFlags::PRECEDER_METADATA));
375
376 flags.set(MessageFlags::PRECEDER_METADATA);
377 assert!(flags.has(MessageFlags::PRECEDER_METADATA));
378 assert_eq!(flags.bits() & (1 << 6), 1 << 6);
379 }
380
381 #[test]
382 fn test_preceder_metadata_frame_header_round_trip() {
383 let fh = FrameHeader {
384 frame_type: FrameType::PrecederMetadata,
385 version: 1,
386 flags: 0,
387 total_length: 256,
388 };
389 let mut buf = Vec::new();
390 fh.write_to(&mut buf);
391 assert_eq!(buf.len(), FRAME_HEADER_SIZE);
392
393 let parsed = FrameHeader::read_from(&buf).unwrap();
394 assert_eq!(parsed.frame_type, FrameType::PrecederMetadata);
395 assert_eq!(parsed.version, 1);
396 assert_eq!(parsed.flags, 0);
397 assert_eq!(parsed.total_length, 256);
398 }
399
400 #[test]
401 fn test_truncated_preamble() {
402 let buf = vec![0u8; 10];
403 assert!(Preamble::read_from(&buf).is_err());
404 }
405}