chore: cleanup pass — remove empty types stub, implement superblock v4, reconcile plan docs

- Remove clawhdf5-types (empty 1-line stub crate; type defs already live in
  clawhdf5-format). Update workspace Cargo.toml and CLAUDE.md accordingly.
- Implement HDF5 superblock v4 (page-buffer mode) read and write support in
  clawhdf5-format: Superblock::parse_v4, page_size field, v4 serialize
  branch, and FileWriter::with_page_size. This was the one task left
  unimplemented from docs/superpowers/plans/2026-06-29-format-write-extensions.md.
- Reconcile the three docs/superpowers/plans/*.md docs (filter codecs,
  format write extensions, MPI-IO VOL) against actual shipped code: they
  were pre-work plans for d6c4d4f (2026-06-30) committed to git late on
  2026-08-03 with all checkboxes still unchecked. Mark completed tasks done
  and add a status note so they read as historical records, not open work.
- Refresh ROADMAP.md's "What's Next" section against current repo state.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Omar Sobh
2026-08-03 08:11:31 -07:00
co-authored by Claude Sonnet 5
parent b1fc23e975
commit 6b1ea450f5
11 changed files with 300 additions and 120 deletions
+157 -1
View File
@@ -39,6 +39,8 @@ pub struct Superblock {
pub superblock_extension_address: Option<u64>,
/// CRC32C checksum (v2/v3 only).
pub checksum: Option<u32>,
/// Page size for page-buffer mode (v4 only). `None` for v0v3.
pub page_size: Option<u32>,
}
/// Read an unsigned integer of `size` bytes (LE) from `data` at `pos`.
@@ -125,7 +127,8 @@ impl Superblock {
/// Serialize this superblock to bytes.
///
/// Always writes v2/v3 format. Computes and appends Jenkins lookup3 checksum.
/// Writes v2/v3 format, or v4 (with `page_size`) when `self.version == 4`.
/// Computes and appends Jenkins lookup3 checksum.
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(48);
buf.extend_from_slice(&HDF5_SIGNATURE);
@@ -142,6 +145,11 @@ impl Superblock {
Self::write_offset(&mut buf, self.eof_address, self.offset_size);
// root_group_address
Self::write_offset(&mut buf, self.root_group_address, self.offset_size);
// page_size (v4 only)
if self.version >= 4 {
let ps = self.page_size.unwrap_or(0);
buf.extend_from_slice(&ps.to_le_bytes());
}
// checksum
let checksum = crate::checksum::jenkins_lookup3(&buf);
buf.extend_from_slice(&checksum.to_le_bytes());
@@ -179,6 +187,7 @@ impl Superblock {
0 => Self::parse_v0(d),
1 => Self::parse_v1(d),
2 | 3 => Self::parse_v2v3(d, version),
4 => Self::parse_v4(d),
v => Err(FormatError::UnsupportedVersion(v)),
}
}
@@ -235,6 +244,7 @@ impl Superblock {
consistency_flags,
superblock_extension_address: None,
checksum: None,
page_size: None,
})
}
@@ -292,6 +302,7 @@ impl Superblock {
consistency_flags,
superblock_extension_address: None,
checksum: None,
page_size: None,
})
}
@@ -348,6 +359,71 @@ impl Superblock {
consistency_flags,
superblock_extension_address: Some(superblock_extension_address),
checksum: Some(stored_checksum),
page_size: None,
})
}
fn parse_v4(d: &[u8]) -> Result<Superblock, FormatError> {
// Same layout as v2/v3, plus page_size(4) inserted before the checksum.
ensure_len(d, 12)?;
let offset_size = d[9];
let length_size = d[10];
validate_sizes(offset_size, length_size)?;
let consistency_flags = d[11] as u32;
let os = offset_size as usize;
// 4 addresses + page_size(4) + checksum(4)
let total = 12 + 4 * os + 4 + 4;
ensure_len(d, total)?;
let mut pos = 12;
let base_address = read_offset(d, pos, offset_size)?;
pos += os;
let superblock_extension_address = read_offset(d, pos, offset_size)?;
pos += os;
let eof_address = read_offset(d, pos, offset_size)?;
pos += os;
let root_group_address = read_offset(d, pos, offset_size)?;
pos += os;
let page_size = LittleEndian::read_u32(&d[pos..pos + 4]);
pos += 4;
let stored_checksum = LittleEndian::read_u32(&d[pos..pos + 4]);
pos += 4;
#[cfg(feature = "checksum")]
{
let computed = crate::checksum::jenkins_lookup3(&d[..pos - 4]);
if computed != stored_checksum {
return Err(FormatError::ChecksumMismatch {
expected: stored_checksum,
computed,
});
}
}
#[cfg(not(feature = "checksum"))]
{
let _ = pos;
}
Ok(Superblock {
version: 4,
offset_size,
length_size,
base_address,
eof_address,
root_group_address,
group_leaf_node_k: None,
group_internal_node_k: None,
indexed_storage_internal_node_k: None,
free_space_address: None,
driver_info_address: None,
consistency_flags,
superblock_extension_address: Some(superblock_extension_address),
checksum: Some(stored_checksum),
page_size: Some(page_size),
})
}
}
@@ -652,4 +728,84 @@ mod tests {
let new_eof = sb.refresh_eof(&data, 0).unwrap();
assert_eq!(new_eof, old_eof);
}
#[test]
fn parse_v4_with_page_size() {
// Superblock v4 = v2/v3 layout + page_size(4) before checksum.
let mut buf = Vec::new();
buf.extend_from_slice(&HDF5_SIGNATURE);
buf.push(4); // version = 4
buf.push(8); // offset_size
buf.push(8); // length_size
buf.push(0); // consistency_flags
write_offset(&mut buf, 0, 8); // base_address
write_offset(&mut buf, u64::MAX, 8); // superblock_extension_address = UNDEF
write_offset(&mut buf, 512, 8); // eof_address
write_offset(&mut buf, 96, 8); // root_group_address
buf.extend_from_slice(&4096u32.to_le_bytes()); // page_size (v4 addition)
let checksum = crate::checksum::jenkins_lookup3(&buf);
buf.extend_from_slice(&checksum.to_le_bytes());
let sb = Superblock::parse(&buf, 0).unwrap();
assert_eq!(sb.version, 4);
assert_eq!(sb.offset_size, 8);
assert_eq!(sb.eof_address, 512);
assert_eq!(sb.root_group_address, 96);
assert_eq!(sb.page_size, Some(4096));
}
#[test]
fn serialize_v4_roundtrip() {
let sb = Superblock {
version: 4,
offset_size: 8,
length_size: 8,
base_address: 0,
eof_address: 1024,
root_group_address: 96,
group_leaf_node_k: None,
group_internal_node_k: None,
indexed_storage_internal_node_k: None,
free_space_address: None,
driver_info_address: None,
consistency_flags: 0,
superblock_extension_address: Some(u64::MAX),
checksum: None,
page_size: Some(4096),
};
let bytes = sb.serialize();
let parsed = Superblock::parse(&bytes, 0).unwrap();
assert_eq!(parsed.version, 4);
assert_eq!(parsed.page_size, Some(4096));
assert_eq!(parsed.eof_address, 1024);
assert_eq!(parsed.root_group_address, 96);
}
#[test]
fn serialize_v3_unchanged_by_page_size_field() {
// v3 (page_size: None) must serialize identically to before this feature existed.
let sb = Superblock {
version: 3,
offset_size: 8,
length_size: 8,
base_address: 0,
eof_address: 2048,
root_group_address: 96,
group_leaf_node_k: None,
group_internal_node_k: None,
indexed_storage_internal_node_k: None,
free_space_address: None,
driver_info_address: None,
consistency_flags: 0,
superblock_extension_address: Some(u64::MAX),
checksum: None,
page_size: None,
};
let bytes = sb.serialize();
// sig(8) + version/offset/length/flags(4) + 4 addresses(8 each) + checksum(4)
assert_eq!(bytes.len(), 8 + 4 + 4 * 8 + 4);
let parsed = Superblock::parse(&bytes, 0).unwrap();
assert_eq!(parsed.version, 3);
assert_eq!(parsed.page_size, None);
}
}