1use crate::error::{Error, Result};
7use crate::io::Cursor;
8
9use super::dataspace::{self, DataspaceMessage};
10use super::datatype::{self, Datatype};
11
12#[derive(Debug, Clone)]
14pub struct AttributeMessage {
15 pub name: String,
17 pub datatype: Datatype,
19 pub dataspace: DataspaceMessage,
21 pub raw_data: Vec<u8>,
23}
24
25pub fn parse(
30 cursor: &mut Cursor<'_>,
31 offset_size: u8,
32 length_size: u8,
33 msg_size: usize,
34) -> Result<AttributeMessage> {
35 let start = cursor.position();
36 let version = cursor.read_u8()?;
37
38 let result = match version {
39 1 => parse_v1(cursor, offset_size, length_size),
40 2 => parse_v2(cursor, offset_size, length_size),
41 3 => parse_v3(cursor, offset_size, length_size),
42 v => Err(Error::UnsupportedAttributeVersion(v)),
43 };
44
45 result.and_then(|msg| {
46 let consumed = (cursor.position() - start) as usize;
47 if consumed < msg_size {
48 cursor.skip(msg_size - consumed)?;
49 }
50 Ok(msg)
51 })
52}
53
54fn parse_v1(cursor: &mut Cursor<'_>, offset_size: u8, length_size: u8) -> Result<AttributeMessage> {
55 let _reserved = cursor.read_u8()?;
56 let name_size = cursor.read_u16_le()? as usize;
57 let datatype_size = cursor.read_u16_le()? as usize;
58 let dataspace_size = cursor.read_u16_le()? as usize;
59
60 let name = cursor.read_fixed_string(name_size)?;
62 let name_padded = (name_size + 7) & !7;
63 if name_padded > name_size {
64 cursor.skip(name_padded - name_size)?;
65 }
66
67 let dt_msg = datatype::parse(cursor, datatype_size)?;
69 let dt_consumed = datatype_size; let dt_padded = (dt_consumed + 7) & !7;
71 if dt_padded > dt_consumed {
72 cursor.skip(dt_padded - dt_consumed)?;
73 }
74
75 let ds_msg = dataspace::parse(cursor, offset_size, length_size, dataspace_size)?;
77 let ds_consumed = dataspace_size;
78 let ds_padded = (ds_consumed + 7) & !7;
79 if ds_padded > ds_consumed {
80 cursor.skip(ds_padded - ds_consumed)?;
81 }
82
83 let data_size = attribute_raw_data_size(&ds_msg, dt_msg.size)?;
85 let raw_data = if data_size > 0 {
86 cursor.read_bytes(data_size)?.to_vec()
87 } else {
88 vec![]
89 };
90
91 Ok(AttributeMessage {
92 name,
93 datatype: dt_msg.datatype,
94 dataspace: ds_msg,
95 raw_data,
96 })
97}
98
99fn parse_v2(cursor: &mut Cursor<'_>, offset_size: u8, length_size: u8) -> Result<AttributeMessage> {
100 let _flags = cursor.read_u8()?;
101 let name_size = cursor.read_u16_le()? as usize;
102 let datatype_size = cursor.read_u16_le()? as usize;
103 let dataspace_size = cursor.read_u16_le()? as usize;
104
105 let name = cursor.read_fixed_string(name_size)?;
107
108 let dt_msg = datatype::parse(cursor, datatype_size)?;
110
111 let ds_msg = dataspace::parse(cursor, offset_size, length_size, dataspace_size)?;
113
114 let data_size = attribute_raw_data_size(&ds_msg, dt_msg.size)?;
116 let raw_data = if data_size > 0 {
117 cursor.read_bytes(data_size)?.to_vec()
118 } else {
119 vec![]
120 };
121
122 Ok(AttributeMessage {
123 name,
124 datatype: dt_msg.datatype,
125 dataspace: ds_msg,
126 raw_data,
127 })
128}
129
130fn parse_v3(cursor: &mut Cursor<'_>, offset_size: u8, length_size: u8) -> Result<AttributeMessage> {
131 let flags = cursor.read_u8()?;
132 let name_size = cursor.read_u16_le()? as usize;
133 let datatype_size = cursor.read_u16_le()? as usize;
134 let dataspace_size = cursor.read_u16_le()? as usize;
135 let _name_encoding = cursor.read_u8()?;
136
137 if (flags & 0x03) != 0 {
138 return Err(Error::InvalidData(
139 "shared datatype/dataspace in attribute v3 is not supported".to_string(),
140 ));
141 }
142
143 let name = cursor.read_fixed_string(name_size)?;
145
146 let dt_msg = datatype::parse(cursor, datatype_size)?;
148
149 let ds_msg = dataspace::parse(cursor, offset_size, length_size, dataspace_size)?;
151
152 let data_size = attribute_raw_data_size(&ds_msg, dt_msg.size)?;
154 let raw_data = if data_size > 0 {
155 cursor.read_bytes(data_size)?.to_vec()
156 } else {
157 vec![]
158 };
159
160 Ok(AttributeMessage {
161 name,
162 datatype: dt_msg.datatype,
163 dataspace: ds_msg,
164 raw_data,
165 })
166}
167
168fn attribute_raw_data_size(ds_msg: &dataspace::DataspaceMessage, dtype_size: u32) -> Result<usize> {
169 let elements = usize::try_from(ds_msg.num_elements()?).map_err(|_| {
170 Error::InvalidData("attribute element count exceeds platform usize capacity".to_string())
171 })?;
172 elements.checked_mul(dtype_size as usize).ok_or_else(|| {
173 Error::InvalidData("attribute raw data size exceeds platform usize capacity".to_string())
174 })
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use crate::error::ByteOrder;
181 use crate::messages::dataspace::DataspaceType;
182
183 fn u32_le_datatype() -> Vec<u8> {
185 let mut buf = Vec::new();
186 let class_word: u32 = 0x01 << 4;
188 buf.extend_from_slice(&class_word.to_le_bytes());
189 buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&0u16.to_le_bytes());
192 buf.extend_from_slice(&32u16.to_le_bytes());
193 buf
194 }
195
196 fn scalar_dataspace() -> Vec<u8> {
198 vec![0x02, 0x00, 0x00, 0x00]
199 }
200
201 #[test]
202 fn parse_v1_scalar_u32_attr() {
203 let dt = u32_le_datatype();
204 let ds = scalar_dataspace();
205
206 let mut data = vec![
207 0x01, 0x00, ];
210 data.extend_from_slice(&5u16.to_le_bytes());
212 data.extend_from_slice(&(dt.len() as u16).to_le_bytes());
214 data.extend_from_slice(&(ds.len() as u16).to_le_bytes());
216
217 data.extend_from_slice(b"temp\0\0\0\0");
219
220 data.extend_from_slice(&dt);
222 data.extend_from_slice(&[0u8; 4]); data.extend_from_slice(&ds);
226 data.extend_from_slice(&[0u8; 4]); data.extend_from_slice(&42u32.to_le_bytes());
230
231 let mut cursor = Cursor::new(&data);
232 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
233 assert_eq!(msg.name, "temp");
234 assert_eq!(msg.dataspace.dataspace_type, DataspaceType::Scalar);
235 assert_eq!(msg.raw_data, 42u32.to_le_bytes());
236 match &msg.datatype {
237 Datatype::FixedPoint {
238 size: 4,
239 signed: false,
240 byte_order: ByteOrder::LittleEndian,
241 } => {}
242 other => panic!("unexpected datatype: {:?}", other),
243 }
244 }
245
246 #[test]
247 fn parse_v3_scalar_attr() {
248 let dt = u32_le_datatype();
249 let ds = scalar_dataspace();
250
251 let mut data = vec![
252 0x03, 0x00, ];
255 data.extend_from_slice(&4u16.to_le_bytes());
257 data.extend_from_slice(&(dt.len() as u16).to_le_bytes());
258 data.extend_from_slice(&(ds.len() as u16).to_le_bytes());
259 data.push(0x00); data.extend_from_slice(b"abc\0");
263
264 data.extend_from_slice(&dt);
266
267 data.extend_from_slice(&ds);
269
270 data.extend_from_slice(&99u32.to_le_bytes());
272
273 let mut cursor = Cursor::new(&data);
274 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
275 assert_eq!(msg.name, "abc");
276 assert_eq!(msg.dataspace.dataspace_type, DataspaceType::Scalar);
277 assert_eq!(msg.raw_data, 99u32.to_le_bytes());
278 }
279
280 #[test]
281 fn parse_v3_utf8_name_attr() {
282 let dt = u32_le_datatype();
283 let ds = scalar_dataspace();
284
285 let mut data = vec![
286 0x03, 0x00, ];
289 data.extend_from_slice(&2u16.to_le_bytes()); data.extend_from_slice(&(dt.len() as u16).to_le_bytes());
291 data.extend_from_slice(&(ds.len() as u16).to_le_bytes());
292 data.push(0x01); data.extend_from_slice(b"x\0");
294 data.extend_from_slice(&dt);
295 data.extend_from_slice(&ds);
296 data.extend_from_slice(&7u32.to_le_bytes());
297
298 let mut cursor = Cursor::new(&data);
299 let msg = parse(&mut cursor, 8, 8, data.len()).unwrap();
300 assert_eq!(msg.name, "x");
301 assert_eq!(msg.dataspace.dataspace_type, DataspaceType::Scalar);
302 assert_eq!(msg.raw_data, 7u32.to_le_bytes());
303 }
304}