use crate::error::{Error, Result};
use crate::io::Cursor;
pub use hdf5_core::{LinkMessage, LinkTarget};
pub fn parse(
cursor: &mut Cursor<'_>,
offset_size: u8,
_length_size: u8,
msg_size: usize,
) -> Result<LinkMessage> {
let start = cursor.position();
let version = cursor.read_u8()?;
if version != 1 {
return Err(Error::UnsupportedLinkVersion(version));
}
let flags = cursor.read_u8()?;
let link_type = if (flags & 0x08) != 0 {
cursor.read_u8()?
} else {
0 };
let creation_order = if (flags & 0x04) != 0 {
Some(cursor.read_u64_le()?)
} else {
None
};
let _name_encoding = if (flags & 0x10) != 0 {
cursor.read_u8()?
} else {
0
};
let name_len_size = 1 << (flags & 0x03);
let name_len = cursor.read_uvar(name_len_size)? as usize;
let name = cursor.read_fixed_string(name_len)?;
let target = match link_type {
0 => {
let address = cursor.read_offset(offset_size)?;
LinkTarget::Hard { address }
}
1 => {
let path_len = cursor.read_u16_le()? as usize;
let path = cursor.read_fixed_string(path_len)?;
LinkTarget::Soft { path }
}
64 => {
let info_len = cursor.read_u16_le()? as usize;
let info_start = cursor.position();
let _ext_flags = cursor.read_u8()?;
let filename = cursor.read_null_terminated_string()?;
let path = cursor.read_null_terminated_string()?;
let info_consumed = (cursor.position() - info_start) as usize;
if info_consumed < info_len {
cursor.skip(info_len - info_consumed)?;
}
LinkTarget::External { filename, path }
}
t => return Err(Error::UnsupportedLinkType(t)),
};
let consumed = (cursor.position() - start) as usize;
if consumed < msg_size {
cursor.skip(msg_size - consumed)?;
}
Ok(LinkMessage {
name,
target,
creation_order,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_hard_link() {
let mut data = vec![
0x01, 0x00, ];
data.push(0x05);
data.extend_from_slice(b"hello");
data.extend_from_slice(&0x3000u64.to_le_bytes());
let mut cursor = Cursor::new(&data);
let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
assert_eq!(msg.name, "hello");
assert!(msg.creation_order.is_none());
match &msg.target {
LinkTarget::Hard { address } => assert_eq!(*address, 0x3000),
other => panic!("expected Hard, got {:?}", other),
}
}
#[test]
fn parse_soft_link() {
let mut data = vec![
0x01, 0x08, ];
data.push(0x01);
data.push(0x03);
data.extend_from_slice(b"lnk");
data.extend_from_slice(&7u16.to_le_bytes());
data.extend_from_slice(b"/a/b/c\0");
let mut cursor = Cursor::new(&data);
let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
assert_eq!(msg.name, "lnk");
match &msg.target {
LinkTarget::Soft { path } => assert_eq!(path, "/a/b/c"),
other => panic!("expected Soft, got {:?}", other),
}
}
#[test]
fn parse_hard_link_with_creation_order() {
let mut data = vec![
0x01, 0x04, ];
data.extend_from_slice(&42u64.to_le_bytes());
data.push(0x01);
data.push(b'x');
data.extend_from_slice(&0x5000u64.to_le_bytes());
let mut cursor = Cursor::new(&data);
let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
assert_eq!(msg.name, "x");
assert_eq!(msg.creation_order, Some(42));
}
}