1use crate::format::checksum::checksum_metadata;
11use crate::format::{FormatError, FormatResult};
12
13pub const HDF5_SIGNATURE: [u8; 8] = [0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a];
15
16pub const SUPERBLOCK_V0: u8 = 0;
19
20pub const SUPERBLOCK_V2: u8 = 2;
22
23pub const SUPERBLOCK_V3: u8 = 3;
25
26pub const FLAG_WRITE_ACCESS: u8 = 0x01;
28
29pub const FLAG_FILE_OK: u8 = 0x02;
31
32pub const FLAG_SWMR_WRITE: u8 = 0x04;
34
35#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct SuperblockV2V3 {
52 pub version: u8,
54 pub sizeof_offsets: u8,
56 pub sizeof_lengths: u8,
58 pub file_consistency_flags: u8,
60 pub base_address: u64,
64 pub superblock_extension_address: u64,
66 pub end_of_file_address: u64,
72 pub root_group_object_header_address: u64,
75}
76
77impl SuperblockV2V3 {
78 pub fn encoded_size(&self) -> usize {
80 Self::size_for(self.sizeof_offsets)
81 }
82
83 pub fn size_for(sizeof_offsets: u8) -> usize {
87 12 + 4 * (sizeof_offsets as usize) + 4
88 }
89
90 pub fn encode(&self) -> Vec<u8> {
92 let size = self.encoded_size();
93 let mut buf = Vec::with_capacity(size);
94
95 buf.extend_from_slice(&HDF5_SIGNATURE);
97 buf.push(self.version);
99 buf.push(self.sizeof_offsets);
101 buf.push(self.sizeof_lengths);
103 buf.push(self.file_consistency_flags);
105
106 let o = self.sizeof_offsets as usize;
108 encode_offset(&mut buf, self.base_address, o);
109 encode_offset(&mut buf, self.superblock_extension_address, o);
110 encode_offset(&mut buf, self.end_of_file_address, o);
111 encode_offset(&mut buf, self.root_group_object_header_address, o);
112
113 debug_assert_eq!(buf.len(), size - 4);
115 let cksum = checksum_metadata(&buf);
116 buf.extend_from_slice(&cksum.to_le_bytes());
117
118 debug_assert_eq!(buf.len(), size);
119 buf
120 }
121
122 pub fn decode(buf: &[u8]) -> FormatResult<Self> {
125 if buf.len() < 12 {
128 return Err(FormatError::BufferTooShort {
129 needed: 12,
130 available: buf.len(),
131 });
132 }
133
134 if buf[0..8] != HDF5_SIGNATURE {
136 return Err(FormatError::InvalidSignature);
137 }
138
139 let version = buf[8];
141 if version != SUPERBLOCK_V2 && version != SUPERBLOCK_V3 {
142 return Err(FormatError::InvalidVersion(version));
143 }
144
145 let sizeof_offsets = buf[9];
146 let sizeof_lengths = buf[10];
147 let file_consistency_flags = buf[11];
148 validate_sizeof(sizeof_offsets, sizeof_lengths)?;
149
150 let o = sizeof_offsets as usize;
151 let total_size = 12 + 4 * o + 4;
152 if buf.len() < total_size {
153 return Err(FormatError::BufferTooShort {
154 needed: total_size,
155 available: buf.len(),
156 });
157 }
158
159 let data_end = total_size - 4;
161 let stored_cksum = u32::from_le_bytes([
162 buf[data_end],
163 buf[data_end + 1],
164 buf[data_end + 2],
165 buf[data_end + 3],
166 ]);
167 let computed_cksum = checksum_metadata(&buf[..data_end]);
168 if stored_cksum != computed_cksum {
169 return Err(FormatError::ChecksumMismatch {
170 expected: stored_cksum,
171 computed: computed_cksum,
172 });
173 }
174
175 let mut pos = 12;
177 let base_address = decode_offset(buf, &mut pos, o);
178 let superblock_extension_address = decode_offset(buf, &mut pos, o);
179 let end_of_file_address = decode_offset(buf, &mut pos, o);
180 let root_group_object_header_address = decode_offset(buf, &mut pos, o);
181
182 Ok(SuperblockV2V3 {
183 version,
184 sizeof_offsets,
185 sizeof_lengths,
186 file_consistency_flags,
187 base_address,
188 superblock_extension_address,
189 end_of_file_address,
190 root_group_object_header_address,
191 })
192 }
193}
194
195fn encode_offset(buf: &mut Vec<u8>, value: u64, size: usize) {
197 let bytes = value.to_le_bytes();
198 buf.extend_from_slice(&bytes[..size]);
199}
200
201fn validate_sizeof(sizeof_offsets: u8, sizeof_lengths: u8) -> FormatResult<()> {
207 for (name, v) in [
208 ("sizeof_offsets", sizeof_offsets),
209 ("sizeof_lengths", sizeof_lengths),
210 ] {
211 if !matches!(v, 2 | 4 | 8) {
212 return Err(FormatError::InvalidData(format!(
213 "unsupported superblock {name} = {v} (only 2, 4, 8 are supported)"
214 )));
215 }
216 }
217 Ok(())
218}
219
220fn decode_offset(buf: &[u8], pos: &mut usize, size: usize) -> u64 {
223 let v = crate::format::bytes::read_le_uint(&buf[*pos..], size);
224 *pos += size;
225 v
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use crate::format::UNDEF_ADDR;
232
233 #[test]
234 fn test_encoded_size() {
235 let sb = SuperblockV2V3 {
236 version: SUPERBLOCK_V3,
237 sizeof_offsets: 8,
238 sizeof_lengths: 8,
239 file_consistency_flags: 0,
240 base_address: 0,
241 superblock_extension_address: UNDEF_ADDR,
242 end_of_file_address: 4096,
243 root_group_object_header_address: 48,
244 };
245 assert_eq!(sb.encoded_size(), 48);
247 }
248
249 #[test]
250 fn test_roundtrip_v3_offset8() {
251 let original = SuperblockV2V3 {
252 version: SUPERBLOCK_V3,
253 sizeof_offsets: 8,
254 sizeof_lengths: 8,
255 file_consistency_flags: FLAG_FILE_OK,
256 base_address: 0,
257 superblock_extension_address: UNDEF_ADDR,
258 end_of_file_address: 0x1_0000,
259 root_group_object_header_address: 48,
260 };
261
262 let encoded = original.encode();
263 assert_eq!(encoded.len(), original.encoded_size());
264
265 assert_eq!(&encoded[..8], &HDF5_SIGNATURE);
267
268 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
269 assert_eq!(decoded, original);
270 }
271
272 #[test]
273 fn test_roundtrip_v2_offset4() {
274 let original = SuperblockV2V3 {
275 version: SUPERBLOCK_V2,
276 sizeof_offsets: 4,
277 sizeof_lengths: 4,
278 file_consistency_flags: 0,
279 base_address: 0,
280 superblock_extension_address: 0xFFFF_FFFF,
281 end_of_file_address: 8192,
282 root_group_object_header_address: 28,
283 };
284
285 let encoded = original.encode();
286 assert_eq!(encoded.len(), 32);
288
289 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
290 assert_eq!(decoded, original);
291 }
292
293 #[test]
294 fn test_decode_bad_signature() {
295 let mut data = vec![0u8; 48];
296 data[0] = 0x00;
298 let err = SuperblockV2V3::decode(&data).unwrap_err();
299 assert!(matches!(err, FormatError::InvalidSignature));
300 }
301
302 #[test]
303 fn test_decode_bad_version() {
304 let sb = SuperblockV2V3 {
305 version: SUPERBLOCK_V3,
306 sizeof_offsets: 8,
307 sizeof_lengths: 8,
308 file_consistency_flags: 0,
309 base_address: 0,
310 superblock_extension_address: UNDEF_ADDR,
311 end_of_file_address: 4096,
312 root_group_object_header_address: 48,
313 };
314 let mut encoded = sb.encode();
315 encoded[8] = 1;
317 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
318 assert!(matches!(err, FormatError::InvalidVersion(1)));
319 }
320
321 #[test]
322 fn test_decode_checksum_mismatch() {
323 let sb = SuperblockV2V3 {
324 version: SUPERBLOCK_V3,
325 sizeof_offsets: 8,
326 sizeof_lengths: 8,
327 file_consistency_flags: 0,
328 base_address: 0,
329 superblock_extension_address: UNDEF_ADDR,
330 end_of_file_address: 4096,
331 root_group_object_header_address: 48,
332 };
333 let mut encoded = sb.encode();
334 encoded[12] = 0xFF;
336 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
337 assert!(matches!(err, FormatError::ChecksumMismatch { .. }));
338 }
339
340 #[test]
341 fn test_decode_buffer_too_short() {
342 let err = SuperblockV2V3::decode(&[0u8; 4]).unwrap_err();
343 assert!(matches!(err, FormatError::BufferTooShort { .. }));
344 }
345
346 #[test]
347 fn test_flags() {
348 let sb = SuperblockV2V3 {
349 version: SUPERBLOCK_V3,
350 sizeof_offsets: 8,
351 sizeof_lengths: 8,
352 file_consistency_flags: FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE,
353 base_address: 0,
354 superblock_extension_address: UNDEF_ADDR,
355 end_of_file_address: 4096,
356 root_group_object_header_address: 48,
357 };
358 let encoded = sb.encode();
359 let decoded = SuperblockV2V3::decode(&encoded).unwrap();
360 assert_eq!(
361 decoded.file_consistency_flags,
362 FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE
363 );
364 }
365
366 #[test]
367 fn test_roundtrip_with_extra_trailing_data() {
368 let sb = SuperblockV2V3 {
370 version: SUPERBLOCK_V3,
371 sizeof_offsets: 8,
372 sizeof_lengths: 8,
373 file_consistency_flags: 0,
374 base_address: 0,
375 superblock_extension_address: UNDEF_ADDR,
376 end_of_file_address: 4096,
377 root_group_object_header_address: 48,
378 };
379 let mut encoded = sb.encode();
380 encoded.extend_from_slice(&[0xAA; 100]); let decoded = SuperblockV2V3::decode(&encoded).unwrap();
382 assert_eq!(decoded, sb);
383 }
384}
385
386#[derive(Debug, Clone, PartialEq, Eq)]
396pub enum SymbolTableCache {
397 Nothing,
399 SymbolTable { btree_addr: u64, heap_addr: u64 },
401 SoftLink { value_offset: u32 },
404}
405
406#[derive(Debug, Clone, PartialEq, Eq)]
408pub struct SymbolTableEntry {
409 pub name_offset: u64,
411 pub obj_header_addr: u64,
414 pub cache: SymbolTableCache,
416}
417
418impl SymbolTableEntry {
419 pub fn cached_symbol_table(&self) -> Option<(u64, u64)> {
422 match self.cache {
423 SymbolTableCache::SymbolTable {
424 btree_addr,
425 heap_addr,
426 } => Some((btree_addr, heap_addr)),
427 _ => None,
428 }
429 }
430}
431
432#[derive(Debug, Clone, PartialEq, Eq)]
452pub struct SuperblockV0V1 {
453 pub version: u8,
454 pub sizeof_offsets: u8,
455 pub sizeof_lengths: u8,
456 pub file_consistency_flags: u32,
457 pub sym_leaf_k: u16,
458 pub btree_internal_k: u16,
459 pub indexed_storage_k: Option<u16>,
460 pub base_address: u64,
463 pub superblock_extension_address: u64,
464 pub end_of_file_address: u64,
467 pub driver_info_address: u64,
468 pub root_symbol_table_entry: SymbolTableEntry,
469}
470
471impl SuperblockV0V1 {
472 pub fn encoded_size(&self) -> usize {
474 let o = self.sizeof_offsets as usize;
475 let s = self.sizeof_lengths as usize;
476 16 + 8 + if self.version == 1 { 4 } else { 0 } + 4 * o + symbol_table_entry_size(o, s)
477 }
478
479 pub fn encode(&self) -> Vec<u8> {
489 let o = self.sizeof_offsets as usize;
490 let s = self.sizeof_lengths as usize;
491 let size = self.encoded_size();
492 let mut buf = Vec::with_capacity(size);
493
494 buf.extend_from_slice(&HDF5_SIGNATURE);
495 buf.push(self.version);
496 buf.push(0); buf.push(0); buf.push(0); buf.push(0); buf.push(self.sizeof_offsets);
501 buf.push(self.sizeof_lengths);
502 buf.push(0); buf.extend_from_slice(&self.sym_leaf_k.to_le_bytes());
504 buf.extend_from_slice(&self.btree_internal_k.to_le_bytes());
505 buf.extend_from_slice(&self.file_consistency_flags.to_le_bytes());
506 if self.version == 1 {
507 buf.extend_from_slice(&self.indexed_storage_k.unwrap_or(32).to_le_bytes());
510 buf.extend_from_slice(&[0u8; 2]); }
512
513 encode_offset(&mut buf, self.base_address, o);
514 encode_offset(&mut buf, self.superblock_extension_address, o);
515 encode_offset(&mut buf, self.end_of_file_address, o);
516 encode_offset(&mut buf, self.driver_info_address, o);
517 encode_symbol_table_entry(&mut buf, &self.root_symbol_table_entry, o, s);
518
519 debug_assert_eq!(buf.len(), size);
520 buf
521 }
522
523 pub fn decode(buf: &[u8]) -> FormatResult<Self> {
526 if buf.len() < 16 {
528 return Err(FormatError::BufferTooShort {
529 needed: 16,
530 available: buf.len(),
531 });
532 }
533
534 if buf[0..8] != HDF5_SIGNATURE {
536 return Err(FormatError::InvalidSignature);
537 }
538
539 let version = buf[8];
540 if version != 0 && version != 1 {
541 return Err(FormatError::InvalidVersion(version));
542 }
543
544 let sizeof_offsets = buf[13];
549 let sizeof_lengths = buf[14];
550 validate_sizeof(sizeof_offsets, sizeof_lengths)?;
552
553 let o = sizeof_offsets as usize;
554 let mut pos = 16;
555
556 if buf.len() < pos + 4 {
558 return Err(FormatError::BufferTooShort {
559 needed: pos + 4,
560 available: buf.len(),
561 });
562 }
563
564 let sym_leaf_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
565 pos += 2;
566 let btree_internal_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
567 pos += 2;
568
569 if buf.len() < pos + 4 {
570 return Err(FormatError::BufferTooShort {
571 needed: pos + 4,
572 available: buf.len(),
573 });
574 }
575 let file_consistency_flags =
576 u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
577 pos += 4;
578
579 let indexed_storage_k = if version == 1 {
580 if buf.len() < pos + 4 {
581 return Err(FormatError::BufferTooShort {
582 needed: pos + 4,
583 available: buf.len(),
584 });
585 }
586 let k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
587 pos += 2;
588 pos += 2;
590 Some(k)
591 } else {
592 None
593 };
594
595 let needed = pos + 4 * o;
597 if buf.len() < needed {
598 return Err(FormatError::BufferTooShort {
599 needed,
600 available: buf.len(),
601 });
602 }
603
604 let base_address = decode_offset(buf, &mut pos, o);
605 let superblock_extension_address = decode_offset(buf, &mut pos, o);
606 let end_of_file_address = decode_offset(buf, &mut pos, o);
607 let driver_info_address = decode_offset(buf, &mut pos, o);
608
609 let ste = decode_symbol_table_entry(buf, &mut pos, o, sizeof_lengths as usize)?;
611
612 Ok(SuperblockV0V1 {
613 version,
614 sizeof_offsets,
615 sizeof_lengths,
616 file_consistency_flags,
617 sym_leaf_k,
618 btree_internal_k,
619 indexed_storage_k,
620 base_address,
621 superblock_extension_address,
622 end_of_file_address,
623 driver_info_address,
624 root_symbol_table_entry: ste,
625 })
626 }
627}
628
629pub fn symbol_table_entry_size(sizeof_addr: usize, sizeof_size: usize) -> usize {
632 sizeof_size + sizeof_addr + 4 + 4 + 16
633}
634
635pub fn encode_symbol_table_entry(
642 out: &mut Vec<u8>,
643 entry: &SymbolTableEntry,
644 sizeof_addr: usize,
645 sizeof_size: usize,
646) {
647 let start = out.len();
648 encode_offset(out, entry.name_offset, sizeof_size);
649 encode_offset(out, entry.obj_header_addr, sizeof_addr);
650 let cache_type: u32 = match entry.cache {
651 SymbolTableCache::Nothing => 0,
652 SymbolTableCache::SymbolTable { .. } => 1,
653 SymbolTableCache::SoftLink { .. } => 2,
654 };
655 out.extend_from_slice(&cache_type.to_le_bytes());
656 out.extend_from_slice(&0u32.to_le_bytes()); let scratch = out.len();
658 match entry.cache {
659 SymbolTableCache::Nothing => {}
660 SymbolTableCache::SymbolTable {
661 btree_addr,
662 heap_addr,
663 } => {
664 encode_offset(out, btree_addr, sizeof_addr);
665 encode_offset(out, heap_addr, sizeof_addr);
666 }
667 SymbolTableCache::SoftLink { value_offset } => {
668 out.extend_from_slice(&value_offset.to_le_bytes());
669 }
670 }
671 out.resize(scratch + 16, 0);
672 debug_assert_eq!(
673 out.len() - start,
674 symbol_table_entry_size(sizeof_addr, sizeof_size)
675 );
676}
677
678pub fn decode_symbol_table_entry(
680 buf: &[u8],
681 pos: &mut usize,
682 sizeof_addr: usize,
683 sizeof_size: usize,
684) -> FormatResult<SymbolTableEntry> {
685 let needed = *pos + sizeof_size + sizeof_addr + 4 + 4 + 16;
686 if buf.len() < needed {
687 return Err(FormatError::BufferTooShort {
688 needed,
689 available: buf.len(),
690 });
691 }
692
693 let name_offset = decode_offset(buf, pos, sizeof_size);
694 let obj_header_addr = decode_offset(buf, pos, sizeof_addr);
695 let cache_type = u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]);
696 *pos += 4;
697 *pos += 4;
699
700 let scratch_start = *pos;
702 let cache = match cache_type {
703 1 => {
704 let btree_addr = decode_offset(buf, pos, sizeof_addr);
705 let heap_addr = decode_offset(buf, pos, sizeof_addr);
706 SymbolTableCache::SymbolTable {
707 btree_addr,
708 heap_addr,
709 }
710 }
711 2 => {
712 let value_offset =
713 u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]);
714 *pos += 4;
715 SymbolTableCache::SoftLink { value_offset }
716 }
717 _ => SymbolTableCache::Nothing,
718 };
719 *pos = scratch_start + 16;
721
722 Ok(SymbolTableEntry {
723 name_offset,
724 obj_header_addr,
725 cache,
726 })
727}
728
729pub fn detect_superblock_version(buf: &[u8]) -> FormatResult<u8> {
732 if buf.len() < 9 {
733 return Err(FormatError::BufferTooShort {
734 needed: 9,
735 available: buf.len(),
736 });
737 }
738 if buf[0..8] != HDF5_SIGNATURE {
739 return Err(FormatError::InvalidSignature);
740 }
741 Ok(buf[8])
742}
743
744#[cfg(test)]
745mod tests_v0v1 {
746 use super::*;
747 use crate::format::UNDEF_ADDR;
748
749 fn build_v0_superblock(
751 root_obj_header_addr: u64,
752 btree_addr: u64,
753 heap_addr: u64,
754 eof: u64,
755 ) -> Vec<u8> {
756 let sizeof_addr: usize = 8;
757 let sizeof_size: usize = 8;
758 let mut buf = Vec::new();
759
760 buf.extend_from_slice(&HDF5_SIGNATURE);
762 buf.push(0);
764 buf.push(0);
766 buf.push(0);
768 buf.push(0);
770 buf.push(0);
772 buf.push(sizeof_addr as u8);
774 buf.push(sizeof_size as u8);
776 buf.push(0);
778
779 buf.extend_from_slice(&4u16.to_le_bytes());
781 buf.extend_from_slice(&32u16.to_le_bytes());
783 buf.extend_from_slice(&0u32.to_le_bytes());
785
786 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
788 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
790 buf.extend_from_slice(&eof.to_le_bytes()[..sizeof_addr]);
792 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
794
795 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
798 buf.extend_from_slice(&root_obj_header_addr.to_le_bytes()[..sizeof_addr]);
800 buf.extend_from_slice(&1u32.to_le_bytes());
802 buf.extend_from_slice(&0u32.to_le_bytes());
804 buf.extend_from_slice(&btree_addr.to_le_bytes()[..sizeof_addr]);
806 buf.extend_from_slice(&heap_addr.to_le_bytes()[..sizeof_addr]);
807
808 buf
809 }
810
811 #[test]
815 fn a_v0_superblock_re_emits_the_bytes_libhdf5_wrote() {
816 let buf = build_v0_superblock(0x60, 0x88, 0x2a8, 4896);
817 let sb = SuperblockV0V1::decode(&buf).unwrap();
818 assert_eq!(sb.encoded_size(), buf.len());
819 assert_eq!(sb.encode(), buf);
820 }
821
822 #[test]
826 fn a_v1_superblock_re_emits_its_indexed_storage_k() {
827 let mut buf = build_v0_superblock(0x60, 0x88, 0x2a8, 4896);
828 buf[8] = 1;
829 buf.splice(24..24, [0x40u8, 0x00, 0x00, 0x00]);
830 let sb = SuperblockV0V1::decode(&buf).unwrap();
831 assert_eq!(sb.version, 1);
832 assert_eq!(sb.indexed_storage_k, Some(0x40));
833 assert_eq!(sb.encode(), buf);
834 }
835
836 #[test]
839 fn every_symbol_table_cache_shape_round_trips() {
840 for cache in [
841 SymbolTableCache::Nothing,
842 SymbolTableCache::SymbolTable {
843 btree_addr: 0x88,
844 heap_addr: 0x2a8,
845 },
846 SymbolTableCache::SoftLink { value_offset: 24 },
847 ] {
848 let entry = SymbolTableEntry {
849 name_offset: 8,
850 obj_header_addr: 0x320,
851 cache: cache.clone(),
852 };
853 let mut buf = Vec::new();
854 encode_symbol_table_entry(&mut buf, &entry, 8, 8);
855 assert_eq!(buf.len(), symbol_table_entry_size(8, 8));
856 let mut pos = 0;
857 let back = decode_symbol_table_entry(&buf, &mut pos, 8, 8).unwrap();
858 assert_eq!(pos, buf.len());
859 assert_eq!(back, entry);
860 }
861 }
862
863 #[test]
864 fn test_decode_v0() {
865 let buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
866 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
867 assert_eq!(sb.version, 0);
868 assert_eq!(sb.sizeof_offsets, 8);
869 assert_eq!(sb.sizeof_lengths, 8);
870 assert_eq!(sb.sym_leaf_k, 4);
871 assert_eq!(sb.btree_internal_k, 32);
872 assert_eq!(sb.file_consistency_flags, 0);
873 assert_eq!(sb.indexed_storage_k, None);
874 assert_eq!(sb.base_address, 0);
875 assert_eq!(sb.end_of_file_address, 0x1000);
876 assert_eq!(sb.root_symbol_table_entry.obj_header_addr, 0x100);
877 assert_eq!(
878 sb.root_symbol_table_entry.cached_symbol_table(),
879 Some((0x200, 0x300))
880 );
881 }
882
883 #[test]
884 fn test_decode_v1() {
885 let sizeof_addr: usize = 8;
887 let sizeof_size: usize = 8;
888 let mut buf = Vec::new();
889 buf.extend_from_slice(&HDF5_SIGNATURE);
890 buf.push(1); buf.push(0);
892 buf.push(0);
893 buf.push(0);
894 buf.push(0);
895 buf.push(sizeof_addr as u8);
896 buf.push(sizeof_size as u8);
897 buf.push(0);
898 buf.extend_from_slice(&4u16.to_le_bytes());
899 buf.extend_from_slice(&32u16.to_le_bytes());
900 buf.extend_from_slice(&0u32.to_le_bytes());
901 buf.extend_from_slice(&16u16.to_le_bytes());
903 buf.extend_from_slice(&0u16.to_le_bytes());
905 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
907 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
908 buf.extend_from_slice(&0x2000u64.to_le_bytes()[..sizeof_addr]);
909 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
910 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
912 buf.extend_from_slice(&0x100u64.to_le_bytes()[..sizeof_addr]);
913 buf.extend_from_slice(&1u32.to_le_bytes());
914 buf.extend_from_slice(&0u32.to_le_bytes());
915 buf.extend_from_slice(&0x200u64.to_le_bytes()[..sizeof_addr]);
916 buf.extend_from_slice(&0x300u64.to_le_bytes()[..sizeof_addr]);
917
918 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
919 assert_eq!(sb.version, 1);
920 assert_eq!(sb.indexed_storage_k, Some(16));
921 assert_eq!(
922 sb.root_symbol_table_entry.cached_symbol_table(),
923 Some((0x200, 0x300))
924 );
925 }
926
927 #[test]
928 fn test_detect_version() {
929 let v0 = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
930 assert_eq!(detect_superblock_version(&v0).unwrap(), 0);
931
932 let sb_v3 = SuperblockV2V3 {
933 version: SUPERBLOCK_V3,
934 sizeof_offsets: 8,
935 sizeof_lengths: 8,
936 file_consistency_flags: 0,
937 base_address: 0,
938 superblock_extension_address: UNDEF_ADDR,
939 end_of_file_address: 4096,
940 root_group_object_header_address: 48,
941 };
942 let v3 = sb_v3.encode();
943 assert_eq!(detect_superblock_version(&v3).unwrap(), 3);
944 }
945
946 #[test]
947 fn test_bad_sig() {
948 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
949 buf[0] = 0;
950 assert!(matches!(
951 SuperblockV0V1::decode(&buf).unwrap_err(),
952 FormatError::InvalidSignature
953 ));
954 }
955
956 #[test]
957 fn test_bad_version() {
958 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
959 buf[8] = 5; assert!(matches!(
961 SuperblockV0V1::decode(&buf).unwrap_err(),
962 FormatError::InvalidVersion(5)
963 ));
964 }
965}