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:
co-authored by
Claude Sonnet 5
parent
b1fc23e975
commit
6b1ea450f5
@@ -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 v0–v3.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user