diff --git a/crates/clawhdf5-format/src/data_layout.rs b/crates/clawhdf5-format/src/data_layout.rs index 59a9065..480f459 100644 --- a/crates/clawhdf5-format/src/data_layout.rs +++ b/crates/clawhdf5-format/src/data_layout.rs @@ -7,6 +7,7 @@ use alloc::{format, string::String, vec::Vec}; use std::string::String; use crate::error::FormatError; +use crate::storage::Storage; /// A single VDS (Virtual Dataset) source mapping. /// @@ -309,6 +310,16 @@ impl DataLayout { &mut self, file_data: &[u8], length_size: u8, + ) -> Result<(), FormatError> { + self.resolve_vds_mappings_in(&file_data, length_size) + } + + /// [`Self::resolve_vds_mappings`] over any [`Storage`]: one read of the + /// global heap collection holding the mappings. + pub fn resolve_vds_mappings_in( + &mut self, + file_data: &dyn Storage, + length_size: u8, ) -> Result<(), FormatError> { if let DataLayout::Virtual { global_heap_address, @@ -318,11 +329,8 @@ impl DataLayout { } = self && let Some(addr) = *global_heap_address { - let coll = crate::global_heap::GlobalHeapCollection::parse( - file_data, - addr as usize, - length_size, - )?; + let coll = + crate::global_heap::GlobalHeapCollection::parse_in(file_data, addr, length_size)?; let obj = coll.get_object(*global_heap_index as u16).ok_or( FormatError::GlobalHeapObjectNotFound { collection_address: addr, @@ -1305,4 +1313,44 @@ mod tests { let blob = [0x01u8, 0, 0, 0, 0, 0, 0, 0, 0]; assert!(parse_vds_mappings(&blob, 8).unwrap().is_empty()); } + + /// A virtual dataset's mappings resolve identically through a + /// read_at-only CountingStorage, in two reads of the global heap. + #[test] + fn vds_mappings_through_storage_match_slice() { + use crate::message_type::MessageType; + use crate::object_header::ObjectHeader; + use crate::storage::CountingStorage; + let file: &[u8] = include_bytes!("../tests/fixtures/vds_same_file.h5"); + let sb = crate::superblock::Superblock::parse(file, 0).unwrap(); + let (os, ls) = (sb.offset_size, sb.length_size); + let storage = CountingStorage::new(file.to_vec()); + let mut virtuals = 0; + for child in + crate::group_v2::resolve_group_children(file, &sb, sb.root_group_address).unwrap() + { + let h = + ObjectHeader::parse(file, child.object_header_address as usize, os, ls).unwrap(); + let Some(msg) = h + .messages + .iter() + .find(|m| m.msg_type == MessageType::DataLayout) + else { + continue; + }; + let mut want = DataLayout::parse(&msg.data, os, ls).unwrap(); + if !matches!(want, DataLayout::Virtual { .. }) { + continue; + } + let mut got = want.clone(); + want.resolve_vds_mappings(file, ls).unwrap(); + storage.reset(); + got.resolve_vds_mappings_in(&storage, ls).unwrap(); + assert_eq!(format!("{got:?}"), format!("{want:?}")); + assert!(matches!(&got, DataLayout::Virtual { mappings, .. } if !mappings.is_empty())); + assert_eq!(storage.reads(), 2); + virtuals += 1; + } + assert!(virtuals >= 1); + } }