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 chunk0_data_size =
249 crate::format::bytes::read_le_uint(&buf[pos..], chunk0_size_bytes) as usize;
250 pos += chunk0_size_bytes;
251
252 let total_consumed = pos
257 .checked_add(chunk0_data_size)
258 .and_then(|x| x.checked_add(4))
259 .ok_or_else(|| {
260 FormatError::InvalidData("object header chunk-0 size overflows usize".into())
261 })?;
262 if buf.len() < total_consumed {
263 return Err(FormatError::BufferTooShort {
264 needed: total_consumed,
265 available: buf.len(),
266 });
267 }
268
269 let data_end = total_consumed - 4;
272 let stored_cksum = u32::from_le_bytes([
273 buf[data_end],
274 buf[data_end + 1],
275 buf[data_end + 2],
276 buf[data_end + 3],
277 ]);
278 let computed_cksum = checksum_metadata(&buf[..data_end]);
279 if stored_cksum != computed_cksum {
280 return Err(FormatError::ChecksumMismatch {
281 expected: stored_cksum,
282 computed: computed_cksum,
283 });
284 }
285
286 let has_creation_order = flags & FLAG_ATTR_CREATION_ORDER_TRACKED != 0;
288 let messages_end = pos + chunk0_data_size;
289 let mut messages = Vec::new();
290
291 while pos < messages_end {
292 let msg_header_size = if has_creation_order { 6 } else { 4 };
294 if pos + msg_header_size > messages_end {
295 break;
299 }
300
301 let msg_type = buf[pos];
302 let msg_data_size = u16::from_le_bytes([buf[pos + 1], buf[pos + 2]]) as usize;
303 let msg_flags = buf[pos + 3];
304 pos += 4;
305
306 if has_creation_order {
307 pos += 2;
309 }
310
311 if pos + msg_data_size > messages_end {
312 return Err(FormatError::InvalidData(format!(
313 "message data ({} bytes) extends past chunk0 boundary",
314 msg_data_size
315 )));
316 }
317
318 let data = buf[pos..pos + msg_data_size].to_vec();
319 pos += msg_data_size;
320
321 messages.push(ObjectHeaderMessage {
322 msg_type,
323 flags: msg_flags,
324 data,
325 });
326 }
327
328 Ok((ObjectHeader { flags, messages }, total_consumed))
329 }
330}
331
332impl Default for ObjectHeader {
333 fn default() -> Self {
334 Self::new()
335 }
336}
337
338impl ObjectHeader {
343 pub fn decode_v1(buf: &[u8]) -> FormatResult<(Self, usize)> {
360 if buf.len() < 16 {
363 return Err(FormatError::BufferTooShort {
364 needed: 16,
365 available: buf.len(),
366 });
367 }
368
369 let version = buf[0];
370 if version != 1 {
371 return Err(FormatError::InvalidVersion(version));
372 }
373
374 let num_messages = u16::from_le_bytes([buf[2], buf[3]]) as usize;
376 let _obj_ref_count = u32::from_le_bytes([buf[4], buf[5], buf[6], buf[7]]);
377 let header_data_size = u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]) as usize;
378 let total_consumed = 16 + header_data_size;
381 if buf.len() < total_consumed {
382 return Err(FormatError::BufferTooShort {
383 needed: total_consumed,
384 available: buf.len(),
385 });
386 }
387
388 let msg_data_start = 16; let mut pos = msg_data_start;
390 let messages_end = msg_data_start + header_data_size;
391 let mut messages = Vec::with_capacity(num_messages);
392
393 for _ in 0..num_messages {
394 if pos + 8 > messages_end {
395 break; }
397
398 let msg_type = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
399 let data_size = u16::from_le_bytes([buf[pos + 2], buf[pos + 3]]) as usize;
400 let msg_flags = buf[pos + 4];
401 pos += 8;
403
404 if pos + data_size > messages_end {
405 return Err(FormatError::InvalidData(format!(
406 "v1 message data ({} bytes) extends past header boundary",
407 data_size
408 )));
409 }
410
411 let data = buf[pos..pos + data_size].to_vec();
412 pos += data_size;
413
414 let rel = pos - msg_data_start;
417 let aligned_rel = (rel + 7) & !7;
418 let aligned_pos = msg_data_start + aligned_rel;
419 if aligned_pos <= messages_end {
420 pos = aligned_pos;
421 }
422
423 if msg_type == 0 {
425 continue;
426 }
427
428 messages.push(ObjectHeaderMessage {
429 msg_type: msg_type as u8,
430 flags: msg_flags,
431 data,
432 });
433 }
434
435 Ok((
436 ObjectHeader {
437 flags: 0x02, messages,
439 },
440 total_consumed,
441 ))
442 }
443
444 pub fn decode_any(buf: &[u8]) -> FormatResult<(Self, usize)> {
448 if buf.len() >= 4 && buf[0..4] == OHDR_SIGNATURE {
449 Self::decode(buf)
450 } else if !buf.is_empty() && buf[0] == 1 {
451 Self::decode_v1(buf)
452 } else {
453 Self::decode(buf)
455 }
456 }
457}
458
459#[cfg(test)]
460mod tests_v1 {
461 use super::*;
462
463 fn build_v1_header(messages: &[(u16, u8, &[u8])]) -> Vec<u8> {
465 let mut msg_data = Vec::new();
466 for (msg_type, flags, data) in messages {
467 msg_data.extend_from_slice(&msg_type.to_le_bytes());
468 msg_data.extend_from_slice(&(data.len() as u16).to_le_bytes());
469 msg_data.push(*flags);
470 msg_data.extend_from_slice(&[0u8; 3]); msg_data.extend_from_slice(data);
472 let aligned = (msg_data.len() + 7) & !7;
474 msg_data.resize(aligned, 0);
475 }
476
477 let mut buf = Vec::new();
478 buf.push(1); buf.push(0); buf.extend_from_slice(&(messages.len() as u16).to_le_bytes());
481 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&(msg_data.len() as u32).to_le_bytes());
483 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&msg_data);
485 buf
486 }
487
488 #[test]
489 fn test_decode_v1_empty() {
490 let buf = build_v1_header(&[]);
491 let (hdr, consumed) = ObjectHeader::decode_v1(&buf).unwrap();
492 assert_eq!(consumed, 16); assert!(hdr.messages.is_empty());
494 }
495
496 #[test]
497 fn test_decode_v1_single_message() {
498 let data = vec![0xAA, 0xBB, 0xCC];
499 let buf = build_v1_header(&[(0x03, 0x00, &data)]);
500 let (hdr, _consumed) = ObjectHeader::decode_v1(&buf).unwrap();
501 assert_eq!(hdr.messages.len(), 1);
502 assert_eq!(hdr.messages[0].msg_type, 0x03);
503 assert_eq!(hdr.messages[0].data, data);
504 }
505
506 #[test]
507 fn test_decode_v1_multiple_messages() {
508 let buf = build_v1_header(&[
509 (0x01, 0x00, &[1, 2, 3, 4]),
510 (0x03, 0x01, &[10, 20]),
511 (0x08, 0x00, &[0xFF; 16]),
512 ]);
513 let (hdr, _) = ObjectHeader::decode_v1(&buf).unwrap();
514 assert_eq!(hdr.messages.len(), 3);
515 assert_eq!(hdr.messages[0].msg_type, 0x01);
516 assert_eq!(hdr.messages[1].msg_type, 0x03);
517 assert_eq!(hdr.messages[2].msg_type, 0x08);
518 assert_eq!(hdr.messages[2].data, vec![0xFF; 16]);
519 }
520
521 #[test]
522 fn test_decode_v1_skips_null_messages() {
523 let buf = build_v1_header(&[
524 (0x00, 0x00, &[0; 8]), (0x03, 0x00, &[1, 2]),
526 ]);
527 let (hdr, _) = ObjectHeader::decode_v1(&buf).unwrap();
528 assert_eq!(hdr.messages.len(), 1);
529 assert_eq!(hdr.messages[0].msg_type, 0x03);
530 }
531
532 #[test]
533 fn test_decode_any_v2() {
534 let mut hdr = ObjectHeader::new();
535 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
536 let encoded = hdr.encode();
537 let (decoded, _) = ObjectHeader::decode_any(&encoded).unwrap();
538 assert_eq!(decoded.messages.len(), 1);
539 }
540
541 #[test]
542 fn test_decode_any_v1() {
543 let buf = build_v1_header(&[(0x03, 0x00, &[1, 2])]);
544 let (decoded, _) = ObjectHeader::decode_any(&buf).unwrap();
545 assert_eq!(decoded.messages.len(), 1);
546 assert_eq!(decoded.messages[0].msg_type, 0x03);
547 }
548
549 #[test]
550 fn test_decode_v1_bad_version() {
551 let mut buf = build_v1_header(&[]);
552 buf[0] = 5;
553 assert!(matches!(
554 ObjectHeader::decode_v1(&buf).unwrap_err(),
555 FormatError::InvalidVersion(5)
556 ));
557 }
558
559 #[test]
560 fn test_decode_v1_buffer_too_short() {
561 assert!(matches!(
562 ObjectHeader::decode_v1(&[1, 0, 0]).unwrap_err(),
563 FormatError::BufferTooShort { .. }
564 ));
565 }
566}
567
568#[cfg(test)]
569mod tests {
570 use super::*;
571
572 #[test]
573 fn test_empty_header_roundtrip() {
574 let hdr = ObjectHeader::new();
575 let encoded = hdr.encode();
576
577 assert_eq!(encoded.len(), 14);
579 assert_eq!(&encoded[..4], b"OHDR");
580 assert_eq!(encoded[4], 2); let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
583 assert_eq!(consumed, encoded.len());
584 assert_eq!(decoded, hdr);
585 }
586
587 #[test]
588 fn test_single_message_roundtrip() {
589 let mut hdr = ObjectHeader::new();
590 hdr.add_message(0x01, 0x00, vec![0xAA, 0xBB, 0xCC]);
591
592 let encoded = hdr.encode();
593 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
594 assert_eq!(consumed, encoded.len());
595 assert_eq!(decoded.messages.len(), 1);
596 assert_eq!(decoded.messages[0].msg_type, 0x01);
597 assert_eq!(decoded.messages[0].flags, 0x00);
598 assert_eq!(decoded.messages[0].data, vec![0xAA, 0xBB, 0xCC]);
599 }
600
601 #[test]
602 fn test_multiple_messages_roundtrip() {
603 let mut hdr = ObjectHeader::new();
604 hdr.add_message(0x01, 0x00, vec![1, 2, 3, 4]);
605 hdr.add_message(0x03, 0x01, vec![10, 20]);
606 hdr.add_message(0x0C, 0x00, vec![]);
607
608 let encoded = hdr.encode();
609 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
610 assert_eq!(consumed, encoded.len());
611 assert_eq!(decoded.messages.len(), 3);
612 assert_eq!(decoded, hdr);
613 }
614
615 #[test]
616 fn test_with_creation_order() {
617 let mut hdr = ObjectHeader {
618 flags: 0x02 | FLAG_ATTR_CREATION_ORDER_TRACKED,
619 messages: Vec::new(),
620 };
621 hdr.add_message(0x01, 0x00, vec![0xFF; 8]);
622 hdr.add_message(0x03, 0x00, vec![0xEE; 4]);
623
624 let encoded = hdr.encode();
625 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
626 assert_eq!(consumed, encoded.len());
627 assert_eq!(decoded.messages.len(), 2);
628 assert_eq!(decoded.messages[0].data, vec![0xFF; 8]);
629 assert_eq!(decoded.messages[1].data, vec![0xEE; 4]);
630 }
631
632 #[test]
633 fn test_chunk0_size_1byte() {
634 let mut hdr = ObjectHeader {
636 flags: 0x00,
637 messages: Vec::new(),
638 };
639 hdr.add_message(0x01, 0x00, vec![42]);
640
641 let encoded = hdr.encode();
642 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
643 assert_eq!(consumed, encoded.len());
644 assert_eq!(decoded.messages[0].data, vec![42]);
645 }
646
647 #[test]
648 fn test_chunk0_size_2byte() {
649 let mut hdr = ObjectHeader {
651 flags: 0x01,
652 messages: Vec::new(),
653 };
654 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
655
656 let encoded = hdr.encode();
657 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
658 assert_eq!(consumed, encoded.len());
659 assert_eq!(decoded.messages[0].data, vec![1, 2, 3]);
660 }
661
662 #[test]
663 fn test_chunk0_size_8byte() {
664 let mut hdr = ObjectHeader {
666 flags: 0x03,
667 messages: Vec::new(),
668 };
669 hdr.add_message(0x01, 0x00, vec![0xDE, 0xAD]);
670
671 let encoded = hdr.encode();
672 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
673 assert_eq!(consumed, encoded.len());
674 assert_eq!(decoded.messages[0].data, vec![0xDE, 0xAD]);
675 }
676
677 #[test]
678 fn test_decode_bad_signature() {
679 let mut data = vec![0u8; 20];
680 data[0..4].copy_from_slice(b"XHDR");
681 let err = ObjectHeader::decode(&data).unwrap_err();
682 assert!(matches!(err, FormatError::InvalidSignature));
683 }
684
685 #[test]
686 fn test_decode_bad_version() {
687 let hdr = ObjectHeader::new();
688 let mut encoded = hdr.encode();
689 encoded[4] = 99; let err = ObjectHeader::decode(&encoded).unwrap_err();
691 assert!(matches!(err, FormatError::InvalidVersion(99)));
692 }
693
694 #[test]
695 fn test_decode_checksum_mismatch() {
696 let mut hdr = ObjectHeader::new();
697 hdr.add_message(0x01, 0x00, vec![1, 2, 3]);
698 let mut encoded = hdr.encode();
699 let last_data = encoded.len() - 5;
701 encoded[last_data] ^= 0xFF;
702 let err = ObjectHeader::decode(&encoded).unwrap_err();
703 assert!(matches!(err, FormatError::ChecksumMismatch { .. }));
704 }
705
706 #[test]
707 fn test_decode_buffer_too_short() {
708 let err = ObjectHeader::decode(&[0u8; 5]).unwrap_err();
709 assert!(matches!(err, FormatError::BufferTooShort { .. }));
710 }
711
712 #[test]
713 fn test_decode_with_trailing_data() {
714 let mut hdr = ObjectHeader::new();
715 hdr.add_message(0x01, 0x00, vec![7, 8, 9]);
716 let mut encoded = hdr.encode();
717 let original_len = encoded.len();
718 encoded.extend_from_slice(&[0xBB; 50]); let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
721 assert_eq!(consumed, original_len);
722 assert_eq!(decoded, hdr);
723 }
724
725 #[test]
726 fn test_large_message_payload() {
727 let mut hdr = ObjectHeader::new();
728 let big_data = vec![0x42; 1000];
729 hdr.add_message(0x0C, 0x00, big_data.clone());
730
731 let encoded = hdr.encode();
732 let (decoded, consumed) = ObjectHeader::decode(&encoded).expect("decode failed");
733 assert_eq!(consumed, encoded.len());
734 assert_eq!(decoded.messages[0].data.len(), 1000);
735 assert_eq!(decoded.messages[0].data, big_data);
736 }
737
738 #[test]
739 fn test_default() {
740 let hdr = ObjectHeader::default();
741 assert_eq!(hdr.flags, 0x02);
742 assert!(hdr.messages.is_empty());
743 }
744}