rust_hdf5/format/messages/
link_info.rs1use crate::format::bytes::read_le_addr as read_addr;
14use crate::format::{FormatContext, FormatError, FormatResult, UNDEF_ADDR};
15
16const VERSION: u8 = 0;
17const FLAG_MAX_CREATION_ORDER: u8 = 0x01;
18const FLAG_CREATION_ORDER_INDEXED: u8 = 0x02;
19
20#[derive(Debug, Clone, PartialEq)]
22pub struct LinkInfoMessage {
23 pub max_creation_order: Option<u64>,
25 pub fractal_heap_address: u64,
27 pub name_btree_address: u64,
29 pub creation_order_btree_address: Option<u64>,
31}
32
33impl LinkInfoMessage {
34 pub fn compact() -> Self {
36 Self {
37 max_creation_order: None,
38 fractal_heap_address: UNDEF_ADDR,
39 name_btree_address: UNDEF_ADDR,
40 creation_order_btree_address: None,
41 }
42 }
43
44 pub fn compact_with_creation_order() -> Self {
46 Self {
47 max_creation_order: Some(0),
48 fractal_heap_address: UNDEF_ADDR,
49 name_btree_address: UNDEF_ADDR,
50 creation_order_btree_address: None,
51 }
52 }
53
54 pub fn encode(&self, ctx: &FormatContext) -> Vec<u8> {
57 let sa = ctx.sizeof_addr as usize;
58
59 let mut flags: u8 = 0;
60 if self.max_creation_order.is_some() {
61 flags |= FLAG_MAX_CREATION_ORDER;
62 }
63 if self.creation_order_btree_address.is_some() {
64 flags |= FLAG_CREATION_ORDER_INDEXED;
65 }
66
67 let mut buf = Vec::with_capacity(2 + 8 + 3 * sa);
68 buf.push(VERSION);
69 buf.push(flags);
70
71 if let Some(max_co) = self.max_creation_order {
72 buf.extend_from_slice(&max_co.to_le_bytes());
73 }
74
75 buf.extend_from_slice(&self.fractal_heap_address.to_le_bytes()[..sa]);
76 buf.extend_from_slice(&self.name_btree_address.to_le_bytes()[..sa]);
77
78 if let Some(co_addr) = self.creation_order_btree_address {
79 buf.extend_from_slice(&co_addr.to_le_bytes()[..sa]);
80 }
81
82 buf
83 }
84
85 pub fn decode(buf: &[u8], ctx: &FormatContext) -> FormatResult<(Self, usize)> {
88 if buf.len() < 2 {
89 return Err(FormatError::BufferTooShort {
90 needed: 2,
91 available: buf.len(),
92 });
93 }
94
95 let version = buf[0];
96 if version != VERSION {
97 return Err(FormatError::InvalidVersion(version));
98 }
99
100 let flags = buf[1];
101 let has_max_co = (flags & FLAG_MAX_CREATION_ORDER) != 0;
102 let has_co_btree = (flags & FLAG_CREATION_ORDER_INDEXED) != 0;
103
104 let sa = ctx.sizeof_addr as usize;
105 let mut pos = 2;
106
107 let max_creation_order = if has_max_co {
108 check_len(buf, pos, 8)?;
109 let v = u64::from_le_bytes([
110 buf[pos],
111 buf[pos + 1],
112 buf[pos + 2],
113 buf[pos + 3],
114 buf[pos + 4],
115 buf[pos + 5],
116 buf[pos + 6],
117 buf[pos + 7],
118 ]);
119 pos += 8;
120 Some(v)
121 } else {
122 None
123 };
124
125 check_len(buf, pos, sa)?;
126 let fractal_heap_address = read_addr(&buf[pos..], sa);
127 pos += sa;
128
129 check_len(buf, pos, sa)?;
130 let name_btree_address = read_addr(&buf[pos..], sa);
131 pos += sa;
132
133 let creation_order_btree_address = if has_co_btree {
134 check_len(buf, pos, sa)?;
135 let v = read_addr(&buf[pos..], sa);
136 pos += sa;
137 Some(v)
138 } else {
139 None
140 };
141
142 Ok((
143 Self {
144 max_creation_order,
145 fractal_heap_address,
146 name_btree_address,
147 creation_order_btree_address,
148 },
149 pos,
150 ))
151 }
152}
153
154fn check_len(buf: &[u8], pos: usize, need: usize) -> FormatResult<()> {
157 if buf.len() < pos + need {
158 Err(FormatError::BufferTooShort {
159 needed: pos + need,
160 available: buf.len(),
161 })
162 } else {
163 Ok(())
164 }
165}
166
167#[cfg(test)]
170mod tests {
171 use super::*;
172
173 fn ctx8() -> FormatContext {
174 FormatContext {
175 sizeof_addr: 8,
176 sizeof_size: 8,
177 }
178 }
179
180 fn ctx4() -> FormatContext {
181 FormatContext {
182 sizeof_addr: 4,
183 sizeof_size: 4,
184 }
185 }
186
187 #[test]
188 fn roundtrip_compact() {
189 let msg = LinkInfoMessage::compact();
190 let encoded = msg.encode(&ctx8());
191 assert_eq!(encoded.len(), 18);
193 let (decoded, consumed) = LinkInfoMessage::decode(&encoded, &ctx8()).unwrap();
194 assert_eq!(consumed, 18);
195 assert_eq!(decoded, msg);
196 }
197
198 #[test]
199 fn roundtrip_compact_ctx4() {
200 let msg = LinkInfoMessage::compact();
201 let encoded = msg.encode(&ctx4());
202 assert_eq!(encoded.len(), 10);
204 let (decoded, consumed) = LinkInfoMessage::decode(&encoded, &ctx4()).unwrap();
205 assert_eq!(consumed, 10);
206 assert_eq!(decoded, msg);
207 }
208
209 #[test]
210 fn roundtrip_with_creation_order() {
211 let msg = LinkInfoMessage::compact_with_creation_order();
212 let encoded = msg.encode(&ctx8());
213 assert_eq!(encoded.len(), 26);
215 let (decoded, consumed) = LinkInfoMessage::decode(&encoded, &ctx8()).unwrap();
216 assert_eq!(consumed, 26);
217 assert_eq!(decoded, msg);
218 }
219
220 #[test]
221 fn roundtrip_full() {
222 let msg = LinkInfoMessage {
223 max_creation_order: Some(42),
224 fractal_heap_address: 0x1000,
225 name_btree_address: 0x2000,
226 creation_order_btree_address: Some(0x3000),
227 };
228 let encoded = msg.encode(&ctx8());
229 assert_eq!(encoded.len(), 34);
231 let (decoded, consumed) = LinkInfoMessage::decode(&encoded, &ctx8()).unwrap();
232 assert_eq!(consumed, 34);
233 assert_eq!(decoded, msg);
234 }
235
236 #[test]
237 fn roundtrip_undef_addresses() {
238 let msg = LinkInfoMessage {
239 max_creation_order: None,
240 fractal_heap_address: UNDEF_ADDR,
241 name_btree_address: UNDEF_ADDR,
242 creation_order_btree_address: None,
243 };
244 let encoded = msg.encode(&ctx4());
245 let (decoded, _) = LinkInfoMessage::decode(&encoded, &ctx4()).unwrap();
246 assert_eq!(decoded.fractal_heap_address, UNDEF_ADDR);
247 assert_eq!(decoded.name_btree_address, UNDEF_ADDR);
248 }
249
250 #[test]
251 fn decode_bad_version() {
252 let buf = [1u8, 0, 0, 0, 0, 0, 0, 0, 0, 0];
253 let err = LinkInfoMessage::decode(&buf, &ctx8()).unwrap_err();
254 match err {
255 FormatError::InvalidVersion(1) => {}
256 other => panic!("unexpected error: {:?}", other),
257 }
258 }
259
260 #[test]
261 fn decode_buffer_too_short() {
262 let buf = [0u8];
263 let err = LinkInfoMessage::decode(&buf, &ctx8()).unwrap_err();
264 match err {
265 FormatError::BufferTooShort { .. } => {}
266 other => panic!("unexpected error: {:?}", other),
267 }
268 }
269
270 #[test]
271 fn version_byte() {
272 let encoded = LinkInfoMessage::compact().encode(&ctx8());
273 assert_eq!(encoded[0], 0);
274 }
275}