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
131 let o = sizeof_offsets as usize;
132 let total_size = 12 + 4 * o + 4;
133 if buf.len() < total_size {
134 return Err(FormatError::BufferTooShort {
135 needed: total_size,
136 available: buf.len(),
137 });
138 }
139
140 let data_end = total_size - 4;
142 let stored_cksum = u32::from_le_bytes([
143 buf[data_end],
144 buf[data_end + 1],
145 buf[data_end + 2],
146 buf[data_end + 3],
147 ]);
148 let computed_cksum = checksum_metadata(&buf[..data_end]);
149 if stored_cksum != computed_cksum {
150 return Err(FormatError::ChecksumMismatch {
151 expected: stored_cksum,
152 computed: computed_cksum,
153 });
154 }
155
156 let mut pos = 12;
158 let base_address = decode_offset(buf, &mut pos, o);
159 let superblock_extension_address = decode_offset(buf, &mut pos, o);
160 let end_of_file_address = decode_offset(buf, &mut pos, o);
161 let root_group_object_header_address = decode_offset(buf, &mut pos, o);
162
163 Ok(SuperblockV2V3 {
164 version,
165 sizeof_offsets,
166 sizeof_lengths,
167 file_consistency_flags,
168 base_address,
169 superblock_extension_address,
170 end_of_file_address,
171 root_group_object_header_address,
172 })
173 }
174}
175
176fn encode_offset(buf: &mut Vec<u8>, value: u64, size: usize) {
178 let bytes = value.to_le_bytes();
179 buf.extend_from_slice(&bytes[..size]);
180}
181
182fn decode_offset(buf: &[u8], pos: &mut usize, size: usize) -> u64 {
185 let mut bytes = [0u8; 8];
186 bytes[..size].copy_from_slice(&buf[*pos..*pos + size]);
187 *pos += size;
188 u64::from_le_bytes(bytes)
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::format::UNDEF_ADDR;
195
196 #[test]
197 fn test_encoded_size() {
198 let sb = SuperblockV2V3 {
199 version: SUPERBLOCK_V3,
200 sizeof_offsets: 8,
201 sizeof_lengths: 8,
202 file_consistency_flags: 0,
203 base_address: 0,
204 superblock_extension_address: UNDEF_ADDR,
205 end_of_file_address: 4096,
206 root_group_object_header_address: 48,
207 };
208 assert_eq!(sb.encoded_size(), 48);
210 }
211
212 #[test]
213 fn test_roundtrip_v3_offset8() {
214 let original = SuperblockV2V3 {
215 version: SUPERBLOCK_V3,
216 sizeof_offsets: 8,
217 sizeof_lengths: 8,
218 file_consistency_flags: FLAG_FILE_OK,
219 base_address: 0,
220 superblock_extension_address: UNDEF_ADDR,
221 end_of_file_address: 0x1_0000,
222 root_group_object_header_address: 48,
223 };
224
225 let encoded = original.encode();
226 assert_eq!(encoded.len(), original.encoded_size());
227
228 assert_eq!(&encoded[..8], &HDF5_SIGNATURE);
230
231 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
232 assert_eq!(decoded, original);
233 }
234
235 #[test]
236 fn test_roundtrip_v2_offset4() {
237 let original = SuperblockV2V3 {
238 version: SUPERBLOCK_V2,
239 sizeof_offsets: 4,
240 sizeof_lengths: 4,
241 file_consistency_flags: 0,
242 base_address: 0,
243 superblock_extension_address: 0xFFFF_FFFF,
244 end_of_file_address: 8192,
245 root_group_object_header_address: 28,
246 };
247
248 let encoded = original.encode();
249 assert_eq!(encoded.len(), 32);
251
252 let decoded = SuperblockV2V3::decode(&encoded).expect("decode failed");
253 assert_eq!(decoded, original);
254 }
255
256 #[test]
257 fn test_decode_bad_signature() {
258 let mut data = vec![0u8; 48];
259 data[0] = 0x00;
261 let err = SuperblockV2V3::decode(&data).unwrap_err();
262 assert!(matches!(err, FormatError::InvalidSignature));
263 }
264
265 #[test]
266 fn test_decode_bad_version() {
267 let sb = SuperblockV2V3 {
268 version: SUPERBLOCK_V3,
269 sizeof_offsets: 8,
270 sizeof_lengths: 8,
271 file_consistency_flags: 0,
272 base_address: 0,
273 superblock_extension_address: UNDEF_ADDR,
274 end_of_file_address: 4096,
275 root_group_object_header_address: 48,
276 };
277 let mut encoded = sb.encode();
278 encoded[8] = 1;
280 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
281 assert!(matches!(err, FormatError::InvalidVersion(1)));
282 }
283
284 #[test]
285 fn test_decode_checksum_mismatch() {
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[12] = 0xFF;
299 let err = SuperblockV2V3::decode(&encoded).unwrap_err();
300 assert!(matches!(err, FormatError::ChecksumMismatch { .. }));
301 }
302
303 #[test]
304 fn test_decode_buffer_too_short() {
305 let err = SuperblockV2V3::decode(&[0u8; 4]).unwrap_err();
306 assert!(matches!(err, FormatError::BufferTooShort { .. }));
307 }
308
309 #[test]
310 fn test_flags() {
311 let sb = SuperblockV2V3 {
312 version: SUPERBLOCK_V3,
313 sizeof_offsets: 8,
314 sizeof_lengths: 8,
315 file_consistency_flags: FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE,
316 base_address: 0,
317 superblock_extension_address: UNDEF_ADDR,
318 end_of_file_address: 4096,
319 root_group_object_header_address: 48,
320 };
321 let encoded = sb.encode();
322 let decoded = SuperblockV2V3::decode(&encoded).unwrap();
323 assert_eq!(
324 decoded.file_consistency_flags,
325 FLAG_WRITE_ACCESS | FLAG_SWMR_WRITE
326 );
327 }
328
329 #[test]
330 fn test_roundtrip_with_extra_trailing_data() {
331 let sb = SuperblockV2V3 {
333 version: SUPERBLOCK_V3,
334 sizeof_offsets: 8,
335 sizeof_lengths: 8,
336 file_consistency_flags: 0,
337 base_address: 0,
338 superblock_extension_address: UNDEF_ADDR,
339 end_of_file_address: 4096,
340 root_group_object_header_address: 48,
341 };
342 let mut encoded = sb.encode();
343 encoded.extend_from_slice(&[0xAA; 100]); let decoded = SuperblockV2V3::decode(&encoded).unwrap();
345 assert_eq!(decoded, sb);
346 }
347}
348
349#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct SymbolTableEntry {
356 pub name_offset: u64,
358 pub obj_header_addr: u64,
360 pub cache_type: u32,
362 pub btree_addr: u64,
364 pub heap_addr: u64,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
388pub struct SuperblockV0V1 {
389 pub version: u8,
390 pub sizeof_offsets: u8,
391 pub sizeof_lengths: u8,
392 pub file_consistency_flags: u32,
393 pub sym_leaf_k: u16,
394 pub btree_internal_k: u16,
395 pub indexed_storage_k: Option<u16>,
396 pub base_address: u64,
397 pub superblock_extension_address: u64,
398 pub end_of_file_address: u64,
399 pub driver_info_address: u64,
400 pub root_symbol_table_entry: SymbolTableEntry,
401}
402
403impl SuperblockV0V1 {
404 pub fn decode(buf: &[u8]) -> FormatResult<Self> {
407 if buf.len() < 16 {
409 return Err(FormatError::BufferTooShort {
410 needed: 16,
411 available: buf.len(),
412 });
413 }
414
415 if buf[0..8] != HDF5_SIGNATURE {
417 return Err(FormatError::InvalidSignature);
418 }
419
420 let version = buf[8];
421 if version != 0 && version != 1 {
422 return Err(FormatError::InvalidVersion(version));
423 }
424
425 let sizeof_offsets = buf[13];
430 let sizeof_lengths = buf[14];
431 let o = sizeof_offsets as usize;
434 let mut pos = 16;
435
436 if buf.len() < pos + 4 {
438 return Err(FormatError::BufferTooShort {
439 needed: pos + 4,
440 available: buf.len(),
441 });
442 }
443
444 let sym_leaf_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
445 pos += 2;
446 let btree_internal_k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
447 pos += 2;
448
449 if buf.len() < pos + 4 {
450 return Err(FormatError::BufferTooShort {
451 needed: pos + 4,
452 available: buf.len(),
453 });
454 }
455 let file_consistency_flags =
456 u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
457 pos += 4;
458
459 let indexed_storage_k = if version == 1 {
460 if buf.len() < pos + 4 {
461 return Err(FormatError::BufferTooShort {
462 needed: pos + 4,
463 available: buf.len(),
464 });
465 }
466 let k = u16::from_le_bytes([buf[pos], buf[pos + 1]]);
467 pos += 2;
468 pos += 2;
470 Some(k)
471 } else {
472 None
473 };
474
475 let needed = pos + 4 * o;
477 if buf.len() < needed {
478 return Err(FormatError::BufferTooShort {
479 needed,
480 available: buf.len(),
481 });
482 }
483
484 let base_address = decode_offset(buf, &mut pos, o);
485 let superblock_extension_address = decode_offset(buf, &mut pos, o);
486 let end_of_file_address = decode_offset(buf, &mut pos, o);
487 let driver_info_address = decode_offset(buf, &mut pos, o);
488
489 let ste = decode_symbol_table_entry(buf, &mut pos, o, sizeof_lengths as usize)?;
491
492 Ok(SuperblockV0V1 {
493 version,
494 sizeof_offsets,
495 sizeof_lengths,
496 file_consistency_flags,
497 sym_leaf_k,
498 btree_internal_k,
499 indexed_storage_k,
500 base_address,
501 superblock_extension_address,
502 end_of_file_address,
503 driver_info_address,
504 root_symbol_table_entry: ste,
505 })
506 }
507}
508
509pub fn decode_symbol_table_entry(
511 buf: &[u8],
512 pos: &mut usize,
513 sizeof_addr: usize,
514 sizeof_size: usize,
515) -> FormatResult<SymbolTableEntry> {
516 let needed = *pos + sizeof_size + sizeof_addr + 4 + 4 + 16;
517 if buf.len() < needed {
518 return Err(FormatError::BufferTooShort {
519 needed,
520 available: buf.len(),
521 });
522 }
523
524 let name_offset = decode_offset(buf, pos, sizeof_size);
525 let obj_header_addr = decode_offset(buf, pos, sizeof_addr);
526 let cache_type = u32::from_le_bytes([buf[*pos], buf[*pos + 1], buf[*pos + 2], buf[*pos + 3]]);
527 *pos += 4;
528 *pos += 4;
530
531 let (btree_addr, heap_addr) = if cache_type == 1 {
533 let btree = decode_offset(buf, pos, sizeof_addr);
534 let heap = decode_offset(buf, pos, sizeof_addr);
535 let used = 2 * sizeof_addr;
537 if used < 16 {
538 *pos += 16 - used;
539 }
540 (btree, heap)
541 } else {
542 *pos += 16;
543 (UNDEF_ADDR, UNDEF_ADDR)
544 };
545
546 Ok(SymbolTableEntry {
547 name_offset,
548 obj_header_addr,
549 cache_type,
550 btree_addr,
551 heap_addr,
552 })
553}
554
555pub fn detect_superblock_version(buf: &[u8]) -> FormatResult<u8> {
558 if buf.len() < 9 {
559 return Err(FormatError::BufferTooShort {
560 needed: 9,
561 available: buf.len(),
562 });
563 }
564 if buf[0..8] != HDF5_SIGNATURE {
565 return Err(FormatError::InvalidSignature);
566 }
567 Ok(buf[8])
568}
569
570#[cfg(test)]
571mod tests_v0v1 {
572 use super::*;
573
574 fn build_v0_superblock(
576 root_obj_header_addr: u64,
577 btree_addr: u64,
578 heap_addr: u64,
579 eof: u64,
580 ) -> Vec<u8> {
581 let sizeof_addr: usize = 8;
582 let sizeof_size: usize = 8;
583 let mut buf = Vec::new();
584
585 buf.extend_from_slice(&HDF5_SIGNATURE);
587 buf.push(0);
589 buf.push(0);
591 buf.push(0);
593 buf.push(0);
595 buf.push(0);
597 buf.push(sizeof_addr as u8);
599 buf.push(sizeof_size as u8);
601 buf.push(0);
603
604 buf.extend_from_slice(&4u16.to_le_bytes());
606 buf.extend_from_slice(&32u16.to_le_bytes());
608 buf.extend_from_slice(&0u32.to_le_bytes());
610
611 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
613 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
615 buf.extend_from_slice(&eof.to_le_bytes()[..sizeof_addr]);
617 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
619
620 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
623 buf.extend_from_slice(&root_obj_header_addr.to_le_bytes()[..sizeof_addr]);
625 buf.extend_from_slice(&1u32.to_le_bytes());
627 buf.extend_from_slice(&0u32.to_le_bytes());
629 buf.extend_from_slice(&btree_addr.to_le_bytes()[..sizeof_addr]);
631 buf.extend_from_slice(&heap_addr.to_le_bytes()[..sizeof_addr]);
632
633 buf
634 }
635
636 #[test]
637 fn test_decode_v0() {
638 let buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
639 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
640 assert_eq!(sb.version, 0);
641 assert_eq!(sb.sizeof_offsets, 8);
642 assert_eq!(sb.sizeof_lengths, 8);
643 assert_eq!(sb.sym_leaf_k, 4);
644 assert_eq!(sb.btree_internal_k, 32);
645 assert_eq!(sb.file_consistency_flags, 0);
646 assert_eq!(sb.indexed_storage_k, None);
647 assert_eq!(sb.base_address, 0);
648 assert_eq!(sb.end_of_file_address, 0x1000);
649 assert_eq!(sb.root_symbol_table_entry.obj_header_addr, 0x100);
650 assert_eq!(sb.root_symbol_table_entry.cache_type, 1);
651 assert_eq!(sb.root_symbol_table_entry.btree_addr, 0x200);
652 assert_eq!(sb.root_symbol_table_entry.heap_addr, 0x300);
653 }
654
655 #[test]
656 fn test_decode_v1() {
657 let sizeof_addr: usize = 8;
659 let sizeof_size: usize = 8;
660 let mut buf = Vec::new();
661 buf.extend_from_slice(&HDF5_SIGNATURE);
662 buf.push(1); buf.push(0);
664 buf.push(0);
665 buf.push(0);
666 buf.push(0);
667 buf.push(sizeof_addr as u8);
668 buf.push(sizeof_size as u8);
669 buf.push(0);
670 buf.extend_from_slice(&4u16.to_le_bytes());
671 buf.extend_from_slice(&32u16.to_le_bytes());
672 buf.extend_from_slice(&0u32.to_le_bytes());
673 buf.extend_from_slice(&16u16.to_le_bytes());
675 buf.extend_from_slice(&0u16.to_le_bytes());
677 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_addr]);
679 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
680 buf.extend_from_slice(&0x2000u64.to_le_bytes()[..sizeof_addr]);
681 buf.extend_from_slice(&UNDEF_ADDR.to_le_bytes()[..sizeof_addr]);
682 buf.extend_from_slice(&0u64.to_le_bytes()[..sizeof_size]);
684 buf.extend_from_slice(&0x100u64.to_le_bytes()[..sizeof_addr]);
685 buf.extend_from_slice(&1u32.to_le_bytes());
686 buf.extend_from_slice(&0u32.to_le_bytes());
687 buf.extend_from_slice(&0x200u64.to_le_bytes()[..sizeof_addr]);
688 buf.extend_from_slice(&0x300u64.to_le_bytes()[..sizeof_addr]);
689
690 let sb = SuperblockV0V1::decode(&buf).expect("decode failed");
691 assert_eq!(sb.version, 1);
692 assert_eq!(sb.indexed_storage_k, Some(16));
693 assert_eq!(sb.root_symbol_table_entry.btree_addr, 0x200);
694 }
695
696 #[test]
697 fn test_detect_version() {
698 let v0 = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
699 assert_eq!(detect_superblock_version(&v0).unwrap(), 0);
700
701 let sb_v3 = SuperblockV2V3 {
702 version: SUPERBLOCK_V3,
703 sizeof_offsets: 8,
704 sizeof_lengths: 8,
705 file_consistency_flags: 0,
706 base_address: 0,
707 superblock_extension_address: UNDEF_ADDR,
708 end_of_file_address: 4096,
709 root_group_object_header_address: 48,
710 };
711 let v3 = sb_v3.encode();
712 assert_eq!(detect_superblock_version(&v3).unwrap(), 3);
713 }
714
715 #[test]
716 fn test_bad_sig() {
717 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
718 buf[0] = 0;
719 assert!(matches!(
720 SuperblockV0V1::decode(&buf).unwrap_err(),
721 FormatError::InvalidSignature
722 ));
723 }
724
725 #[test]
726 fn test_bad_version() {
727 let mut buf = build_v0_superblock(0x100, 0x200, 0x300, 0x1000);
728 buf[8] = 5; assert!(matches!(
730 SuperblockV0V1::decode(&buf).unwrap_err(),
731 FormatError::InvalidVersion(5)
732 ));
733 }
734}