1#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5
6use byteorder::{ByteOrder, LittleEndian};
7
8use crate::error::FormatError;
9use crate::signature::HDF5_SIGNATURE;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Superblock {
14 pub version: u8,
16 pub offset_size: u8,
18 pub length_size: u8,
20 pub base_address: u64,
22 pub eof_address: u64,
24 pub root_group_address: u64,
26 pub group_leaf_node_k: Option<u16>,
28 pub group_internal_node_k: Option<u16>,
30 pub indexed_storage_internal_node_k: Option<u16>,
32 pub free_space_address: Option<u64>,
34 pub driver_info_address: Option<u64>,
36 pub consistency_flags: u32,
38 pub superblock_extension_address: Option<u64>,
40 pub checksum: Option<u32>,
42}
43
44fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
46 let s = size as usize;
47 if pos + s > data.len() {
48 return Err(FormatError::UnexpectedEof {
49 expected: pos + s,
50 available: data.len(),
51 });
52 }
53 let slice = &data[pos..pos + s];
54 Ok(match size {
55 2 => LittleEndian::read_u16(slice) as u64,
56 4 => LittleEndian::read_u32(slice) as u64,
57 8 => LittleEndian::read_u64(slice),
58 _ => unreachable!(), })
60}
61
62fn validate_sizes(offset_size: u8, length_size: u8) -> Result<(), FormatError> {
63 if !matches!(offset_size, 2 | 4 | 8) {
64 return Err(FormatError::InvalidOffsetSize(offset_size));
65 }
66 if !matches!(length_size, 2 | 4 | 8) {
67 return Err(FormatError::InvalidLengthSize(length_size));
68 }
69 Ok(())
70}
71
72fn ensure_len(data: &[u8], needed: usize) -> Result<(), FormatError> {
73 if data.len() < needed {
74 Err(FormatError::UnexpectedEof {
75 expected: needed,
76 available: data.len(),
77 })
78 } else {
79 Ok(())
80 }
81}
82
83impl Superblock {
84 pub fn serialize(&self) -> Vec<u8> {
88 let mut buf = Vec::with_capacity(48);
89 buf.extend_from_slice(&HDF5_SIGNATURE);
90 buf.push(self.version);
91 buf.push(self.offset_size);
92 buf.push(self.length_size);
93 buf.push(self.consistency_flags as u8);
94 Self::write_offset(&mut buf, self.base_address, self.offset_size);
96 let ext_addr = self.superblock_extension_address.unwrap_or(u64::MAX);
98 Self::write_offset(&mut buf, ext_addr, self.offset_size);
99 Self::write_offset(&mut buf, self.eof_address, self.offset_size);
101 Self::write_offset(&mut buf, self.root_group_address, self.offset_size);
103 let checksum = crate::checksum::jenkins_lookup3(&buf);
105 buf.extend_from_slice(&checksum.to_le_bytes());
106 buf
107 }
108
109 fn write_offset(buf: &mut Vec<u8>, val: u64, size: u8) {
110 match size {
111 2 => buf.extend_from_slice(&(val as u16).to_le_bytes()),
112 4 => buf.extend_from_slice(&(val as u32).to_le_bytes()),
113 8 => buf.extend_from_slice(&val.to_le_bytes()),
114 _ => {}
115 }
116 }
117
118 pub fn parse(data: &[u8], signature_offset: usize) -> Result<Superblock, FormatError> {
122 let d = &data[signature_offset..];
123 ensure_len(d, 9)?; if d[..8] != HDF5_SIGNATURE {
127 return Err(FormatError::SignatureNotFound);
128 }
129
130 let version = d[8];
131 match version {
132 0 => Self::parse_v0(d),
133 1 => Self::parse_v1(d),
134 2 | 3 => Self::parse_v2v3(d, version),
135 v => Err(FormatError::UnsupportedVersion(v)),
136 }
137 }
138
139 fn parse_v0(d: &[u8]) -> Result<Superblock, FormatError> {
140 ensure_len(d, 24)?;
145
146 let offset_size = d[13];
147 let length_size = d[14];
148 validate_sizes(offset_size, length_size)?;
149
150 let group_leaf_node_k = LittleEndian::read_u16(&d[16..18]);
151 let group_internal_node_k = LittleEndian::read_u16(&d[18..20]);
152 let consistency_flags = LittleEndian::read_u32(&d[20..24]);
153
154 let os = offset_size as usize;
155 let var_start = 24;
157 let sym_entry_size = os + os + 4 + 4 + 16; let total = var_start + 4 * os + sym_entry_size;
159 ensure_len(d, total)?;
160
161 let mut pos = var_start;
162 let base_address = read_offset(d, pos, offset_size)?;
163 pos += os;
164 let free_space_address = read_offset(d, pos, offset_size)?;
165 pos += os;
166 let eof_address = read_offset(d, pos, offset_size)?;
167 pos += os;
168 let driver_info_address = read_offset(d, pos, offset_size)?;
169 pos += os;
170
171 let _link_name_offset = read_offset(d, pos, offset_size)?;
173 pos += os;
174 let object_header_addr = read_offset(d, pos, offset_size)?;
175
176 Ok(Superblock {
177 version: 0,
178 offset_size,
179 length_size,
180 base_address,
181 eof_address,
182 root_group_address: object_header_addr,
183 group_leaf_node_k: Some(group_leaf_node_k),
184 group_internal_node_k: Some(group_internal_node_k),
185 indexed_storage_internal_node_k: None,
186 free_space_address: Some(free_space_address),
187 driver_info_address: Some(driver_info_address),
188 consistency_flags,
189 superblock_extension_address: None,
190 checksum: None,
191 })
192 }
193
194 fn parse_v1(d: &[u8]) -> Result<Superblock, FormatError> {
195 ensure_len(d, 28)?;
201
202 let offset_size = d[13];
203 let length_size = d[14];
204 validate_sizes(offset_size, length_size)?;
205
206 let group_leaf_node_k = LittleEndian::read_u16(&d[16..18]);
207 let group_internal_node_k = LittleEndian::read_u16(&d[18..20]);
208 let indexed_storage_internal_node_k = LittleEndian::read_u16(&d[20..22]);
209 let consistency_flags = LittleEndian::read_u32(&d[24..28]);
211
212 let os = offset_size as usize;
213 let var_start = 28;
214 let sym_entry_size = os + os + 4 + 4 + 16;
215 let total = var_start + 4 * os + sym_entry_size;
216 ensure_len(d, total)?;
217
218 let mut pos = var_start;
219 let base_address = read_offset(d, pos, offset_size)?;
220 pos += os;
221 let free_space_address = read_offset(d, pos, offset_size)?;
222 pos += os;
223 let eof_address = read_offset(d, pos, offset_size)?;
224 pos += os;
225 let driver_info_address = read_offset(d, pos, offset_size)?;
226 pos += os;
227
228 let _link_name_offset = read_offset(d, pos, offset_size)?;
230 pos += os;
231 let object_header_addr = read_offset(d, pos, offset_size)?;
232
233 Ok(Superblock {
234 version: 1,
235 offset_size,
236 length_size,
237 base_address,
238 eof_address,
239 root_group_address: object_header_addr,
240 group_leaf_node_k: Some(group_leaf_node_k),
241 group_internal_node_k: Some(group_internal_node_k),
242 indexed_storage_internal_node_k: Some(indexed_storage_internal_node_k),
243 free_space_address: Some(free_space_address),
244 driver_info_address: Some(driver_info_address),
245 consistency_flags,
246 superblock_extension_address: None,
247 checksum: None,
248 })
249 }
250
251 fn parse_v2v3(d: &[u8], version: u8) -> Result<Superblock, FormatError> {
252 ensure_len(d, 12)?;
254
255 let offset_size = d[9];
256 let length_size = d[10];
257 validate_sizes(offset_size, length_size)?;
258 let consistency_flags = d[11] as u32;
259
260 let os = offset_size as usize;
261 let total = 12 + 4 * os + 4;
263 ensure_len(d, total)?;
264
265 let mut pos = 12;
266 let base_address = read_offset(d, pos, offset_size)?;
267 pos += os;
268 let superblock_extension_address = read_offset(d, pos, offset_size)?;
269 pos += os;
270 let eof_address = read_offset(d, pos, offset_size)?;
271 pos += os;
272 let root_group_address = read_offset(d, pos, offset_size)?;
273 pos += os;
274
275 let stored_checksum = LittleEndian::read_u32(&d[pos..pos + 4]);
276
277 #[cfg(feature = "checksum")]
279 {
280 let computed = crate::checksum::jenkins_lookup3(&d[..pos]);
281 if computed != stored_checksum {
282 return Err(FormatError::ChecksumMismatch {
283 expected: stored_checksum,
284 computed,
285 });
286 }
287 }
288
289 Ok(Superblock {
290 version,
291 offset_size,
292 length_size,
293 base_address,
294 eof_address,
295 root_group_address,
296 group_leaf_node_k: None,
297 group_internal_node_k: None,
298 indexed_storage_internal_node_k: None,
299 free_space_address: None,
300 driver_info_address: None,
301 consistency_flags,
302 superblock_extension_address: Some(superblock_extension_address),
303 checksum: Some(stored_checksum),
304 })
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 fn build_v0_bytes(offset_size: u8) -> Vec<u8> {
314 let mut buf = Vec::new();
315 buf.extend_from_slice(&HDF5_SIGNATURE); buf.push(0); buf.push(0); buf.push(0); buf.push(0); buf.push(0); buf.push(offset_size); buf.push(offset_size); buf.push(0); buf.extend_from_slice(&4u16.to_le_bytes()); buf.extend_from_slice(&16u16.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); write_offset(&mut buf, 0, offset_size);
329 write_offset(&mut buf, 0xFFFFFFFFFFFFFFFF, offset_size);
331 write_offset(&mut buf, 4096, offset_size);
333 write_offset(&mut buf, 0xFFFFFFFFFFFFFFFF, offset_size);
335 write_offset(&mut buf, 0, offset_size); write_offset(&mut buf, 96, offset_size); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 16]); buf
342 }
343
344 fn write_offset(buf: &mut Vec<u8>, val: u64, size: u8) {
345 match size {
346 2 => buf.extend_from_slice(&(val as u16).to_le_bytes()),
347 4 => buf.extend_from_slice(&(val as u32).to_le_bytes()),
348 8 => buf.extend_from_slice(&val.to_le_bytes()),
349 _ => panic!("bad test offset size"),
350 }
351 }
352
353 fn build_v1_bytes(offset_size: u8) -> Vec<u8> {
354 let mut buf = Vec::new();
355 buf.extend_from_slice(&HDF5_SIGNATURE);
356 buf.push(1); buf.push(0); buf.push(0); buf.push(0); buf.push(0); buf.push(offset_size);
362 buf.push(offset_size);
363 buf.push(0); buf.extend_from_slice(&4u16.to_le_bytes()); buf.extend_from_slice(&16u16.to_le_bytes()); buf.extend_from_slice(&32u16.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes()); buf.extend_from_slice(&0u32.to_le_bytes()); write_offset(&mut buf, 0, offset_size); write_offset(&mut buf, 0xFFFFFFFFFFFFFFFF, offset_size); write_offset(&mut buf, 8192, offset_size); write_offset(&mut buf, 0xFFFFFFFFFFFFFFFF, offset_size); write_offset(&mut buf, 0, offset_size);
375 write_offset(&mut buf, 200, offset_size); buf.extend_from_slice(&0u32.to_le_bytes());
377 buf.extend_from_slice(&0u32.to_le_bytes());
378 buf.extend_from_slice(&[0u8; 16]);
379 buf
380 }
381
382 fn build_v2_bytes(offset_size: u8, version: u8) -> Vec<u8> {
383 let mut buf = Vec::new();
384 buf.extend_from_slice(&HDF5_SIGNATURE);
385 buf.push(version);
386 buf.push(offset_size);
387 buf.push(offset_size); buf.push(0); write_offset(&mut buf, 0, offset_size); write_offset(&mut buf, 0xFFFFFFFFFFFFFFFF, offset_size); write_offset(&mut buf, 2048, offset_size); write_offset(&mut buf, 48, offset_size); let checksum = crate::checksum::jenkins_lookup3(&buf);
396 buf.extend_from_slice(&checksum.to_le_bytes());
397 buf
398 }
399
400 #[test]
401 fn parse_v0_8byte_offsets() {
402 let data = build_v0_bytes(8);
403 let sb = Superblock::parse(&data, 0).unwrap();
404 assert_eq!(sb.version, 0);
405 assert_eq!(sb.offset_size, 8);
406 assert_eq!(sb.base_address, 0);
407 assert_eq!(sb.eof_address, 4096);
408 assert_eq!(sb.root_group_address, 96);
409 assert_eq!(sb.group_leaf_node_k, Some(4));
410 assert_eq!(sb.group_internal_node_k, Some(16));
411 assert_eq!(sb.indexed_storage_internal_node_k, None);
412 assert_eq!(sb.free_space_address, Some(0xFFFFFFFFFFFFFFFF));
413 assert_eq!(sb.driver_info_address, Some(0xFFFFFFFFFFFFFFFF));
414 assert_eq!(sb.checksum, None);
415 }
416
417 #[test]
418 fn parse_v0_4byte_offsets() {
419 let data = build_v0_bytes(4);
420 let sb = Superblock::parse(&data, 0).unwrap();
421 assert_eq!(sb.version, 0);
422 assert_eq!(sb.offset_size, 4);
423 assert_eq!(sb.eof_address, 4096);
424 assert_eq!(sb.root_group_address, 96);
425 }
426
427 #[test]
428 fn parse_v1_8byte_offsets() {
429 let data = build_v1_bytes(8);
430 let sb = Superblock::parse(&data, 0).unwrap();
431 assert_eq!(sb.version, 1);
432 assert_eq!(sb.offset_size, 8);
433 assert_eq!(sb.eof_address, 8192);
434 assert_eq!(sb.root_group_address, 200);
435 assert_eq!(sb.indexed_storage_internal_node_k, Some(32));
436 assert_eq!(sb.group_leaf_node_k, Some(4));
437 }
438
439 #[test]
440 fn parse_v1_4byte_offsets() {
441 let data = build_v1_bytes(4);
442 let sb = Superblock::parse(&data, 0).unwrap();
443 assert_eq!(sb.version, 1);
444 assert_eq!(sb.offset_size, 4);
445 }
446
447 #[test]
448 fn parse_v2_8byte_offsets() {
449 let data = build_v2_bytes(8, 2);
450 let sb = Superblock::parse(&data, 0).unwrap();
451 assert_eq!(sb.version, 2);
452 assert_eq!(sb.offset_size, 8);
453 assert_eq!(sb.eof_address, 2048);
454 assert_eq!(sb.root_group_address, 48);
455 assert!(sb.checksum.is_some());
456 assert_eq!(sb.group_leaf_node_k, None);
457 }
458
459 #[test]
460 fn parse_v2_4byte_offsets() {
461 let data = build_v2_bytes(4, 2);
462 let sb = Superblock::parse(&data, 0).unwrap();
463 assert_eq!(sb.version, 2);
464 assert_eq!(sb.offset_size, 4);
465 }
466
467 #[test]
468 fn parse_v3() {
469 let data = build_v2_bytes(8, 3);
470 let sb = Superblock::parse(&data, 0).unwrap();
471 assert_eq!(sb.version, 3);
472 }
473
474 #[test]
475 fn checksum_mismatch_v2() {
476 let mut data = build_v2_bytes(8, 2);
477 let len = data.len();
479 data[len - 1] ^= 0xFF;
480 let err = Superblock::parse(&data, 0).unwrap_err();
481 matches!(err, FormatError::ChecksumMismatch { .. });
482 }
483
484 #[test]
485 fn unsupported_version() {
486 let mut data = vec![0u8; 64];
487 data[..8].copy_from_slice(&HDF5_SIGNATURE);
488 data[8] = 99;
489 assert_eq!(
490 Superblock::parse(&data, 0),
491 Err(FormatError::UnsupportedVersion(99))
492 );
493 }
494
495 #[test]
496 fn truncated_data() {
497 let data = HDF5_SIGNATURE.to_vec(); assert!(matches!(
500 Superblock::parse(&data, 0),
501 Err(FormatError::UnexpectedEof { .. })
502 ));
503 }
504
505 #[test]
506 fn truncated_v0() {
507 let mut data = vec![0u8; 20]; data[..8].copy_from_slice(&HDF5_SIGNATURE);
509 data[8] = 0; data[13] = 8; data[14] = 8; assert!(matches!(
513 Superblock::parse(&data, 0),
514 Err(FormatError::UnexpectedEof { .. })
515 ));
516 }
517
518 #[test]
519 fn invalid_offset_size() {
520 let mut data = vec![0u8; 64];
521 data[..8].copy_from_slice(&HDF5_SIGNATURE);
522 data[8] = 0; data[13] = 3; data[14] = 8;
525 assert_eq!(
526 Superblock::parse(&data, 0),
527 Err(FormatError::InvalidOffsetSize(3))
528 );
529 }
530
531 #[test]
532 fn invalid_length_size() {
533 let mut data = vec![0u8; 64];
534 data[..8].copy_from_slice(&HDF5_SIGNATURE);
535 data[8] = 0;
536 data[13] = 8;
537 data[14] = 5; assert_eq!(
539 Superblock::parse(&data, 0),
540 Err(FormatError::InvalidLengthSize(5))
541 );
542 }
543
544 #[test]
545 fn parse_at_nonzero_offset() {
546 let mut data = vec![0u8; 1024];
547 let v0 = build_v0_bytes(8);
548 data[512..512 + v0.len()].copy_from_slice(&v0);
549 let sb = Superblock::parse(&data, 512).unwrap();
550 assert_eq!(sb.version, 0);
551 assert_eq!(sb.root_group_address, 96);
552 }
553
554 #[test]
555 fn v2_2byte_offsets() {
556 let data = build_v2_bytes(2, 2);
557 let sb = Superblock::parse(&data, 0).unwrap();
558 assert_eq!(sb.offset_size, 2);
559 assert_eq!(sb.eof_address, 2048);
560 }
561}