hdf5_reader/messages/
link.rs1use crate::error::{Error, Result};
8use crate::io::Cursor;
9pub use hdf5_core::{LinkMessage, LinkTarget};
10
11pub fn parse(
13 cursor: &mut Cursor<'_>,
14 offset_size: u8,
15 _length_size: u8,
16 msg_size: usize,
17) -> Result<LinkMessage> {
18 let start = cursor.position();
19 let version = cursor.read_u8()?;
20
21 if version != 1 {
22 return Err(Error::UnsupportedLinkVersion(version));
23 }
24
25 let flags = cursor.read_u8()?;
26
27 let link_type = if (flags & 0x08) != 0 {
29 cursor.read_u8()?
30 } else {
31 0 };
33
34 let creation_order = if (flags & 0x04) != 0 {
36 Some(cursor.read_u64_le()?)
37 } else {
38 None
39 };
40
41 let _name_encoding = if (flags & 0x10) != 0 {
44 cursor.read_u8()?
45 } else {
46 0
47 };
48
49 let name_len_size = 1 << (flags & 0x03);
51 let name_len = cursor.read_uvar(name_len_size)? as usize;
52
53 let name = cursor.read_fixed_string(name_len)?;
54
55 let target = match link_type {
57 0 => {
58 let address = cursor.read_offset(offset_size)?;
60 LinkTarget::Hard { address }
61 }
62 1 => {
63 let path_len = cursor.read_u16_le()? as usize;
65 let path = cursor.read_fixed_string(path_len)?;
66 LinkTarget::Soft { path }
67 }
68 64 => {
69 let info_len = cursor.read_u16_le()? as usize;
71 let info_start = cursor.position();
72 let _ext_flags = cursor.read_u8()?;
74 let filename = cursor.read_null_terminated_string()?;
75 let path = cursor.read_null_terminated_string()?;
76 let info_consumed = (cursor.position() - info_start) as usize;
78 if info_consumed < info_len {
79 cursor.skip(info_len - info_consumed)?;
80 }
81 LinkTarget::External { filename, path }
82 }
83 t => return Err(Error::UnsupportedLinkType(t)),
84 };
85
86 let consumed = (cursor.position() - start) as usize;
87 if consumed < msg_size {
88 cursor.skip(msg_size - consumed)?;
89 }
90
91 Ok(LinkMessage {
92 name,
93 target,
94 creation_order,
95 })
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn parse_hard_link() {
104 let mut data = vec![
105 0x01, 0x00, ];
108 data.push(0x05);
110 data.extend_from_slice(b"hello");
112 data.extend_from_slice(&0x3000u64.to_le_bytes());
114
115 let mut cursor = Cursor::new(&data);
116 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
117 assert_eq!(msg.name, "hello");
118 assert!(msg.creation_order.is_none());
119 match &msg.target {
120 LinkTarget::Hard { address } => assert_eq!(*address, 0x3000),
121 other => panic!("expected Hard, got {:?}", other),
122 }
123 }
124
125 #[test]
126 fn parse_soft_link() {
127 let mut data = vec![
128 0x01, 0x08, ];
131 data.push(0x01);
133 data.push(0x03);
135 data.extend_from_slice(b"lnk");
137 data.extend_from_slice(&7u16.to_le_bytes());
139 data.extend_from_slice(b"/a/b/c\0");
140
141 let mut cursor = Cursor::new(&data);
142 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
143 assert_eq!(msg.name, "lnk");
144 match &msg.target {
145 LinkTarget::Soft { path } => assert_eq!(path, "/a/b/c"),
146 other => panic!("expected Soft, got {:?}", other),
147 }
148 }
149
150 #[test]
151 fn parse_hard_link_with_creation_order() {
152 let mut data = vec![
153 0x01, 0x04, ];
156 data.extend_from_slice(&42u64.to_le_bytes());
158 data.push(0x01);
160 data.push(b'x');
162 data.extend_from_slice(&0x5000u64.to_le_bytes());
164
165 let mut cursor = Cursor::new(&data);
166 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
167 assert_eq!(msg.name, "x");
168 assert_eq!(msg.creation_order, Some(42));
169 }
170}