Merge branch 'feat/p3-storage-trait' into feat/p3-range-zfp-edit

# Conflicts:
#	CHANGELOG.md
#	crates/clawhdf5-format/src/attribute.rs
#	crates/clawhdf5-format/src/btree_v1.rs
#	crates/clawhdf5-format/src/data_layout.rs
#	crates/clawhdf5-format/src/extensible_array.rs
#	crates/clawhdf5-format/src/fixed_array.rs
#	crates/clawhdf5-format/src/fractal_heap.rs
#	crates/clawhdf5-format/src/local_heap.rs
#	crates/clawhdf5-format/src/shared_message.rs
This commit is contained in:
osobh
2026-09-26 14:51:51 -05:00
26 changed files with 3480 additions and 497 deletions
+4
View File
@@ -31,6 +31,10 @@ harness = false
name = "parallel_bench"
harness = false
[[bench]]
name = "local_metadata_bench"
harness = false
[features]
default = ["mmap", "provenance", "lzf"]
mmap = ["clawhdf5-io/mmap"]
@@ -0,0 +1,91 @@
//! Metadata parsing over an in-memory file: the local fast path that the
//! range-read `Storage` migration must not slow down
//! (`docs/design/range-reads.md`, "Keeping the local fast path").
//!
//! Only the `&[u8]` APIs are used, so the same file builds against older
//! revisions for an A/B comparison. The input is a version-1 (symbol table)
//! file with 400 groups, written by h5py with `libver='earliest'` and a
//! 512-byte user block (`clawhdf5-format/tests/fixtures/v1_groups_400.h5`).
use clawhdf5::{File, Group};
use clawhdf5_format::btree_v1::collect_symbol_table_nodes;
use clawhdf5_format::message_type::MessageType;
use clawhdf5_format::object_header::ObjectHeader;
use clawhdf5_format::superblock::Superblock;
use clawhdf5_format::symbol_table::{SymbolTableMessage, SymbolTableNode};
use criterion::{Criterion, criterion_group, criterion_main};
use std::hint::black_box;
const FIXTURE: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../clawhdf5-format/tests/fixtures/v1_groups_400.h5"
);
fn walk(g: &Group<'_>, objs: &mut usize) {
for name in g.datasets().unwrap_or_default() {
*objs += 1;
if let Ok(ds) = g.dataset(&name) {
let _ = black_box(ds.shape());
let _ = black_box(ds.dtype());
let _ = black_box(ds.attrs());
}
}
for name in g.groups().unwrap_or_default() {
*objs += 1;
if let Ok(sub) = g.group(&name) {
walk(&sub, objs);
}
}
}
fn bench_local_metadata(c: &mut Criterion) {
let bytes = std::fs::read(FIXTURE).unwrap();
let (_, f) = clawhdf5_format::signature::split_user_block(&bytes).unwrap();
let sb = Superblock::parse(f, 0).unwrap();
let (os, ls) = (sb.offset_size, sb.length_size);
let root = ObjectHeader::parse(f, sb.root_group_address as usize, os, ls).unwrap();
let stm = root
.messages
.iter()
.find(|m| m.msg_type == MessageType::SymbolTable)
.map(|m| SymbolTableMessage::parse(&m.data, os).unwrap())
.unwrap();
let nodes = collect_symbol_table_nodes(f, stm.btree_address, os, ls).unwrap();
let headers: Vec<u64> = nodes
.iter()
.flat_map(|&a| SymbolTableNode::parse(f, a as usize, os).unwrap().entries)
.map(|e| e.object_header_address)
.collect();
assert_eq!(headers.len(), 401);
let mut g = c.benchmark_group("local_metadata");
g.bench_function("object_header_parse_x401", |b| {
b.iter(|| {
for &a in &headers {
black_box(ObjectHeader::parse(f, a as usize, os, ls).unwrap());
}
})
});
g.bench_function("snod_parse_all", |b| {
b.iter(|| {
for &a in &nodes {
black_box(SymbolTableNode::parse(f, a as usize, os).unwrap());
}
})
});
g.bench_function("btree_v1_walk", |b| {
b.iter(|| black_box(collect_symbol_table_nodes(f, stm.btree_address, os, ls).unwrap()))
});
let file = File::open(FIXTURE).unwrap();
g.bench_function("facade_list_400_groups", |b| {
b.iter(|| {
let mut n = 0;
walk(&file.root(), &mut n);
assert_eq!(n, 401);
})
});
g.finish();
}
criterion_group!(benches, bench_local_metadata);
criterion_main!(benches);
+5
View File
@@ -6,7 +6,12 @@ use clawhdf5_format::error::FormatError;
use clawhdf5_format::message_type::MessageType;
/// Errors that can occur when using the high-level API.
///
/// Non-exhaustive: new failure modes add variants, so a `match` needs a
/// wildcard arm. Failures of a [`Storage`](clawhdf5_format::storage::Storage)
/// backend arrive as `Error::Format(FormatError::Storage(..))`.
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
/// I/O error from the filesystem.
Io(std::io::Error),