format: chunk grids with a zero extent have no chunks (no division by zero)

A Fixed or Extensible Array index whose maximum extent (the current one
when no maximum is recorded) is 0 along a dimension has a zero stride for
every dimension before it; ChunkGrid::offsets divided by it. The unfixed
editor made such files by resizing a clawhdf5-written dataset to a zero
extent: `h5rs check` panicked and the next resize raised an internal
error (12 of the reviewer's random-edit seeds 10..39). Such an index has
no slot for any chunk of the dataset; offsets now returns None.

Tests: chunk_grid::zero_extent_has_no_chunks; edit_interop's
zero_extent_resizes_without_a_recorded_maximum on a file the unfixed
editor left (fixture) and on a 2.7.0-written file taken through zero
extents with `h5rs check --data` and h5dump at every step; test_edit.py
random edits on seeds 10..39 of a clawhdf5-written file.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-27 07:40:11 -05:00
co-authored by Claude Opus 5.5
parent 1f7651644b
commit bdf584abb2
5 changed files with 116 additions and 0 deletions
+28
View File
@@ -135,6 +135,12 @@ impl ChunkGrid {
let mut rem = index;
for p in 0..rank {
let d = self.order[p];
// A zero stride: a later dimension has no chunks (its maximum,
// or with none recorded its current extent, is 0), so no slot of
// the index is a chunk of the dataset.
if self.down[p] == 0 {
return None;
}
let scaled = rem / self.down[p];
rem %= self.down[p];
if scaled >= self.cur_chunks[d] {
@@ -193,6 +199,28 @@ mod tests {
assert_eq!(g.offsets(11), Some(vec![2, 3]));
}
#[test]
fn zero_extent_has_no_chunks() {
// No maximum recorded and a zero current dimension: every stride
// before it is 0 (this divided by zero).
let g = ChunkGrid::fixed_array(&[1, 0], None, &[6, 6]).unwrap();
for i in 0..16 {
assert_eq!(g.offsets(i), None);
}
let g = ChunkGrid::fixed_array(&[0, 0, 3], Some(&[4, 0, 3]), &[2, 2, 3]).unwrap();
for i in 0..16 {
assert_eq!(g.offsets(i), None);
}
let g = ChunkGrid::extensible_array(&[0, 5], Some(&[u64::MAX, 0]), &[2, 2]).unwrap();
for i in 0..16 {
assert_eq!(g.offsets(i), None);
}
// A zero last dimension leaves the other strides alone.
let g = ChunkGrid::fixed_array(&[4, 0], Some(&[4, 6]), &[2, 3]).unwrap();
assert_eq!(g.offsets(0), None);
assert_eq!(g.linear_index(&[1, 1]), 3);
}
#[test]
fn rejects_two_unlimited_dims_after_the_first() {
assert!(ChunkGrid::fixed_array(&[4, 6], Some(&[u64::MAX, u64::MAX]), &[2, 3]).is_err());