use crate::error::{Error, Result};
use crate::format::messages::dataspace::DataspaceMessage;
use crate::format::messages::datatype::DatatypeMessage;
#[derive(Debug, Clone)]
pub struct AttributeMessage {
pub version: u8,
pub name: String,
pub datatype: DatatypeMessage,
pub dataspace: DataspaceMessage,
pub data: Vec<u8>,
}
impl AttributeMessage {
pub fn decode(raw: &[u8]) -> Result<Self> {
if raw.len() < 6 {
return Err(Error::InvalidFormat("attribute message too short".into()));
}
let version = raw[0];
match version {
1 => Self::decode_v1(raw),
2 => Self::decode_v2(raw),
3 => Self::decode_v3(raw),
_ => Err(Error::Unsupported(format!(
"attribute message version {version}"
))),
}
}
fn decode_v1(raw: &[u8]) -> Result<Self> {
let name_size = u16::from_le_bytes([raw[2], raw[3]]) as usize;
let dt_size = u16::from_le_bytes([raw[4], raw[5]]) as usize;
let ds_size = u16::from_le_bytes([raw[6], raw[7]]) as usize;
let mut pos = 8;
let name_end = raw[pos..pos + name_size]
.iter()
.position(|&b| b == 0)
.unwrap_or(name_size);
let name = String::from_utf8_lossy(&raw[pos..pos + name_end]).to_string();
let name_padded = (name_size + 7) & !7;
pos += name_padded;
let datatype = DatatypeMessage::decode(&raw[pos..pos + dt_size])?;
let dt_padded = (dt_size + 7) & !7;
pos += dt_padded;
let dataspace = DataspaceMessage::decode(&raw[pos..pos + ds_size])?;
let ds_padded = (ds_size + 7) & !7;
pos += ds_padded;
let data = raw[pos..].to_vec();
Ok(Self {
version: 1,
name,
datatype,
dataspace,
data,
})
}
fn decode_v2(raw: &[u8]) -> Result<Self> {
let name_size = u16::from_le_bytes([raw[2], raw[3]]) as usize;
let dt_size = u16::from_le_bytes([raw[4], raw[5]]) as usize;
let ds_size = u16::from_le_bytes([raw[6], raw[7]]) as usize;
let mut pos = 8;
let name_end = raw[pos..pos + name_size]
.iter()
.position(|&b| b == 0)
.unwrap_or(name_size);
let name = String::from_utf8_lossy(&raw[pos..pos + name_end]).to_string();
pos += name_size;
let datatype = DatatypeMessage::decode(&raw[pos..pos + dt_size])?;
pos += dt_size;
let dataspace = DataspaceMessage::decode(&raw[pos..pos + ds_size])?;
pos += ds_size;
let data = raw[pos..].to_vec();
Ok(Self {
version: 2,
name,
datatype,
dataspace,
data,
})
}
fn decode_v3(raw: &[u8]) -> Result<Self> {
let _flags = raw[1];
let name_size = u16::from_le_bytes([raw[2], raw[3]]) as usize;
let dt_size = u16::from_le_bytes([raw[4], raw[5]]) as usize;
let ds_size = u16::from_le_bytes([raw[6], raw[7]]) as usize;
let _encoding = raw[8]; let mut pos = 9;
let name_end = raw[pos..pos + name_size]
.iter()
.position(|&b| b == 0)
.unwrap_or(name_size);
let name = String::from_utf8_lossy(&raw[pos..pos + name_end]).to_string();
pos += name_size;
let datatype = DatatypeMessage::decode(&raw[pos..pos + dt_size])?;
pos += dt_size;
let dataspace = DataspaceMessage::decode(&raw[pos..pos + ds_size])?;
pos += ds_size;
let data = raw[pos..].to_vec();
Ok(Self {
version: 3,
name,
datatype,
dataspace,
data,
})
}
pub fn num_elements(&self) -> u64 {
if self.dataspace.dims.is_empty() {
1 } else {
self.dataspace.dims.iter().product()
}
}
pub fn data_size(&self) -> usize {
self.num_elements() as usize * self.datatype.size as usize
}
}