use std::fs;
use std::io::BufReader;
use std::sync::Arc;
use parking_lot::Mutex;
use crate::error::{Error, Result};
use crate::format::messages::attribute::AttributeMessage;
use crate::format::object_header::{self, ObjectHeader};
use crate::hl::file::FileInner;
#[derive(Debug, Clone)]
pub struct Attribute {
pub msg: AttributeMessage,
}
impl Attribute {
pub fn name(&self) -> &str {
&self.msg.name
}
pub fn raw_data(&self) -> &[u8] {
&self.msg.data
}
pub fn element_size(&self) -> usize {
self.msg.datatype.size as usize
}
pub fn shape(&self) -> &[u64] {
&self.msg.dataspace.dims
}
pub fn read_scalar_f64(&self) -> Option<f64> {
if self.msg.data.len() >= 8 {
Some(f64::from_le_bytes(self.msg.data[..8].try_into().ok()?))
} else {
None
}
}
pub fn read_scalar_i64(&self) -> Option<i64> {
if self.msg.data.len() >= 8 {
Some(i64::from_le_bytes(self.msg.data[..8].try_into().ok()?))
} else if self.msg.data.len() >= 4 {
Some(i32::from_le_bytes(self.msg.data[..4].try_into().ok()?) as i64)
} else {
None
}
}
pub fn read<T: crate::hl::types::H5Type>(&self) -> crate::Result<Vec<T>> {
crate::hl::types::bytes_to_vec::<T>(self.msg.data.clone())
}
pub fn read_scalar<T: crate::hl::types::H5Type>(&self) -> crate::Result<T> {
let vec = self.read::<T>()?;
if vec.is_empty() {
return Err(crate::Error::InvalidFormat("no data for scalar read".into()));
}
Ok(vec[0])
}
pub fn read_string(&self) -> String {
let data = &self.msg.data;
let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
String::from_utf8_lossy(&data[..end]).trim_end().to_string()
}
}
pub(crate) fn collect_attributes(
inner: &Arc<Mutex<FileInner<BufReader<fs::File>>>>,
addr: u64,
) -> Result<Vec<Attribute>> {
let mut guard = inner.lock();
let oh = ObjectHeader::read_at(&mut guard.reader, addr)?;
let mut attrs = Vec::new();
for msg in &oh.messages {
if msg.msg_type == object_header::MSG_ATTRIBUTE {
match AttributeMessage::decode(&msg.data) {
Ok(attr_msg) => attrs.push(Attribute { msg: attr_msg }),
Err(e) => {
eprintln!("Warning: failed to decode attribute: {e}");
}
}
}
}
Ok(attrs)
}
pub(crate) fn attr_names(
inner: &Arc<Mutex<FileInner<BufReader<fs::File>>>>,
addr: u64,
) -> Result<Vec<String>> {
let attrs = collect_attributes(inner, addr)?;
Ok(attrs.into_iter().map(|a| a.msg.name).collect())
}
pub(crate) fn get_attr(
inner: &Arc<Mutex<FileInner<BufReader<fs::File>>>>,
addr: u64,
name: &str,
) -> Result<Attribute> {
let attrs = collect_attributes(inner, addr)?;
attrs
.into_iter()
.find(|a| a.msg.name == name)
.ok_or_else(|| Error::InvalidFormat(format!("attribute '{name}' not found")))
}