1use crate::error::FormatError;
4
5#[derive(Debug, Clone, PartialEq)]
7pub struct LinkInfoMessage {
8 pub max_creation_order: Option<u64>,
10 pub fractal_heap_address: Option<u64>,
12 pub btree_name_index_address: Option<u64>,
14 pub btree_creation_order_address: Option<u64>,
16}
17
18fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
19 let s = size as usize;
20 if pos + s > data.len() {
21 return Err(FormatError::UnexpectedEof {
22 expected: pos + s,
23 available: data.len(),
24 });
25 }
26 Ok(match size {
27 2 => u16::from_le_bytes([data[pos], data[pos + 1]]) as u64,
28 4 => u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as u64,
29 8 => u64::from_le_bytes([
30 data[pos],
31 data[pos + 1],
32 data[pos + 2],
33 data[pos + 3],
34 data[pos + 4],
35 data[pos + 5],
36 data[pos + 6],
37 data[pos + 7],
38 ]),
39 _ => return Err(FormatError::InvalidOffsetSize(size)),
40 })
41}
42
43fn is_undefined(val: u64, offset_size: u8) -> bool {
44 match offset_size {
45 2 => val == 0xFFFF,
46 4 => val == 0xFFFF_FFFF,
47 8 => val == 0xFFFF_FFFF_FFFF_FFFF,
48 _ => false,
49 }
50}
51
52fn ensure_len(data: &[u8], pos: usize, needed: usize) -> Result<(), FormatError> {
53 match pos.checked_add(needed) {
54 Some(end) if end <= data.len() => Ok(()),
55 _ => Err(FormatError::UnexpectedEof {
56 expected: pos.saturating_add(needed),
57 available: data.len(),
58 }),
59 }
60}
61
62impl LinkInfoMessage {
63 pub fn parse(data: &[u8], offset_size: u8) -> Result<LinkInfoMessage, FormatError> {
65 ensure_len(data, 0, 2)?;
66
67 let version = data[0];
68 if version != 0 {
69 return Err(FormatError::InvalidLinkInfoVersion(version));
70 }
71
72 let flags = data[1];
73 let has_max_creation_order = flags & 0x01 != 0;
74 let has_creation_order_index = flags & 0x02 != 0;
75
76 let mut pos = 2;
77
78 let max_creation_order = if has_max_creation_order {
79 ensure_len(data, pos, 8)?;
80 let v = u64::from_le_bytes([
81 data[pos],
82 data[pos + 1],
83 data[pos + 2],
84 data[pos + 3],
85 data[pos + 4],
86 data[pos + 5],
87 data[pos + 6],
88 data[pos + 7],
89 ]);
90 pos += 8;
91 Some(v)
92 } else {
93 None
94 };
95
96 let fh_addr = read_offset(data, pos, offset_size)?;
97 pos += offset_size as usize;
98 let fractal_heap_address = if is_undefined(fh_addr, offset_size) {
99 None
100 } else {
101 Some(fh_addr)
102 };
103
104 let btree_addr = read_offset(data, pos, offset_size)?;
105 pos += offset_size as usize;
106 let btree_name_index_address = if is_undefined(btree_addr, offset_size) {
107 None
108 } else {
109 Some(btree_addr)
110 };
111
112 let btree_creation_order_address = if has_creation_order_index {
113 let addr = read_offset(data, pos, offset_size)?;
114 if is_undefined(addr, offset_size) {
115 None
116 } else {
117 Some(addr)
118 }
119 } else {
120 None
121 };
122
123 Ok(LinkInfoMessage {
124 max_creation_order,
125 fractal_heap_address,
126 btree_name_index_address,
127 btree_creation_order_address,
128 })
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn compact_storage() {
138 let mut data = vec![0u8; 2 + 8 + 8];
140 data[0] = 0; data[1] = 0; data[2..10].copy_from_slice(&0xFFFF_FFFF_FFFF_FFFFu64.to_le_bytes());
144 data[10..18].copy_from_slice(&0xFFFF_FFFF_FFFF_FFFFu64.to_le_bytes());
146
147 let msg = LinkInfoMessage::parse(&data, 8).unwrap();
148 assert_eq!(msg.fractal_heap_address, None);
149 assert_eq!(msg.btree_name_index_address, None);
150 assert_eq!(msg.max_creation_order, None);
151 assert_eq!(msg.btree_creation_order_address, None);
152 }
153
154 #[test]
155 fn dense_storage_with_creation_order() {
156 let mut data = Vec::new();
158 data.push(0); data.push(0x03); data.extend_from_slice(&42u64.to_le_bytes()); data.extend_from_slice(&0x1000u64.to_le_bytes()); data.extend_from_slice(&0x2000u64.to_le_bytes()); data.extend_from_slice(&0x3000u64.to_le_bytes()); let msg = LinkInfoMessage::parse(&data, 8).unwrap();
166 assert_eq!(msg.max_creation_order, Some(42));
167 assert_eq!(msg.fractal_heap_address, Some(0x1000));
168 assert_eq!(msg.btree_name_index_address, Some(0x2000));
169 assert_eq!(msg.btree_creation_order_address, Some(0x3000));
170 }
171
172 #[test]
173 fn no_creation_order_tracking() {
174 let mut data = Vec::new();
175 data.push(0); data.push(0x00); data.extend_from_slice(&0x500u64.to_le_bytes()); data.extend_from_slice(&0x600u64.to_le_bytes()); let msg = LinkInfoMessage::parse(&data, 8).unwrap();
181 assert_eq!(msg.max_creation_order, None);
182 assert_eq!(msg.fractal_heap_address, Some(0x500));
183 assert_eq!(msg.btree_name_index_address, Some(0x600));
184 assert_eq!(msg.btree_creation_order_address, None);
185 }
186
187 #[test]
188 fn invalid_version() {
189 let data = vec![1, 0, 0, 0];
190 let err = LinkInfoMessage::parse(&data, 8).unwrap_err();
191 assert_eq!(err, FormatError::InvalidLinkInfoVersion(1));
192 }
193
194 #[test]
195 fn four_byte_offsets() {
196 let mut data = Vec::new();
197 data.push(0); data.push(0x00); data.extend_from_slice(&0x100u32.to_le_bytes()); data.extend_from_slice(&0x200u32.to_le_bytes()); let msg = LinkInfoMessage::parse(&data, 4).unwrap();
203 assert_eq!(msg.fractal_heap_address, Some(0x100));
204 assert_eq!(msg.btree_name_index_address, Some(0x200));
205 }
206}