fix(format): give empty string attributes a 1-byte type
An empty AttrValue::String (or a StringArray of empty strings) was
written with a size-0 fixed-length string type. libhdf5 rejects that
("invalid datatype size"), and the failure takes every attribute on the
object with it. Strings are now at least 1 byte, NUL-padded, which is
how h5py stores "" and reads back as "" in both h5py and our reader.
check_encodable also refuses a size-0 string type passed in directly.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -842,6 +842,9 @@ impl Datatype {
|
||||
opaque_tag_text(tag).len()
|
||||
)))
|
||||
}
|
||||
Datatype::String { size: 0, .. } => Err(FormatError::SerializationError(
|
||||
"fixed-length string datatype of size 0 (libhdf5 requires at least 1 byte)".into(),
|
||||
)),
|
||||
Datatype::Compound { members, .. } => members
|
||||
.iter()
|
||||
.try_for_each(|m| m.datatype.check_encodable()),
|
||||
|
||||
@@ -384,7 +384,11 @@ pub(crate) fn build_attr_message(name: &str, value: &AttrValue) -> AttributeMess
|
||||
raw_data: data.clone(),
|
||||
},
|
||||
AttrValue::String(s) => {
|
||||
let bytes = s.as_bytes();
|
||||
// A fixed-length string type must be at least 1 byte: libhdf5
|
||||
// rejects size 0 ("invalid datatype size") and with it every
|
||||
// attribute on the object. h5py stores "" as one NUL byte.
|
||||
let mut bytes = s.as_bytes().to_vec();
|
||||
bytes.resize(bytes.len().max(1), 0);
|
||||
AttributeMessage {
|
||||
name: name.to_string(),
|
||||
datatype: Datatype::String {
|
||||
@@ -393,11 +397,12 @@ pub(crate) fn build_attr_message(name: &str, value: &AttrValue) -> AttributeMess
|
||||
charset: CharacterSet::Utf8,
|
||||
},
|
||||
dataspace: scalar_ds(),
|
||||
raw_data: bytes.to_vec(),
|
||||
raw_data: bytes,
|
||||
}
|
||||
}
|
||||
AttrValue::StringArray(arr) => {
|
||||
let max_len = arr.iter().map(|s| s.len()).max().unwrap_or(0);
|
||||
// At least 1 byte per element, as for a single string.
|
||||
let max_len = arr.iter().map(|s| s.len()).max().unwrap_or(0).max(1);
|
||||
let mut raw = Vec::new();
|
||||
for s in arr {
|
||||
let mut b = s.as_bytes().to_vec();
|
||||
|
||||
Reference in New Issue
Block a user