1use crate::error::FormatError;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct AttributeInfoMessage {
11 pub max_creation_index: Option<u16>,
13 pub fractal_heap_address: Option<u64>,
15 pub btree_name_index_address: Option<u64>,
17 pub btree_creation_order_address: Option<u64>,
19}
20
21fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
22 let s = size as usize;
23 if pos + s > data.len() {
24 return Err(FormatError::UnexpectedEof {
25 expected: pos + s,
26 available: data.len(),
27 });
28 }
29 Ok(match size {
30 2 => u16::from_le_bytes([data[pos], data[pos + 1]]) as u64,
31 4 => u32::from_le_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as u64,
32 8 => u64::from_le_bytes([
33 data[pos], data[pos + 1], data[pos + 2], data[pos + 3],
34 data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7],
35 ]),
36 _ => return Err(FormatError::InvalidOffsetSize(size)),
37 })
38}
39
40fn is_undefined(val: u64, offset_size: u8) -> bool {
41 match offset_size {
42 2 => val == 0xFFFF,
43 4 => val == 0xFFFF_FFFF,
44 8 => val == 0xFFFF_FFFF_FFFF_FFFF,
45 _ => false,
46 }
47}
48
49fn ensure_len(data: &[u8], pos: usize, needed: usize) -> Result<(), FormatError> {
50 match pos.checked_add(needed) {
51 Some(end) if end <= data.len() => Ok(()),
52 _ => Err(FormatError::UnexpectedEof {
53 expected: pos.saturating_add(needed),
54 available: data.len(),
55 }),
56 }
57}
58
59impl AttributeInfoMessage {
60 pub fn parse(data: &[u8], offset_size: u8) -> Result<AttributeInfoMessage, FormatError> {
66 ensure_len(data, 0, 2)?;
67
68 let version = data[0];
69 if version != 0 {
70 return Err(FormatError::InvalidAttributeInfoVersion(version));
71 }
72
73 let flags = data[1];
74 let has_max_creation_index = flags & 0x01 != 0;
75 let has_creation_order_index = flags & 0x02 != 0;
76
77 let mut pos = 2;
78
79 let max_creation_index = if has_max_creation_index {
80 ensure_len(data, pos, 2)?;
81 let v = u16::from_le_bytes([data[pos], data[pos + 1]]);
82 pos += 2;
83 Some(v)
84 } else {
85 None
86 };
87
88 let fh_addr = read_offset(data, pos, offset_size)?;
89 pos += offset_size as usize;
90 let fractal_heap_address = if is_undefined(fh_addr, offset_size) {
91 None
92 } else {
93 Some(fh_addr)
94 };
95
96 let btree_addr = read_offset(data, pos, offset_size)?;
97 pos += offset_size as usize;
98 let btree_name_index_address = if is_undefined(btree_addr, offset_size) {
99 None
100 } else {
101 Some(btree_addr)
102 };
103
104 let btree_creation_order_address = if has_creation_order_index {
105 let addr = read_offset(data, pos, offset_size)?;
106 if is_undefined(addr, offset_size) {
107 None
108 } else {
109 Some(addr)
110 }
111 } else {
112 None
113 };
114
115 Ok(AttributeInfoMessage {
116 max_creation_index,
117 fractal_heap_address,
118 btree_name_index_address,
119 btree_creation_order_address,
120 })
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn parse_compact_storage() {
130 let mut data = vec![0u8; 2 + 8 + 8];
132 data[0] = 0; data[1] = 0; data[2..10].copy_from_slice(&0xFFFF_FFFF_FFFF_FFFFu64.to_le_bytes());
135 data[10..18].copy_from_slice(&0xFFFF_FFFF_FFFF_FFFFu64.to_le_bytes());
136
137 let msg = AttributeInfoMessage::parse(&data, 8).unwrap();
138 assert_eq!(msg.fractal_heap_address, None);
139 assert_eq!(msg.btree_name_index_address, None);
140 assert_eq!(msg.max_creation_index, None);
141 assert_eq!(msg.btree_creation_order_address, None);
142 }
143
144 #[test]
145 fn parse_dense_storage() {
146 let mut data = Vec::new();
147 data.push(0); data.push(0x00); data.extend_from_slice(&0x1000u64.to_le_bytes()); data.extend_from_slice(&0x2000u64.to_le_bytes()); let msg = AttributeInfoMessage::parse(&data, 8).unwrap();
153 assert_eq!(msg.fractal_heap_address, Some(0x1000));
154 assert_eq!(msg.btree_name_index_address, Some(0x2000));
155 assert_eq!(msg.max_creation_index, None);
156 assert_eq!(msg.btree_creation_order_address, None);
157 }
158
159 #[test]
160 fn parse_dense_with_creation_order() {
161 let mut data = Vec::new();
162 data.push(0); data.push(0x03); data.extend_from_slice(&42u16.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 = AttributeInfoMessage::parse(&data, 8).unwrap();
170 assert_eq!(msg.max_creation_index, Some(42));
171 assert_eq!(msg.fractal_heap_address, Some(0x1000));
172 assert_eq!(msg.btree_name_index_address, Some(0x2000));
173 assert_eq!(msg.btree_creation_order_address, Some(0x3000));
174 }
175
176 #[test]
177 fn parse_four_byte_offsets() {
178 let mut data = Vec::new();
179 data.push(0); data.push(0x00); data.extend_from_slice(&0x100u32.to_le_bytes()); data.extend_from_slice(&0x200u32.to_le_bytes()); let msg = AttributeInfoMessage::parse(&data, 4).unwrap();
185 assert_eq!(msg.fractal_heap_address, Some(0x100));
186 assert_eq!(msg.btree_name_index_address, Some(0x200));
187 }
188
189 #[test]
190 fn invalid_version() {
191 let data = vec![1, 0, 0, 0];
192 let err = AttributeInfoMessage::parse(&data, 8).unwrap_err();
193 assert_eq!(err, FormatError::InvalidAttributeInfoVersion(1));
194 }
195
196 #[test]
197 fn truncated_data() {
198 let data = vec![0u8]; let err = AttributeInfoMessage::parse(&data, 8).unwrap_err();
200 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
201 }
202
203 #[test]
204 fn undefined_addresses_four_byte() {
205 let mut data = Vec::new();
206 data.push(0); data.push(0x00); data.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes()); data.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes()); let msg = AttributeInfoMessage::parse(&data, 4).unwrap();
212 assert_eq!(msg.fractal_heap_address, None);
213 assert_eq!(msg.btree_name_index_address, None);
214 }
215}