1use crate::format::checksum::checksum_metadata;
11use crate::format::{FormatError, FormatResult, UNDEF_ADDR};
12
13pub const HDF5_SIGNATURE: [u8; 8] = [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a];
15
16pub const SUPERBLOCK_V2: u8 = 2;
18
19pub const SUPERBLOCK_V3: u8 = 3;
21
22pub const FLAG_WRITE_ACCESS: u8 = 0x01;
24
25pub const FLAG_FILE_OK: u8 = 0x02;
27
28pub const FLAG_SWMR_WRITE: u8 = 0x04;
30
31#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct SuperblockV2V3 {
48 pub version: u8,
50 pub sizeof_offsets: u8,
52 pub sizeof_lengths: u8,
54 pub file_consistency_flags: u8,
56 pub base_address: u64,
58 pub superblock_extension_address: u64,
60 pub end_of_file_address: u64,
62 pub root_group_object_header_address: u64,
64}
65
66impl SuperblockV2V3 {
67 pub fn encoded_size(&self) -> usize {
69 12 + 4 * (self.sizeof_offsets as usize) + 4
70 }
71
72 pub fn encode(&self) -> Vec<u8> {
74 let size = self.encoded_size();
75 let mut buf = Vec::with_capacity(size);
76
77 buf.extend_from_slice(&HDF5_SIGNATURE);
79 buf.push(self.version);
81 buf.push(self.sizeof_offsets);
83 buf.push(self.sizeof_lengths);
85 buf.push(self.file_consistency_flags);
87
88 let o = self.sizeof_offsets as usize;
90 encode_offset(&mut buf, self.base_address, o);
91 encode_offset(&mut buf, self.superblock_extension_address, o);
92 encode_offset(&mut buf, self.end_of_file_address, o);
93 encode_offset(&mut buf, self.root_group_object_header_address, o);
94
95 debug_assert_eq!(buf.len(), size - 4);
97 let cksum = checksum_metadata(&buf);
98 buf.extend_from_slice(&cksum.to_le_bytes());
99
100 debug_assert_eq!(buf.len(), size);
101 buf
102 }
103
104 pub fn decode(buf: &[u8]) -> FormatResult<Self> {
107 if buf.len() < 12 {
110 return Err(FormatError::BufferTooShort {
111 needed: 12,
112 available: buf.len(),
113 });
114 }
115
116 if buf[0..8] != HDF5_SIGNATURE {
118 return Err(FormatError::InvalidSignature);
119 }
120
121 let version = buf[8];
123 if version != SUPERBLOCK_V2 && version != SUPERBLOCK_V3 {
124 return Err(FormatError::InvalidVersion(version));
125 }
126
127 let sizeof_offsets = buf[9];
128 let sizeof_lengths = buf[10];
129 let file_consistency_flags = buf[11];
130 validate_sizeof(sizeof_offsets, sizeof_lengths)?;
131
132 let o = sizeof_offsets as usize;
133 let total_size = 12 + 4 * o + 4;
134 if buf.len() < total_size {
135 return Err(FormatError::BufferTooShort {
136 needed: total_size,
137 available: buf.len(),
138 });
139 }
140
141 let data_end = total_size - 4;
143 let stored_cksum = u32::from_le_bytes([
144 buf[data_end],
145 buf[data_end + 1],
146 buf[data_end + 2],
147 buf[data_end + 3],
148 ]);
149 let computed_cksum = checksum_metadata(&buf[..data_end]);
150 if stored_cksum != computed_cksum {
151 return Err(FormatError::ChecksumMismatch {
152 expected: stored_cksum,
153 computed: computed_cksum,
154 });
155 }
156
157 let mut pos = 12;
159 let base_address = decode_offset(buf, &mut pos, o);
160 let superblock_extension_address = decode_offset(buf, &mut pos, o);
161 let end_of_file_address = decode_offset(buf, &mut pos, o);
162 let root_group_object_header_address = decode_offset(buf, &mut pos, o);
163
164 Ok(SuperblockV2V3 {
165 version,
166 sizeof_offsets,
167 sizeof_lengths,
168 file_consistency_flags,
169 base_address,
170 superblock_extension_address,
171 end_of_file_address,
172 root_group_object_header_address,
173 })
174 }
175}
176
177fn encode_offset(buf: &mut Vec<u8>, value: u64, size: usize) {
179 let bytes = value.to_le_bytes();
180 buf.extend_from_slice(&bytes[..size]);
181}
182
183fn validate_sizeof(sizeof_offsets: u8, sizeof_lengths: u8) -> FormatResult<()> {
189 for (name, v) in [
190 ("sizeof_offsets", sizeof_offsets),
191 ("sizeof_lengths", sizeof_lengths),
192 ] {
193 if !matches!(v, 2 | 4 | 8) {
194 return Err(FormatError::InvalidData(format!(
195 "unsupported superblock {name} = {v} (only 2, 4, 8 are supported)"
196 )));
197 }
198 }
199 Ok(())
200}
201
202fn decode_offset(buf: &[u8], pos: &mut usize, size: usize) -> u64 {
205 let v = crate::format::bytes::read_le_uint(&buf[*pos..], size);
206 *pos += size;
207 v
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::format::UNDEF_ADDR;
214
215 #[test]
216 fn test_encoded_size() {
217 let sb = SuperblockV2V3 {
218 version: SUPERBLOCK_V3,
219 sizeof_offsets: 8,
220 sizeof_lengths: 8,
221 file_consistency_flags: 0,
222 base_address: 0,
223 superblock_extension_address: UNDEF_ADDR,
224 end_of_file_address: 4096,
225 root_group_object_header_address: 48,
226 };
227 assert_eq!(sb.encoded_size(), 48);
229 }
230
231 #[test]
232 fn test_roundtrip_v3_offset8() {
233 let original = SuperblockV2V3 {
234 version: SUPERBLOCK_V3,
235 sizeof_offsets: 8,
236 sizeof_lengths: 8,
237 file_consistency_flags: FLAG_FILE_OK,
238 base_address: 0,
239 superblock_extension_address: UNDEF_ADDR,
240 end_of_file_address: 0x1_0000,
241 root_group_object_header_address: 48,
242 };
243
244 let encoded = original.encode();
245 assert_eq!(encoded.len(), original.encoded_size());
246
247 assert_eq!(&encoded[..8], &HDF5_SIGNATURE);
249
250 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
251 assert_eq!(decoded, original);
252 }
253
254 #[test]
255 fn test_roundtrip_v2_offset4() {
256 let original = SuperblockV2V3 {
257 version: SUPERBLOCK_V2,
258 sizeof_offsets: 4,
259 sizeof_lengths: 4,
260 file_consistency_flags: 0,
261 base_address: 0,
262 superblock_extension_address: 0xFFFF_FFFF,
263 end_of_file_address: 8192,
264 root_group_object_header_address: 28,
265 };
266
267 let encoded = original.encode();
268 assert_eq!(encoded.len(), 32);
270
271 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
272 assert_eq!(decoded, original);
273 }
274
275 #[test]
276 fn test_decode_bad_signature() {
277 let mut data = vec![0u8; 48];
278 data[0] = 0x00;
280 let err = SuperblockV2V3::decode(&data).unwrap_err();
281 assert!(matches!(err, FormatError::InvalidSignature));
282 }
283
284 #[test]
285 fn test_decode_bad_version() {
286 let sb = SuperblockV2V3 {
287 version: SUPERBLOCK_V3,
288 sizeof_offsets: 8,
289 sizeof_lengths: 8,
290 file_consistency_flags: 0,
291 base_address: 0,
292 superblock_extension_address: UNDEF_ADDR,
293 end_of_file_address: 4096,
294 root_group_object_header_address: 48,
295 };
296 let mut encoded = sb.encode();
297 encoded[8] = 1;
299 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
300 assert!(matches!(err, FormatError::InvalidVersion(1)));
301 }
302
303 #[test]
304 fn test_decode_checksum_mismatch() {
305 let sb = SuperblockV2V3 {
306 version: SUPERBLOCK_V3,
307 sizeof_offsets: 8,
308 sizeof_lengths: 8,
309 file_consistency_flags: 0,
310 base_address: 0,
311 superblock_extension_address: UNDEF_ADDR,
312 end_of_file_address: 4096,
313 root_group_object_header_address: 48,
314 };
315 let mut encoded = sb.encode();
316 encoded[12] = 0xFF;
318 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
319 assert!(matches!(err, FormatError::ChecksumMismatch { .. }));
320 }
321
322 #[test]
323 fn test_decode_buffer_too_short() {
324 let err = SuperblockV2V3::decode(&[0u8; 4]).unwrap_err();
325 assert!(matches!(err, FormatError::BufferTooShort { .. }));
326 }
327
328 #[test]
329 fn test_flags() {
330 let sb = SuperblockV2V3 {
331 version: SUPERBLOCK_V3,
332 sizeof_offsets: 8,
333 sizeof_lengths: 8,
334 file_consistency_flags: FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE,
335 base_address: 0,
336 superblock_extension_address: UNDEF_ADDR,
337 end_of_file_address: 4096,
338 root_group_object_header_address: 48,
339 };
340 let encoded = sb.encode();
341 let decoded = SuperblockV2V3::decode(&encoded).unwrap();
342 assert_eq!(
343 decoded.file_consistency_flags,
344 FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE
345 );
346 }
347
348 #[test]
349 fn test_roundtrip_with_extra_trailing_data() {
350 let sb = SuperblockV2V3 {
352 version: SUPERBLOCK_V3,
353 sizeof_offsets: 8,
354 sizeof_lengths: 8,
355 file_consistency_flags: 0,
356 base_address: 0,
357 superblock_extension_address: UNDEF_ADDR,
358 end_of_file_address: 4096,
359 root_group_object_header_address: 48,
360 };
361 let mut encoded = sb.encode();
362 encoded.extend_from_slice(&[0xAA; 100]); let decoded = SuperblockV2V3::decode(&encoded).unwrap();
364 assert_eq!(decoded, sb);
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
374pub struct SymbolTableEntry {
375 pub name_offset: u64,
377 pub obj_header_addr: u64,
379 pub cache_type: u32,
381 pub btree_addr: u64,
383 pub heap_addr: u64,
385}
386
387#[derive(Debug, Clone, PartialEq, Eq)]
407pub struct SuperblockV0V1 {
408 pub version: u8,
409 pub sizeof_offsets: u8,
410 pub sizeof_lengths: u8,
411 pub file_consistency_flags: u32,
412 pub sym_leaf_k: u16,
413 pub btree_internal_k: u16,
414 pub indexed_storage_k: Option<u16>,
415 pub base_address: u64,
416 pub superblock_extension_address: u64,
417 pub end_of_file_address: u64,
418 pub driver_info_address: u64,
419 pub root_symbol_table_entry: SymbolTableEntry,
420}
421
422impl SuperblockV0V1 {
423 pub fn decode(buf: &[u8]) -> FormatResult<Self> {
426 if buf.len() < 16 {
428 return Err(FormatError::BufferTooShort {
429 needed: 16,
430 available: buf.len(),
431 });
432 }
433
434 if buf[0..8] != HDF5_SIGNATURE {
436 return Err(FormatError::InvalidSignature);
437 }
438
439 let version = buf[8];
440 if version != 0 && version != 1 {
441 return Err(FormatError::InvalidVersion(version));
442 }
443
444 let sizeof_offsets = buf[13];
449 let sizeof_lengths = buf[14];
450 validate_sizeof(sizeof_offsets, sizeof_lengths)?;
452
453 let o = sizeof_offsets as usize;
454 let mut pos = 16;
455
456 if buf.len() < pos + 4 {
458 return Err(FormatError::BufferTooShort {
459 needed: pos + 4,
460 available: buf.len(),
461 });
462 }
463
464 let sym_leaf_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
465 pos += 2;
466 let btree_internal_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
467 pos += 2;
468
469 if buf.len() < pos + 4 {
470 return Err(FormatError::BufferTooShort {
471 needed: pos + 4,
472 available: buf.len(),
473 });
474 }
475 let file_consistency_flags =
476 u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
477 pos += 4;
478
479 let indexed_storage_k = if version == 1 {
480 if buf.len() < pos + 4 {
481 return Err(FormatError::BufferTooShort {
482 needed: pos + 4,
483 available: buf.len(),
484 });
485 }
486 let k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
487 pos += 2;
488 pos += 2;
490 Some(k)
491 } else {
492 None
493 };
494
495 let needed = pos + 4 * o;
497 if buf.len() < needed {
498 return Err(FormatError::BufferTooShort {
499 needed,
500 available: buf.len(),
501 });
502 }
503
504 let base_address = decode_offset(buf, &mut pos, o);
505 let superblock_extension_address = decode_offset(buf, &mut pos, o);
506 let end_of_file_address = decode_offset(buf, &mut pos, o);
507 let driver_info_address = decode_offset(buf, &mut pos, o);
508
509 let ste = decode_symbol_table_entry(buf, &mut pos, o, sizeof_lengths as usize)?;
511
512 Ok(SuperblockV0V1 {
513 version,
514 sizeof_offsets,
515 sizeof_lengths,
516 file_consistency_flags,
517 sym_leaf_k,
518 btree_internal_k,
519 indexed_storage_k,
520 base_address,
521 superblock_extension_address,
522 end_of_file_address,
523 driver_info_address,
524 root_symbol_table_entry: ste,
525 })
526 }
527}
528
529pub fn decode_symbol_table_entry(
531 buf: &[u8],
532 pos: &mut usize,
533 sizeof_addr: usize,
534 sizeof_size: usize,
535) -> FormatResult<SymbolTableEntry> {
536 let needed = *pos + sizeof_size + sizeof_addr + 4 + 4 + 16;
537 if buf.len() < needed {
538 return Err(FormatError::BufferTooShort {
539 needed,
540 available: buf.len(),
541 });
542 }
543
544 let name_offset = decode_offset(buf, pos, sizeof_size);
545 let obj_header_addr = decode_offset(buf, pos, sizeof_addr);
546 let cache_type = u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]);
547 *pos += 4;
548 *pos += 4;
550
551 let (btree_addr, heap_addr) = if cache_type == 1 {
553 let btree = decode_offset(buf, pos, sizeof_addr);
554 let heap = decode_offset(buf, pos, sizeof_addr);
555 let used = 2 * sizeof_addr;
557 if used < 16 {
558 *pos += 16 - used;
559 }
560 (btree, heap)
561 } else {
562 *pos += 16;
563 (UNDEF_ADDR, UNDEF_ADDR)
564 };
565
566 Ok(SymbolTableEntry {
567 name_offset,
568 obj_header_addr,
569 cache_type,
570 btree_addr,
571 heap_addr,
572 })
573}
574
575pub fn detect_superblock_version(buf: &[u8]) -> FormatResult<u8> {
578 if buf.len() < 9 {
579 return Err(FormatError::BufferTooShort {
580 needed: 9,
581 available: buf.len(),
582 });
583 }
584 if buf[0..8] != HDF5_SIGNATURE {
585 return Err(FormatError::InvalidSignature);
586 }
587 Ok(buf[8])
588}
589
590#[cfg(test)]
591mod tests_v0v1 {
592 use super::*;
593
594 fn build_v0_superblock(
596 root_obj_header_addr: u64,
597 btree_addr: u64,
598 heap_addr: u64,
599 eof: u64,
600 ) -> Vec<u8> {
601 let sizeof_addr: usize = 8;
602 let sizeof_size: usize = 8;
603 let mut buf = Vec::new();
604
605 buf.extend_from_slice(&HDF5_SIGNATURE);
607 buf.push(0);
609 buf.push(0);
611 buf.push(0);
613 buf.push(0);
615 buf.push(0);
617 buf.push(sizeof_addr as u8);
619 buf.push(sizeof_size as u8);
621 buf.push(0);
623
624 buf.extend_from_slice(&4u16.to_le_bytes());
626 buf.extend_from_slice(&32u16.to_le_bytes());
628 buf.extend_from_slice(&0u32.to_le_bytes());
630
631 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
633 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
635 buf.extend_from_slice(&eof.to_le_bytes()[..sizeof_addr]);
637 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
639
640 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
643 buf.extend_from_slice(&root_obj_header_addr.to_le_bytes()[..sizeof_addr]);
645 buf.extend_from_slice(&1u32.to_le_bytes());
647 buf.extend_from_slice(&0u32.to_le_bytes());
649 buf.extend_from_slice(&btree_addr.to_le_bytes()[..sizeof_addr]);
651 buf.extend_from_slice(&heap_addr.to_le_bytes()[..sizeof_addr]);
652
653 buf
654 }
655
656 #[test]
657 fn test_decode_v0() {
658 let buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
659 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
660 assert_eq!(sb.version, 0);
661 assert_eq!(sb.sizeof_offsets, 8);
662 assert_eq!(sb.sizeof_lengths, 8);
663 assert_eq!(sb.sym_leaf_k, 4);
664 assert_eq!(sb.btree_internal_k, 32);
665 assert_eq!(sb.file_consistency_flags, 0);
666 assert_eq!(sb.indexed_storage_k, None);
667 assert_eq!(sb.base_address, 0);
668 assert_eq!(sb.end_of_file_address, 0x1000);
669 assert_eq!(sb.root_symbol_table_entry.obj_header_addr, 0x100);
670 assert_eq!(sb.root_symbol_table_entry.cache_type, 1);
671 assert_eq!(sb.root_symbol_table_entry.btree_addr, 0x200);
672 assert_eq!(sb.root_symbol_table_entry.heap_addr, 0x300);
673 }
674
675 #[test]
676 fn test_decode_v1() {
677 let sizeof_addr: usize = 8;
679 let sizeof_size: usize = 8;
680 let mut buf = Vec::new();
681 buf.extend_from_slice(&HDF5_SIGNATURE);
682 buf.push(1); buf.push(0);
684 buf.push(0);
685 buf.push(0);
686 buf.push(0);
687 buf.push(sizeof_addr as u8);
688 buf.push(sizeof_size as u8);
689 buf.push(0);
690 buf.extend_from_slice(&4u16.to_le_bytes());
691 buf.extend_from_slice(&32u16.to_le_bytes());
692 buf.extend_from_slice(&0u32.to_le_bytes());
693 buf.extend_from_slice(&16u16.to_le_bytes());
695 buf.extend_from_slice(&0u16.to_le_bytes());
697 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
699 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
700 buf.extend_from_slice(&0x2000u64.to_le_bytes()[..sizeof_addr]);
701 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
702 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
704 buf.extend_from_slice(&0x100u64.to_le_bytes()[..sizeof_addr]);
705 buf.extend_from_slice(&1u32.to_le_bytes());
706 buf.extend_from_slice(&0u32.to_le_bytes());
707 buf.extend_from_slice(&0x200u64.to_le_bytes()[..sizeof_addr]);
708 buf.extend_from_slice(&0x300u64.to_le_bytes()[..sizeof_addr]);
709
710 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
711 assert_eq!(sb.version, 1);
712 assert_eq!(sb.indexed_storage_k, Some(16));
713 assert_eq!(sb.root_symbol_table_entry.btree_addr, 0x200);
714 }
715
716 #[test]
717 fn test_detect_version() {
718 let v0 = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
719 assert_eq!(detect_superblock_version(&v0).unwrap(), 0);
720
721 let sb_v3 = SuperblockV2V3 {
722 version: SUPERBLOCK_V3,
723 sizeof_offsets: 8,
724 sizeof_lengths: 8,
725 file_consistency_flags: 0,
726 base_address: 0,
727 superblock_extension_address: UNDEF_ADDR,
728 end_of_file_address: 4096,
729 root_group_object_header_address: 48,
730 };
731 let v3 = sb_v3.encode();
732 assert_eq!(detect_superblock_version(&v3).unwrap(), 3);
733 }
734
735 #[test]
736 fn test_bad_sig() {
737 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
738 buf[0] = 0;
739 assert!(matches!(
740 SuperblockV0V1::decode(&buf).unwrap_err(),
741 FormatError::InvalidSignature
742 ));
743 }
744
745 #[test]
746 fn test_bad_version() {
747 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
748 buf[8] = 5; assert!(matches!(
750 SuperblockV0V1::decode(&buf).unwrap_err(),
751 FormatError::InvalidVersion(5)
752 ));
753 }
754}