diff --git a/crates/clawhdf5-format/src/vl_data.rs b/crates/clawhdf5-format/src/vl_data.rs index f37638b..378ada4 100644 --- a/crates/clawhdf5-format/src/vl_data.rs +++ b/crates/clawhdf5-format/src/vl_data.rs @@ -305,6 +305,24 @@ impl<'a, S: crate::storage::Storage + ?Sized> VlResolver<'a, S> { Ok(Some(data)) } + /// [`VlResolver::element`] over any storage: the element's bytes + /// (borrowed from the resolver's cache of heap collections, so they + /// live until the next call), or `None` for a null element. + pub fn element_in( + &mut self, + elem: &[u8], + base_size: usize, + ) -> Result, FormatError> { + let vl = parse_vl_references(elem, 1, self.offset_size)?; + self.resolve(&vl[0], base_size) + } + + /// [`VlResolver::string_element`] over any storage (see + /// [`element_in`](Self::element_in)). + pub fn string_element_in(&mut self, elem: &[u8]) -> Result, FormatError> { + Ok(self.element_in(elem, 1)?.map(cut_at_nul)) + } + /// The strings of the variable-length string elements in `raw`, as /// bytes. A string ends at its first NUL, as libhdf5 returns it (it /// converts each to a C string); a null element is empty. @@ -645,6 +663,32 @@ mod tests { } } + #[test] + fn element_in_over_a_storage_matches_element_over_a_slice() { + let mut file_data = vec![0u8; 512]; + build_gcol_at(&mut file_data, 256, &[(1, b"Alice\0x"), (2, b"Bob")]); + let mut raw = build_vl_refs(&["Alice\0x", "Bob"], 256, 1, 8); + raw.extend(element(0, 0, 0, 8)); // null + raw.extend(element(9, 256, 1, 8)); // wrong length: an error + let storage = crate::storage::CountingStorage::new(file_data.clone()); + let dynamic: &dyn crate::storage::Storage = &storage; + let mut slice = VlResolver::new(&file_data, 8, 8); + let mut any = VlResolver::new_in(dynamic, 8, 8); + for e in raw.chunks(16) { + let want = slice.element(e, 1).map(|o| o.map(<[u8]>::to_vec)); + let got = any.element_in(e, 1).map(|o| o.map(<[u8]>::to_vec)); + assert_eq!(format!("{want:?}"), format!("{got:?}")); + let want = slice.string_element(e).map(|o| o.map(<[u8]>::to_vec)); + let got = any.string_element_in(e).map(|o| o.map(<[u8]>::to_vec)); + assert_eq!(format!("{want:?}"), format!("{got:?}")); + } + assert_eq!( + any.string_element_in(&raw[..16]).unwrap(), + Some(&b"Alice"[..]) + ); + assert!(storage.reads() > 0); + } + #[test] fn null_vl_element_zero_address() { let mut raw = Vec::new();