format: monomorphise the Storage parsers so local files stay as fast
Every `*_in` core and the read helpers take `file: &S` with `S: Storage + ?Sized` instead of `&dyn Storage`, and the `&[u8]` wrappers pass the slice itself, so they compile to a `[u8]` instance: `as_contiguous()` inlines to `Some(self)` and each structure read is the slice code's bounds check again, with no indirect call. `&dyn Storage` still works (`S = dyn Storage`); there is one parser implementation. Also, so the structure reads cost no more than the slice checks did: - ObjectHeader::parse_in reads the prefix once (signature included) instead of the signature and then the prefix: two reads for a one-chunk header instead of three on a range backend; - the symbol-table node and group B-tree (v1) loops walk their entries with chunks_exact over the bytes read, and the node's redundant second bounds check is gone (the entries' read is the check, same error); - a version-1 header's message list is sized from its (capped) count. Same results and errors; the unit and equivalence tests are unchanged. New Criterion bench `clawhdf5/benches/local_metadata_bench.rs` over a 400-group version-1 file written by h5py (new fixture `v1_groups_400.h5`): ObjectHeader::parse, symbol-table nodes, the group B-tree walk and a facade listing, using only APIs that exist atf2ff2c4so it builds there for an A/B. Provisional A/B againstf2ff2c4(busy machine, not for docs): both builds linked into one binary and timed in alternation, 200 rounds; median ratio new/old: facade listing -0.5% to -3.5% (was +14%), ObjectHeader::parse +1% to +2% (was +25%), symbol-table nodes -18%, group B-tree walk -18%, local-heap names and resolve_group_children within +-1.5%. An old-vs-old-copy run shows +-2% from code layout alone. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -44,12 +44,12 @@ impl LocalHeap {
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<LocalHeap, FormatError> {
|
||||
Self::parse_in(&file_data, offset as u64, offset_size, length_size)
|
||||
Self::parse_in(file_data, offset as u64, offset_size, length_size)
|
||||
}
|
||||
|
||||
/// [`Self::parse`] over any [`Storage`]: one read of the header.
|
||||
pub fn parse_in(
|
||||
file: &dyn Storage,
|
||||
pub fn parse_in<S: Storage + ?Sized>(
|
||||
file: &S,
|
||||
offset: u64,
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
@@ -97,14 +97,14 @@ impl LocalHeap {
|
||||
/// The end of the list is `H5HL_FREE_NULL` (1); an all-ones value (the
|
||||
/// undefined address) is accepted as "no free list" too.
|
||||
pub fn validate_free_list(&self, file_data: &[u8], length_size: u8) -> Result<(), FormatError> {
|
||||
self.validate_free_list_in(&file_data, length_size)
|
||||
self.validate_free_list_in(file_data, length_size)
|
||||
}
|
||||
|
||||
/// [`Self::validate_free_list`] over any [`Storage`]: two small reads
|
||||
/// per free block.
|
||||
pub fn validate_free_list_in(
|
||||
pub fn validate_free_list_in<S: Storage + ?Sized>(
|
||||
&self,
|
||||
file: &dyn Storage,
|
||||
file: &S,
|
||||
length_size: u8,
|
||||
) -> Result<(), FormatError> {
|
||||
const FREE_NULL: u64 = 1;
|
||||
@@ -149,14 +149,14 @@ impl LocalHeap {
|
||||
|
||||
/// Read a null-terminated string from the heap's data segment at the given byte offset.
|
||||
pub fn read_string(&self, file_data: &[u8], string_offset: u64) -> Result<String, FormatError> {
|
||||
self.read_string_in(&file_data, string_offset)
|
||||
self.read_string_in(file_data, string_offset)
|
||||
}
|
||||
|
||||
/// [`Self::read_string`] over any [`Storage`]: one read, from the
|
||||
/// string to the end of the data segment.
|
||||
pub fn read_string_in(
|
||||
pub fn read_string_in<S: Storage + ?Sized>(
|
||||
&self,
|
||||
file: &dyn Storage,
|
||||
file: &S,
|
||||
string_offset: u64,
|
||||
) -> Result<String, FormatError> {
|
||||
let file_len = len_usize(file);
|
||||
|
||||
Reference in New Issue
Block a user