1use crate::format::checksum::checksum_metadata;
33use crate::format::{FormatError, FormatResult};
34
35pub const OHDR_SIGNATURE: [u8; 4] = *b"OHDR";
37
38pub const OHDR_VERSION: u8 = 2;
40
41const FLAG_SIZE_MASK: u8 = 0x03;
43const FLAG_ATTR_CREATION_ORDER_TRACKED: u8 = 0x04;
44const FLAG_NON_DEFAULT_ATTR_THRESHOLDS: u8 = 0x10;
46const FLAG_STORE_TIMESTAMPS: u8 = 0x20;
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct ObjectHeaderMessage {
51 pub msg_type: u8,
53 pub flags: u8,
55 pub data: Vec<u8>,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ObjectHeader {
62 pub flags: u8,
65 pub messages: Vec<ObjectHeaderMessage>,
67}
68
69impl ObjectHeader {
70 pub fn new() -> Self {
75 Self {
76 flags: 0x02, messages: Vec::new(),
78 }
79 }
80
81 pub fn add_message(&mut self, msg_type: u8, flags: u8, data: Vec<u8>) {
83 self.messages.push(ObjectHeaderMessage {
84 msg_type,
85 flags,
86 data,
87 });
88 }
89
90 fn chunk0_size_bytes(&self) -> usize {
93 match self.flags & FLAG_SIZE_MASK {
94 0 => 1,
95 1 => 2,
96 2 => 4,
97 3 => 8,
98 _ => unreachable!(),
99 }
100 }
101
102 fn has_creation_order(&self) -> bool {
104 self.flags & FLAG_ATTR_CREATION_ORDER_TRACKED != 0
105 }
106
107 fn messages_data_size(&self) -> usize {
109 let per_msg_overhead = if self.has_creation_order() {
110 1 + 2 + 1 + 2 } else {
112 1 + 2 + 1 };
114 self.messages
115 .iter()
116 .map(|m| per_msg_overhead + m.data.len())
117 .sum()
118 }
119
120 pub fn encode(&self) -> Vec<u8> {
123 let messages_size = self.messages_data_size();
124
125 let mut prefix_size: usize = 4 + 1 + 1; if self.flags & FLAG_STORE_TIMESTAMPS != 0 {
128 prefix_size += 16; }
130 if self.flags & FLAG_NON_DEFAULT_ATTR_THRESHOLDS != 0 {
131 prefix_size += 4; }
133 prefix_size += self.chunk0_size_bytes(); let total = prefix_size + messages_size + 4; let mut buf = Vec::with_capacity(total);
137
138 buf.extend_from_slice(&OHDR_SIGNATURE);
140 buf.push(OHDR_VERSION);
142 buf.push(self.flags);
144
145 if self.flags & FLAG_STORE_TIMESTAMPS != 0 {
147 buf.extend_from_slice(&[0u8; 16]);
148 }
149
150 if self.flags & FLAG_NON_DEFAULT_ATTR_THRESHOLDS != 0 {
152 buf.extend_from_slice(&8u16.to_le_bytes());
154 buf.extend_from_slice(&6u16.to_le_bytes());
155 }
156
157 let chunk0_data_size = messages_size as u64;
159 let csb = self.chunk0_size_bytes();
160 buf.extend_from_slice(&chunk0_data_size.to_le_bytes()[..csb]);
161
162 for msg in &self.messages {
164 buf.push(msg.msg_type);
165 buf.extend_from_slice(&(msg.data.len() as u16).to_le_bytes());
166 buf.push(msg.flags);
167 if self.has_creation_order() {
168 buf.extend_from_slice(&0u16.to_le_bytes());
171 }
172 buf.extend_from_slice(&msg.data);
173 }
174
175 let cksum = checksum_metadata(&buf);
177 buf.extend_from_slice(&cksum.to_le_bytes());
178
179 debug_assert_eq!(buf.len(), total);
180 buf
181 }
182
183 pub fn decode(buf: &[u8]) -> FormatResult<(Self, usize)> {
186 if buf.len() < 11 {
188 return Err(FormatError::BufferTooShort {
189 needed: 11,
190 available: buf.len(),
191 });
192 }
193
194 if buf[0..4] != OHDR_SIGNATURE {
196 return Err(FormatError::InvalidSignature);
197 }
198
199 let version = buf[4];
201 if version != OHDR_VERSION {
202 return Err(FormatError::InvalidVersion(version));
203 }
204
205 let flags = buf[5];
206 let mut pos: usize = 6;
207
208 if flags & FLAG_STORE_TIMESTAMPS != 0 {
210 if buf.len() < pos + 16 {
211 return Err(FormatError::BufferTooShort {
212 needed: pos + 16,
213 available: buf.len(),
214 });
215 }
216 pos += 16;
218 }
219
220 if flags & FLAG_NON_DEFAULT_ATTR_THRESHOLDS != 0 {
222 if buf.len() < pos + 4 {
223 return Err(FormatError::BufferTooShort {
224 needed: pos + 4,
225 available: buf.len(),
226 });
227 }
228 pos += 4;
230 }
231
232 let chunk0_size_bytes = match flags & FLAG_SIZE_MASK {
234 0 => 1,
235 1 => 2,
236 2 => 4,
237 3 => 8,
238 _ => unreachable!(),
239 };
240
241 if buf.len() < pos + chunk0_size_bytes {
242 return Err(FormatError::BufferTooShort {
243 needed: pos + chunk0_size_bytes,
244 available: buf.len(),
245 });
246 }
247
248 let mut size_bytes = [0u8; 8];
249 size_bytes[..chunk0_size_bytes].copy_from_slice(&buf[pos..pos + chunk0_size_bytes]);
250 let chunk0_data_size = u64::from_le_bytes(size_bytes) as usize;
251 pos += chunk0_size_bytes;
252
253 let total_consumed = pos + chunk0_data_size + 4;
255 if buf.len() < total_consumed {
256 return Err(FormatError::BufferTooShort {
257 needed: total_consumed,
258 available: buf.len(),
259 });
260 }
261
262 let data_end = total_consumed - 4;
265 let stored_cksum = u32::from_le_bytes([
266 buf[data_end],
267 buf[data_end + 1],
268 buf[data_end + 2],
269 buf[data_end + 3],
270 ]);
271 let computed_cksum = checksum_metadata(&buf[..data_end]);
272 if stored_cksum != computed_cksum {
273 return Err(FormatError::ChecksumMismatch {
274 expected: stored_cksum,
275 computed: computed_cksum,
276 });
277 }
278
279 let has_creation_order = flags & FLAG_ATTR_CREATION_ORDER_TRACKED != 0;
281 let messages_end = pos + chunk0_data_size;
282 let mut messages = Vec::new();
283
284 while pos < messages_end {
285 let msg_header_size = if has_creation_order { 6 } else { 4 };
287 if pos + msg_header_size > messages_end {
288 return Err(FormatError::InvalidData(
289 "truncated message header in object header".into(),
290 ));
291 }
292
293 let msg_type = buf[pos];
294 let msg_data_size = u16::from_le_bytes([buf[pos + 1], buf[pos + 2]]) as usize;
295 let msg_flags = buf[pos + 3];
296 pos += 4;
297
298 if has_creation_order {
299 pos += 2;
301 }
302
303 if pos + msg_data_size > messages_end {
304 return Err(FormatError::InvalidData(format!(
305 "message data ({} bytes) extends past chunk0 boundary",
306 msg_data_size
307 )));
308 }
309
310 let data = buf[pos..pos + msg_data_size].to_vec();
311 pos += msg_data_size;
312
313 messages.push(ObjectHeaderMessage {
314 msg_type,
315 flags: msg_flags,
316 data,
317 });
318 }
319
320 Ok((ObjectHeader { flags, messages }, total_consumed))
321 }
322}
323
324impl Default for ObjectHeader {
325 fn default() -> Self {
326 Self::new()
327 }
328}
329
330impl ObjectHeader {
335 pub fn decode_v1(buf: &[u8]) -> FormatResult<(Self, usize)> {
352 if buf.len() < 16 {
355 return Err(FormatError::BufferTooShort {
356 needed: 16,
357 available: buf.len(),
358 });
359 }
360
361 let version = buf[0];
362 if version != 1 {
363 return Err(FormatError::InvalidVersion(version));
364 }
365
366 let num_messages = u16::from_le_bytes([buf[2], buf[3]]) as usize;
368 let _obj_ref_count = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
369 let header_data_size = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]) as usize;
370 let total_consumed = 16 + header_data_size;
373 if buf.len() < total_consumed {
374 return Err(FormatError::BufferTooShort {
375 needed: total_consumed,
376 available: buf.len(),
377 });
378 }
379
380 let msg_data_start = 16; let mut pos = msg_data_start;
382 let messages_end = msg_data_start + header_data_size;
383 let mut messages = Vec::with_capacity(num_messages);
384
385 for _ in 0..num_messages {
386 if pos + 8 > messages_end {
387 break; }
389
390 let msg_type = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
391 let data_size = u16::from_le_bytes([buf[pos + 2], buf[pos + 3]]) as usize;
392 let msg_flags = buf[pos + 4];
393 pos += 8;
395
396 if pos + data_size > messages_end {
397 return Err(FormatError::InvalidData(format!(
398 "v1 message data ({} bytes) extends past header boundary",
399 data_size
400 )));
401 }
402
403 let data = buf[pos..pos + data_size].to_vec();
404 pos += data_size;
405
406 let rel = pos - msg_data_start;
409 let aligned_rel = (rel + 7) & !7;
410 let aligned_pos = msg_data_start + aligned_rel;
411 if aligned_pos <= messages_end {
412 pos = aligned_pos;
413 }
414
415 if msg_type == 0 {
417 continue;
418 }
419
420 messages.push(ObjectHeaderMessage {
421 msg_type: msg_type as u8,
422 flags: msg_flags,
423 data,
424 });
425 }
426
427 Ok((
428 ObjectHeader {
429 flags: 0x02, messages,
431 },
432 total_consumed,
433 ))
434 }
435
436 pub fn decode_any(buf: &[u8]) -> FormatResult<(Self, usize)> {
440 if buf.len() >= 4 && buf[0..4] == OHDR_SIGNATURE {
441 Self::decode(buf)
442 } else if !buf.is_empty() && buf[0] == 1 {
443 Self::decode_v1(buf)
444 } else {
445 Self::decode(buf)
447 }
448 }
449}
450
451#[cfg(test)]
452mod tests_v1 {
453 use super::*;
454
455 fn build_v1_header(messages: &[(u16, u8, &[u8])]) -> Vec<u8> {
457 let mut msg_data = Vec::new();
458 for (msg_type, flags, data) in messages {
459 msg_data.extend_from_slice(&msg_type.to_le_bytes());
460 msg_data.extend_from_slice(&(data.len() as u16).to_le_bytes());
461 msg_data.push(*flags);
462 msg_data.extend_from_slice(&[0u8; 3]); msg_data.extend_from_slice(data);
464 let aligned = (msg_data.len() + 7) & !7;
466 msg_data.resize(aligned, 0);
467 }
468
469 let mut buf = Vec::new();
470 buf.push(1); buf.push(0); buf.extend_from_slice(&(messages.len() as u16).to_le_bytes());
473 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&(msg_data.len() as u32).to_le_bytes());
475 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&msg_data);
477 buf
478 }
479
480 #[test]
481 fn test_decode_v1_empty() {
482 let buf = build_v1_header(&[]);
483 let (hdr, consumed) = ObjectHeader::decode_v1(&buf).unwrap();
484 assert_eq!(consumed, 16); assert!(hdr.messages.is_empty());
486 }
487
488 #[test]
489 fn test_decode_v1_single_message() {
490 let data = vec![0xAA, 0xBB, 0xCC];
491 let buf = build_v1_header(&[(0x03, 0x00, &data)]);
492 let (hdr, _consumed) = ObjectHeader::decode_v1(&buf).unwrap();
493 assert_eq!(hdr.messages.len(), 1);
494 assert_eq!(hdr.messages[0].msg_type, 0x03);
495 assert_eq!(hdr.messages[0].data, data);
496 }
497
498 #[test]
499 fn test_decode_v1_multiple_messages() {
500 let buf = build_v1_header(&[
501 (0x01, 0x00, &[1, 2, 3, 4]),
502 (0x03, 0x01, &[10, 20]),
503 (0x08, 0x00, &[0xFF; 16]),
504 ]);
505 let (hdr, _) = ObjectHeader::decode_v1(&buf).unwrap();
506 assert_eq!(hdr.messages.len(), 3);
507 assert_eq!(hdr.messages[0].msg_type, 0x01);
508 assert_eq!(hdr.messages[1].msg_type, 0x03);
509 assert_eq!(hdr.messages[2].msg_type, 0x08);
510 assert_eq!(hdr.messages[2].data, vec![0xFF; 16]);
511 }
512
513 #[test]
514 fn test_decode_v1_skips_null_messages() {
515 let buf = build_v1_header(&[
516 (0x00, 0x00, &[0; 8]), (0x03, 0x00, &[1, 2]),
518 ]);
519 let (hdr, _) = ObjectHeader::decode_v1(&buf).unwrap();
520 assert_eq!(hdr.messages.len(), 1);
521 assert_eq!(hdr.messages[0].msg_type, 0x03);
522 }
523
524 #[test]
525 fn test_decode_any_v2() {
526 let mut hdr = ObjectHeader::new();
527 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
528 let encoded = hdr.encode();
529 let (decoded, _) = ObjectHeader::decode_any(&encoded).unwrap();
530 assert_eq!(decoded.messages.len(), 1);
531 }
532
533 #[test]
534 fn test_decode_any_v1() {
535 let buf = build_v1_header(&[(0x03, 0x00, &[1, 2])]);
536 let (decoded, _) = ObjectHeader::decode_any(&buf).unwrap();
537 assert_eq!(decoded.messages.len(), 1);
538 assert_eq!(decoded.messages[0].msg_type, 0x03);
539 }
540
541 #[test]
542 fn test_decode_v1_bad_version() {
543 let mut buf = build_v1_header(&[]);
544 buf[0] = 5;
545 assert!(matches!(
546 ObjectHeader::decode_v1(&buf).unwrap_err(),
547 FormatError::InvalidVersion(5)
548 ));
549 }
550
551 #[test]
552 fn test_decode_v1_buffer_too_short() {
553 assert!(matches!(
554 ObjectHeader::decode_v1(&[1, 0, 0]).unwrap_err(),
555 FormatError::BufferTooShort { .. }
556 ));
557 }
558}
559
560#[cfg(test)]
561mod tests {
562 use super::*;
563
564 #[test]
565 fn test_empty_header_roundtrip() {
566 let hdr = ObjectHeader::new();
567 let encoded = hdr.encode();
568
569 assert_eq!(encoded.len(), 14);
571 assert_eq!(&encoded[..4], b"OHDR");
572 assert_eq!(encoded[4], 2); let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
575 assert_eq!(consumed, encoded.len());
576 assert_eq!(decoded, hdr);
577 }
578
579 #[test]
580 fn test_single_message_roundtrip() {
581 let mut hdr = ObjectHeader::new();
582 hdr.add_message(0x01, 0x00, vec![0xAA, 0xBB, 0xCC]);
583
584 let encoded = hdr.encode();
585 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
586 assert_eq!(consumed, encoded.len());
587 assert_eq!(decoded.messages.len(), 1);
588 assert_eq!(decoded.messages[0].msg_type, 0x01);
589 assert_eq!(decoded.messages[0].flags, 0x00);
590 assert_eq!(decoded.messages[0].data, vec![0xAA, 0xBB, 0xCC]);
591 }
592
593 #[test]
594 fn test_multiple_messages_roundtrip() {
595 let mut hdr = ObjectHeader::new();
596 hdr.add_message(0x01, 0x00, vec![1, 2, 3, 4]);
597 hdr.add_message(0x03, 0x01, vec![10, 20]);
598 hdr.add_message(0x0C, 0x00, vec![]);
599
600 let encoded = hdr.encode();
601 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
602 assert_eq!(consumed, encoded.len());
603 assert_eq!(decoded.messages.len(), 3);
604 assert_eq!(decoded, hdr);
605 }
606
607 #[test]
608 fn test_with_creation_order() {
609 let mut hdr = ObjectHeader {
610 flags: 0x02 | FLAG_ATTR_CREATION_ORDER_TRACKED,
611 messages: Vec::new(),
612 };
613 hdr.add_message(0x01, 0x00, vec![0xFF; 8]);
614 hdr.add_message(0x03, 0x00, vec![0xEE; 4]);
615
616 let encoded = hdr.encode();
617 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
618 assert_eq!(consumed, encoded.len());
619 assert_eq!(decoded.messages.len(), 2);
620 assert_eq!(decoded.messages[0].data, vec![0xFF; 8]);
621 assert_eq!(decoded.messages[1].data, vec![0xEE; 4]);
622 }
623
624 #[test]
625 fn test_chunk0_size_1byte() {
626 let mut hdr = ObjectHeader {
628 flags: 0x00,
629 messages: Vec::new(),
630 };
631 hdr.add_message(0x01, 0x00, vec![42]);
632
633 let encoded = hdr.encode();
634 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
635 assert_eq!(consumed, encoded.len());
636 assert_eq!(decoded.messages[0].data, vec![42]);
637 }
638
639 #[test]
640 fn test_chunk0_size_2byte() {
641 let mut hdr = ObjectHeader {
643 flags: 0x01,
644 messages: Vec::new(),
645 };
646 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
647
648 let encoded = hdr.encode();
649 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
650 assert_eq!(consumed, encoded.len());
651 assert_eq!(decoded.messages[0].data, vec![1, 2, 3]);
652 }
653
654 #[test]
655 fn test_chunk0_size_8byte() {
656 let mut hdr = ObjectHeader {
658 flags: 0x03,
659 messages: Vec::new(),
660 };
661 hdr.add_message(0x01, 0x00, vec![0xDE, 0xAD]);
662
663 let encoded = hdr.encode();
664 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
665 assert_eq!(consumed, encoded.len());
666 assert_eq!(decoded.messages[0].data, vec![0xDE, 0xAD]);
667 }
668
669 #[test]
670 fn test_decode_bad_signature() {
671 let mut data = vec![0u8; 20];
672 data[0..4].copy_from_slice(b"XHDR");
673 let err = ObjectHeader::decode(&data).unwrap_err();
674 assert!(matches!(err, FormatError::InvalidSignature));
675 }
676
677 #[test]
678 fn test_decode_bad_version() {
679 let hdr = ObjectHeader::new();
680 let mut encoded = hdr.encode();
681 encoded[4] = 99; let err = ObjectHeader::decode(&encoded).unwrap_err();
683 assert!(matches!(err, FormatError::InvalidVersion(99)));
684 }
685
686 #[test]
687 fn test_decode_checksum_mismatch() {
688 let mut hdr = ObjectHeader::new();
689 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
690 let mut encoded = hdr.encode();
691 let last_data = encoded.len() - 5;
693 encoded[last_data] ^= 0xFF;
694 let err = ObjectHeader::decode(&encoded).unwrap_err();
695 assert!(matches!(err, FormatError::ChecksumMismatch { .. }));
696 }
697
698 #[test]
699 fn test_decode_buffer_too_short() {
700 let err = ObjectHeader::decode(&[0u8; 5]).unwrap_err();
701 assert!(matches!(err, FormatError::BufferTooShort { .. }));
702 }
703
704 #[test]
705 fn test_decode_with_trailing_data() {
706 let mut hdr = ObjectHeader::new();
707 hdr.add_message(0x01, 0x00, vec![7, 8, 9]);
708 let mut encoded = hdr.encode();
709 let original_len = encoded.len();
710 encoded.extend_from_slice(&[0xBB; 50]); let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
713 assert_eq!(consumed, original_len);
714 assert_eq!(decoded, hdr);
715 }
716
717 #[test]
718 fn test_large_message_payload() {
719 let mut hdr = ObjectHeader::new();
720 let big_data = vec![0x42; 1000];
721 hdr.add_message(0x0C, 0x00, big_data.clone());
722
723 let encoded = hdr.encode();
724 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
725 assert_eq!(consumed, encoded.len());
726 assert_eq!(decoded.messages[0].data.len(), 1000);
727 assert_eq!(decoded.messages[0].data, big_data);
728 }
729
730 #[test]
731 fn test_default() {
732 let hdr = ObjectHeader::default();
733 assert_eq!(hdr.flags, 0x02);
734 assert!(hdr.messages.is_empty());
735 }
736}