format: VlResolver::element_in and string_element_in over any Storage
VlResolver::element and string_element return slices of the whole file, so they exist only for a resolver over &[u8]. Their *_in forms work for any Storage (a remote file): the element's bytes borrowed from the resolver's cache of heap collections, with the same null-element, NUL and size checks. h5rs decodes variable-length values with them. Test: over a read_at-only storage they give what element/string_element give over the slice, for a string with an embedded NUL, a null element and an element whose heap object has the wrong size. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -305,6 +305,24 @@ impl<'a, S: crate::storage::Storage + ?Sized> VlResolver<'a, S> {
|
|||||||
Ok(Some(data))
|
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<Option<&[u8]>, 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<Option<&[u8]>, FormatError> {
|
||||||
|
Ok(self.element_in(elem, 1)?.map(cut_at_nul))
|
||||||
|
}
|
||||||
|
|
||||||
/// The strings of the variable-length string elements in `raw`, as
|
/// The strings of the variable-length string elements in `raw`, as
|
||||||
/// bytes. A string ends at its first NUL, as libhdf5 returns it (it
|
/// bytes. A string ends at its first NUL, as libhdf5 returns it (it
|
||||||
/// converts each to a C string); a null element is empty.
|
/// 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]
|
#[test]
|
||||||
fn null_vl_element_zero_address() {
|
fn null_vl_element_zero_address() {
|
||||||
let mut raw = Vec::new();
|
let mut raw = Vec::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user