fix(format): resolve shared fill value messages instead of zero-filling
dataset_fill_value treated a shared Fill Value message as "no fill value", so unwritten storage of a dataset whose fill value lives in the file's shared-message (SOHM) heap read as zeros rather than its fill value. libhdf5 shares fill values whenever the file has a SOHM index for them. - fill_value::dataset_fill_value_in follows the reference (another object header, or the SOHM heap); read_full_with_fill and the facade's selection read use it. - dataset_fill_value, which has no file to follow a reference into, now returns UnresolvedSharedMessage for a shared message instead of None. - shared_message::load_sohm_table / message_data_with_sohm load the SOHM table from the superblock extension on demand. - parse_sohm_table skipped each index's leading version byte, reading every field one byte off; SOHM references could never resolve. Fixture shared_fill_value.h5 (HDF5 2.0, gen_shared_fill.py): sohm_b read [0,1,2,3,0,0,0,0] and now reads [0,1,2,3,-7,-7,-7,-7], as h5py does. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -98,15 +98,50 @@ pub fn parse_fill_value(msg: &HeaderMessage) -> Result<Option<Vec<u8>>, FormatEr
|
||||
|
||||
/// The fill value that applies to a dataset given its header messages. The new
|
||||
/// message wins over the old one when both are present.
|
||||
///
|
||||
/// A *shared* fill value message holds only a reference to the real message,
|
||||
/// which cannot be followed without the file: this returns
|
||||
/// [`FormatError::UnresolvedSharedMessage`] for one (it used to answer "zeros").
|
||||
/// Use [`dataset_fill_value_in`] when the file bytes are at hand.
|
||||
pub fn dataset_fill_value(messages: &[HeaderMessage]) -> Result<Option<Vec<u8>>, FormatError> {
|
||||
fill_value_from(messages, |_| Err(FormatError::UnresolvedSharedMessage))
|
||||
}
|
||||
|
||||
/// [`dataset_fill_value`] for a dataset in `file_data`, following a shared
|
||||
/// fill value message to where it lives: another object header, or the
|
||||
/// file's shared-message (SOHM) heap, as libhdf5 writes it when the file has
|
||||
/// a SOHM index for fill values.
|
||||
pub fn dataset_fill_value_in(
|
||||
file_data: &[u8],
|
||||
messages: &[HeaderMessage],
|
||||
offset_size: u8,
|
||||
length_size: u8,
|
||||
) -> Result<Option<Vec<u8>>, FormatError> {
|
||||
fill_value_from(messages, |msg| {
|
||||
crate::shared_message::message_data_with_sohm(file_data, msg, offset_size, length_size)
|
||||
.map(|data| data.into_owned())
|
||||
})
|
||||
}
|
||||
|
||||
fn fill_value_from(
|
||||
messages: &[HeaderMessage],
|
||||
resolve_shared: impl Fn(&HeaderMessage) -> Result<Vec<u8>, FormatError>,
|
||||
) -> Result<Option<Vec<u8>>, FormatError> {
|
||||
for wanted in [MessageType::FillValue, MessageType::FillValueOld] {
|
||||
if let Some(msg) = messages.iter().find(|m| m.msg_type == wanted) {
|
||||
if crate::shared_message::is_shared(msg.flags) {
|
||||
// A shared fill value is legal but vanishingly rare; treat it
|
||||
// as the default rather than misparsing the reference.
|
||||
return Ok(None);
|
||||
}
|
||||
if let Some(value) = parse_fill_value(msg)? {
|
||||
let value = if crate::shared_message::is_shared(msg.flags) {
|
||||
let data = resolve_shared(msg)?;
|
||||
parse_fill_value(&HeaderMessage {
|
||||
msg_type: msg.msg_type,
|
||||
size: data.len(),
|
||||
flags: msg.flags & !0x02,
|
||||
creation_order: msg.creation_order,
|
||||
data,
|
||||
})?
|
||||
} else {
|
||||
parse_fill_value(msg)?
|
||||
};
|
||||
if let Some(value) = value {
|
||||
return Ok(Some(value));
|
||||
}
|
||||
}
|
||||
@@ -174,7 +209,7 @@ pub fn read_full_with_fill<E: From<FormatError>>(
|
||||
{
|
||||
return Err(FormatError::ExternalDataFilesUnsupported.into());
|
||||
}
|
||||
let fill = dataset_fill_value(messages)?;
|
||||
let fill = dataset_fill_value_in(file_data, messages, offset_size, length_size)?;
|
||||
if !has_storage(layout) {
|
||||
return Ok(filled_dataset(dataspace, elem_size, fill.as_deref())?);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user