hdf5_reader/messages/
link.rs1use crate::error::{Error, Result};
8use crate::io::Cursor;
9
10#[derive(Debug, Clone)]
12pub enum LinkTarget {
13 Hard { address: u64 },
15 Soft { path: String },
17 External { filename: String, path: String },
19}
20
21#[derive(Debug, Clone)]
23pub struct LinkMessage {
24 pub name: String,
26 pub target: LinkTarget,
28 pub creation_order: Option<u64>,
30}
31
32pub fn parse(
34 cursor: &mut Cursor<'_>,
35 offset_size: u8,
36 _length_size: u8,
37 msg_size: usize,
38) -> Result<LinkMessage> {
39 let start = cursor.position();
40 let version = cursor.read_u8()?;
41
42 if version != 1 {
43 return Err(Error::UnsupportedLinkVersion(version));
44 }
45
46 let flags = cursor.read_u8()?;
47
48 let link_type = if (flags & 0x08) != 0 {
50 cursor.read_u8()?
51 } else {
52 0 };
54
55 let creation_order = if (flags & 0x04) != 0 {
57 Some(cursor.read_u64_le()?)
58 } else {
59 None
60 };
61
62 let _name_encoding = if (flags & 0x10) != 0 {
65 cursor.read_u8()?
66 } else {
67 0
68 };
69
70 let name_len_size = 1 << (flags & 0x03);
72 let name_len = cursor.read_uvar(name_len_size)? as usize;
73
74 let name = cursor.read_fixed_string(name_len)?;
75
76 let target = match link_type {
78 0 => {
79 let address = cursor.read_offset(offset_size)?;
81 LinkTarget::Hard { address }
82 }
83 1 => {
84 let path_len = cursor.read_u16_le()? as usize;
86 let path = cursor.read_fixed_string(path_len)?;
87 LinkTarget::Soft { path }
88 }
89 64 => {
90 let info_len = cursor.read_u16_le()? as usize;
92 let info_start = cursor.position();
93 let _ext_flags = cursor.read_u8()?;
95 let filename = cursor.read_null_terminated_string()?;
96 let path = cursor.read_null_terminated_string()?;
97 let info_consumed = (cursor.position() - info_start) as usize;
99 if info_consumed < info_len {
100 cursor.skip(info_len - info_consumed)?;
101 }
102 LinkTarget::External { filename, path }
103 }
104 t => return Err(Error::UnsupportedLinkType(t)),
105 };
106
107 let consumed = (cursor.position() - start) as usize;
108 if consumed < msg_size {
109 cursor.skip(msg_size - consumed)?;
110 }
111
112 Ok(LinkMessage {
113 name,
114 target,
115 creation_order,
116 })
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_parse_hard_link() {
125 let mut data = vec![
126 0x01, 0x00, ];
129 data.push(0x05);
131 data.extend_from_slice(b"hello");
133 data.extend_from_slice(&0x3000u64.to_le_bytes());
135
136 let mut cursor = Cursor::new(&data);
137 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
138 assert_eq!(msg.name, "hello");
139 assert!(msg.creation_order.is_none());
140 match &msg.target {
141 LinkTarget::Hard { address } => assert_eq!(*address, 0x3000),
142 other => panic!("expected Hard, got {:?}", other),
143 }
144 }
145
146 #[test]
147 fn test_parse_soft_link() {
148 let mut data = vec![
149 0x01, 0x08, ];
152 data.push(0x01);
154 data.push(0x03);
156 data.extend_from_slice(b"lnk");
158 data.extend_from_slice(&7u16.to_le_bytes());
160 data.extend_from_slice(b"/a/b/c\0");
161
162 let mut cursor = Cursor::new(&data);
163 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
164 assert_eq!(msg.name, "lnk");
165 match &msg.target {
166 LinkTarget::Soft { path } => assert_eq!(path, "/a/b/c"),
167 other => panic!("expected Soft, got {:?}", other),
168 }
169 }
170
171 #[test]
172 fn test_parse_hard_link_with_creation_order() {
173 let mut data = vec![
174 0x01, 0x04, ];
177 data.extend_from_slice(&42u64.to_le_bytes());
179 data.push(0x01);
181 data.push(b'x');
183 data.extend_from_slice(&0x5000u64.to_le_bytes());
185
186 let mut cursor = Cursor::new(&data);
187 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
188 assert_eq!(msg.name, "x");
189 assert_eq!(msg.creation_order, Some(42));
190 }
191}