Fix silent wrong data and libhdf5 interop found by the HDF5 audit #11
@@ -888,9 +888,9 @@ fn native_le_to_vec<T: Copy>(raw: &[u8], count: usize) -> Vec<T> {
|
||||
|
||||
/// Convert raw bytes to `f64` values.
|
||||
pub fn read_as_f64(raw: &[u8], datatype: &Datatype) -> Result<Vec<f64>, FormatError> {
|
||||
// Array datatypes (e.g. an array-typed compound member) are read as a flat
|
||||
// sequence of their base elements.
|
||||
if let Datatype::Array { base_type, .. } = datatype {
|
||||
// Array datatypes read as a flat sequence of their base elements, and
|
||||
// enumerations (h5py's bool among them) as their integer values.
|
||||
if let Datatype::Array { base_type, .. } | Datatype::Enumeration { base_type, .. } = datatype {
|
||||
return read_as_f64(raw, base_type);
|
||||
}
|
||||
ensure_numeric(datatype, "FloatingPoint or FixedPoint")?;
|
||||
@@ -1030,7 +1030,9 @@ fn decode_scalar(
|
||||
/// unsigned is 0), and floating-point data is truncated toward zero and
|
||||
/// saturated, with NaN read as 0.
|
||||
pub fn read_as_i64(raw: &[u8], datatype: &Datatype) -> Result<Vec<i64>, FormatError> {
|
||||
if let Datatype::Array { base_type, .. } = datatype {
|
||||
// Array datatypes read as a flat sequence of their base elements, and
|
||||
// enumerations (h5py's bool among them) as their integer values.
|
||||
if let Datatype::Array { base_type, .. } | Datatype::Enumeration { base_type, .. } = datatype {
|
||||
return read_as_i64(raw, base_type);
|
||||
}
|
||||
ensure_numeric(datatype, "FixedPoint (signed)")?;
|
||||
@@ -1075,7 +1077,9 @@ pub fn read_as_i64(raw: &[u8], datatype: &Datatype) -> Result<Vec<i64>, FormatEr
|
||||
/// unsigned is 0), and floating-point data is truncated toward zero and
|
||||
/// saturated, with NaN read as 0.
|
||||
pub fn read_as_u64(raw: &[u8], datatype: &Datatype) -> Result<Vec<u64>, FormatError> {
|
||||
if let Datatype::Array { base_type, .. } = datatype {
|
||||
// Array datatypes read as a flat sequence of their base elements, and
|
||||
// enumerations (h5py's bool among them) as their integer values.
|
||||
if let Datatype::Array { base_type, .. } | Datatype::Enumeration { base_type, .. } = datatype {
|
||||
return read_as_u64(raw, base_type);
|
||||
}
|
||||
ensure_numeric(datatype, "FixedPoint (unsigned)")?;
|
||||
@@ -1098,7 +1102,9 @@ pub fn read_as_u64(raw: &[u8], datatype: &Datatype) -> Result<Vec<u64>, FormatEr
|
||||
|
||||
/// Convert raw bytes to `f32` values.
|
||||
pub fn read_as_f32(raw: &[u8], datatype: &Datatype) -> Result<Vec<f32>, FormatError> {
|
||||
if let Datatype::Array { base_type, .. } = datatype {
|
||||
// Array datatypes read as a flat sequence of their base elements, and
|
||||
// enumerations (h5py's bool among them) as their integer values.
|
||||
if let Datatype::Array { base_type, .. } | Datatype::Enumeration { base_type, .. } = datatype {
|
||||
return read_as_f32(raw, base_type);
|
||||
}
|
||||
ensure_numeric(datatype, "FloatingPoint")?;
|
||||
@@ -1183,7 +1189,9 @@ pub fn read_as_f32(raw: &[u8], datatype: &Datatype) -> Result<Vec<f32>, FormatEr
|
||||
/// unsigned is 0), and floating-point data is truncated toward zero and
|
||||
/// saturated, with NaN read as 0.
|
||||
pub fn read_as_i32(raw: &[u8], datatype: &Datatype) -> Result<Vec<i32>, FormatError> {
|
||||
if let Datatype::Array { base_type, .. } = datatype {
|
||||
// Array datatypes read as a flat sequence of their base elements, and
|
||||
// enumerations (h5py's bool among them) as their integer values.
|
||||
if let Datatype::Array { base_type, .. } | Datatype::Enumeration { base_type, .. } = datatype {
|
||||
return read_as_i32(raw, base_type);
|
||||
}
|
||||
ensure_numeric(datatype, "FixedPoint")?;
|
||||
|
||||
@@ -273,3 +273,41 @@ with h5py.File("{path}", "r") as f:
|
||||
assert_eq!(got32, want32, "{name} as f32");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_and_bool_datasets_read_as_their_integer_values() {
|
||||
// Enumerations (h5py stores bool as an enum of int8) were refused by the
|
||||
// numeric readers with a type mismatch.
|
||||
skip_if_no_python!();
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("enums.h5");
|
||||
let script = format!(
|
||||
r#"{PRELUDE}
|
||||
with h5py.File("{path}", "w") as f:
|
||||
f.create_dataset("bool", data=np.array([True, False, True]))
|
||||
e = h5py.enum_dtype({{"RED": 0, "GREEN": 7, "BLUE": -3}}, basetype=">i2")
|
||||
f.create_dataset("enum_i2be", data=np.array([0, 7, -3, 7], ">i2"), dtype=e)
|
||||
e = h5py.enum_dtype({{"LOW": 0, "HIGH": 200}}, basetype="u1")
|
||||
f.create_dataset("enum_u1", data=np.array([200, 0, 200], "u1"), dtype=e)
|
||||
e = h5py.enum_dtype({{"A": -(2**40), "B": 2**40}}, basetype="<i8")
|
||||
f.create_dataset("enum_i8", data=np.array([2**40, -(2**40)], "<i8"), dtype=e)
|
||||
with h5py.File("{path}", "r") as f:
|
||||
for name in ("bool", "enum_i2be", "enum_u1", "enum_i8"):
|
||||
emit(name, np.asarray(f[name][()]).astype(np.int64))
|
||||
"#,
|
||||
path = path.display()
|
||||
);
|
||||
let expected = run_python(&script);
|
||||
|
||||
let file = File::open(&path).unwrap();
|
||||
for name in ["bool", "enum_i2be", "enum_u1", "enum_i8"] {
|
||||
let ds = file.dataset(name).unwrap();
|
||||
let want: Vec<i64> = parse(&expected[name]);
|
||||
assert_eq!(ds.read_i64().unwrap(), want, "{name} as i64");
|
||||
let want_f64: Vec<f64> = want.iter().map(|&v| v as f64).collect();
|
||||
assert_eq!(ds.read_f64().unwrap(), want_f64, "{name} as f64");
|
||||
}
|
||||
let bools = file.dataset("bool").unwrap();
|
||||
assert_eq!(bools.read_u64().unwrap(), vec![1, 0, 1]);
|
||||
assert_eq!(bools.read_i32().unwrap(), vec![1, 0, 1]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user