1#[cfg(not(feature = "std"))]
4use alloc::{string::String, vec::Vec};
5
6use crate::attribute_info::AttributeInfoMessage;
7use crate::btree_v2::{collect_btree_v2_records, BTreeV2Header};
8use crate::data_read;
9use crate::dataspace::Dataspace;
10use crate::datatype::Datatype;
11use crate::error::FormatError;
12use crate::fractal_heap::FractalHeapHeader;
13use crate::message_type::MessageType;
14use crate::object_header::ObjectHeader;
15use crate::shared_message;
16use crate::vl_data;
17
18#[derive(Debug, Clone)]
20pub struct AttributeMessage {
21 pub name: String,
23 pub datatype: Datatype,
25 pub dataspace: Dataspace,
27 pub raw_data: Vec<u8>,
29}
30
31fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
32 match offset.checked_add(needed) {
33 Some(end) if end <= data.len() => Ok(()),
34 _ => Err(FormatError::UnexpectedEof {
35 expected: offset.saturating_add(needed),
36 available: data.len(),
37 }),
38 }
39}
40
41fn pad8(x: usize) -> usize {
43 (x + 7) & !7
44}
45
46impl AttributeMessage {
47 pub fn parse(data: &[u8], length_size: u8) -> Result<AttributeMessage, FormatError> {
51 ensure_len(data, 0, 2)?;
52 let version = data[0];
53
54 match version {
55 1 => Self::parse_v1(data, length_size),
56 2 => Self::parse_v2(data, length_size),
57 3 => Self::parse_v3(data, length_size),
58 _ => Err(FormatError::InvalidAttributeVersion(version)),
59 }
60 }
61
62 fn parse_v1(data: &[u8], length_size: u8) -> Result<AttributeMessage, FormatError> {
63 ensure_len(data, 0, 8)?;
65 let name_size = u16::from_le_bytes([data[2], data[3]]) as usize;
66 let datatype_size = u16::from_le_bytes([data[4], data[5]]) as usize;
67 let dataspace_size = u16::from_le_bytes([data[6], data[7]]) as usize;
68
69 let mut pos = 8;
70
71 ensure_len(data, pos, name_size)?;
73 let name = extract_name(&data[pos..pos + name_size]);
74 pos += pad8(name_size);
75
76 ensure_len(data, pos, datatype_size)?;
78 let (datatype, _) = Datatype::parse(&data[pos..pos + datatype_size])?;
79 pos += pad8(datatype_size);
80
81 ensure_len(data, pos, dataspace_size)?;
83 let dataspace = Dataspace::parse(&data[pos..pos + dataspace_size], length_size)?;
84 pos += pad8(dataspace_size);
85
86 let raw_data = compute_raw_data(data, pos, &dataspace, &datatype);
88
89 Ok(AttributeMessage {
90 name,
91 datatype,
92 dataspace,
93 raw_data,
94 })
95 }
96
97 fn parse_v2(data: &[u8], length_size: u8) -> Result<AttributeMessage, FormatError> {
98 ensure_len(data, 0, 8)?;
100 let name_size = u16::from_le_bytes([data[2], data[3]]) as usize;
101 let datatype_size = u16::from_le_bytes([data[4], data[5]]) as usize;
102 let dataspace_size = u16::from_le_bytes([data[6], data[7]]) as usize;
103
104 let mut pos = 8;
105
106 ensure_len(data, pos, name_size)?;
108 let name = extract_name(&data[pos..pos + name_size]);
109 pos += name_size;
110
111 ensure_len(data, pos, datatype_size)?;
113 let (datatype, _) = Datatype::parse(&data[pos..pos + datatype_size])?;
114 pos += datatype_size;
115
116 ensure_len(data, pos, dataspace_size)?;
118 let dataspace = Dataspace::parse(&data[pos..pos + dataspace_size], length_size)?;
119 pos += dataspace_size;
120
121 let raw_data = compute_raw_data(data, pos, &dataspace, &datatype);
122
123 Ok(AttributeMessage {
124 name,
125 datatype,
126 dataspace,
127 raw_data,
128 })
129 }
130
131 fn parse_v3(data: &[u8], length_size: u8) -> Result<AttributeMessage, FormatError> {
132 ensure_len(data, 0, 9)?;
134 let name_size = u16::from_le_bytes([data[2], data[3]]) as usize;
135 let datatype_size = u16::from_le_bytes([data[4], data[5]]) as usize;
136 let dataspace_size = u16::from_le_bytes([data[6], data[7]]) as usize;
137 let _encoding = data[8]; let mut pos = 9;
140
141 ensure_len(data, pos, name_size)?;
143 let name = extract_name(&data[pos..pos + name_size]);
144 pos += name_size;
145
146 ensure_len(data, pos, datatype_size)?;
148 let (datatype, _) = Datatype::parse(&data[pos..pos + datatype_size])?;
149 pos += datatype_size;
150
151 ensure_len(data, pos, dataspace_size)?;
153 let dataspace = Dataspace::parse(&data[pos..pos + dataspace_size], length_size)?;
154 pos += dataspace_size;
155
156 let raw_data = compute_raw_data(data, pos, &dataspace, &datatype);
157
158 Ok(AttributeMessage {
159 name,
160 datatype,
161 dataspace,
162 raw_data,
163 })
164 }
165
166 pub fn serialize(&self, length_size: u8) -> Vec<u8> {
168 self.serialize_version(2, length_size)
169 }
170
171 pub fn serialize_v3(&self, length_size: u8) -> Vec<u8> {
173 self.serialize_version(3, length_size)
174 }
175
176 fn serialize_version(&self, version: u8, length_size: u8) -> Vec<u8> {
177 let name_bytes = {
178 let mut n = self.name.as_bytes().to_vec();
179 n.push(0); n
181 };
182 let dt_bytes = self.datatype.serialize();
183 let ds_bytes = self.dataspace.serialize(length_size);
184
185 let mut buf = Vec::new();
186 buf.push(version);
187 buf.push(0); buf.extend_from_slice(&(name_bytes.len() as u16).to_le_bytes());
189 buf.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
190 buf.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
191 if version >= 3 {
192 buf.push(0x00); }
194 buf.extend_from_slice(&name_bytes);
195 buf.extend_from_slice(&dt_bytes);
196 buf.extend_from_slice(&ds_bytes);
197 buf.extend_from_slice(&self.raw_data);
198 buf
199 }
200
201 pub fn read_as_f64(&self) -> Result<Vec<f64>, FormatError> {
203 data_read::read_as_f64(&self.raw_data, &self.datatype)
204 }
205
206 pub fn read_as_i64(&self) -> Result<Vec<i64>, FormatError> {
208 data_read::read_as_i64(&self.raw_data, &self.datatype)
209 }
210
211 pub fn read_as_u64(&self) -> Result<Vec<u64>, FormatError> {
213 data_read::read_as_u64(&self.raw_data, &self.datatype)
214 }
215
216 pub fn read_as_string(&self) -> Result<String, FormatError> {
218 let strings = data_read::read_as_strings(&self.raw_data, &self.datatype)?;
219 Ok(strings.into_iter().next().unwrap_or_default())
220 }
221
222 pub fn read_as_strings(&self) -> Result<Vec<String>, FormatError> {
224 data_read::read_as_strings(&self.raw_data, &self.datatype)
225 }
226
227 pub fn read_vl_strings(
232 &self,
233 file_data: &[u8],
234 offset_size: u8,
235 length_size: u8,
236 ) -> Result<Vec<String>, FormatError> {
237 let num_elements = self.dataspace.num_elements();
238 vl_data::read_vl_strings(
239 file_data,
240 &self.raw_data,
241 num_elements,
242 offset_size,
243 length_size,
244 )
245 }
246}
247
248fn compute_raw_data(data: &[u8], pos: usize, dataspace: &Dataspace, datatype: &Datatype) -> Vec<u8> {
250 let num_elements = dataspace.num_elements() as usize;
251 let elem_size = datatype.type_size() as usize;
252 let expected_size = num_elements * elem_size;
253 let available = data.len().saturating_sub(pos);
254 let take = expected_size.min(available);
255 if take > 0 {
256 data[pos..pos + take].to_vec()
257 } else if available > 0 {
258 data[pos..].to_vec()
260 } else {
261 Vec::new()
262 }
263}
264
265fn extract_name(bytes: &[u8]) -> String {
267 let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
268 String::from_utf8_lossy(&bytes[..end]).into_owned()
269}
270
271pub fn extract_attributes(
273 header: &ObjectHeader,
274 length_size: u8,
275) -> Result<Vec<AttributeMessage>, FormatError> {
276 let mut attrs = Vec::new();
277 for msg in &header.messages {
278 if msg.msg_type == MessageType::Attribute {
279 let attr = AttributeMessage::parse(&msg.data, length_size)?;
280 attrs.push(attr);
281 }
282 }
283 Ok(attrs)
284}
285
286pub fn find_attribute<'a>(
288 attrs: &'a [AttributeMessage],
289 name: &str,
290) -> Option<&'a AttributeMessage> {
291 attrs.iter().find(|a| a.name == name)
292}
293
294pub fn extract_attributes_full(
304 file_data: &[u8],
305 header: &ObjectHeader,
306 offset_size: u8,
307 length_size: u8,
308) -> Result<Vec<AttributeMessage>, FormatError> {
309 let mut attrs = Vec::new();
310
311 for msg in &header.messages {
313 if msg.msg_type == MessageType::Attribute {
314 if shared_message::is_shared(msg.flags) {
315 let shared_ref = shared_message::parse_shared_ref(&msg.data, offset_size)?;
317 let resolved_data = shared_message::resolve_shared_message(
318 file_data,
319 &shared_ref,
320 MessageType::Attribute,
321 offset_size,
322 length_size,
323 )?;
324 let attr = AttributeMessage::parse(&resolved_data, length_size)?;
325 attrs.push(attr);
326 } else {
327 let attr = AttributeMessage::parse(&msg.data, length_size)?;
328 attrs.push(attr);
329 }
330 }
331 }
332
333 let attr_info = find_attribute_info(header, offset_size)?;
335 if let Some(info) = attr_info {
336 if let Some(fh_addr) = info.fractal_heap_address {
337 let dense_attrs =
338 extract_dense_attributes(file_data, &info, fh_addr, offset_size, length_size)?;
339 attrs.extend(dense_attrs);
340 }
341 }
342
343 Ok(attrs)
344}
345
346fn find_attribute_info(
348 header: &ObjectHeader,
349 offset_size: u8,
350) -> Result<Option<AttributeInfoMessage>, FormatError> {
351 for msg in &header.messages {
352 if msg.msg_type == MessageType::AttributeInfo {
353 let info = AttributeInfoMessage::parse(&msg.data, offset_size)?;
354 return Ok(Some(info));
355 }
356 }
357 Ok(None)
358}
359
360fn extract_dense_attributes(
362 file_data: &[u8],
363 attr_info: &AttributeInfoMessage,
364 fh_addr: u64,
365 offset_size: u8,
366 length_size: u8,
367) -> Result<Vec<AttributeMessage>, FormatError> {
368 let fh = FractalHeapHeader::parse(file_data, fh_addr as usize, offset_size, length_size)?;
370
371 let btree_addr = attr_info.btree_name_index_address.ok_or(
373 FormatError::UnexpectedEof {
374 expected: 1,
375 available: 0,
376 }
377 )?;
378 let btree_hdr =
379 BTreeV2Header::parse(file_data, btree_addr as usize, offset_size, length_size)?;
380 let records = collect_btree_v2_records(file_data, &btree_hdr, offset_size, length_size)?;
381
382 let mut attrs = Vec::new();
383 for record in &records {
384 let id_offset = 0;
388
389 if record.data.len() < id_offset + fh.heap_id_length as usize {
390 continue;
391 }
392 let id_bytes = &record.data[id_offset..id_offset + fh.heap_id_length as usize];
393
394 let attr_data = fh.read_managed_object(file_data, id_bytes, offset_size)?;
396
397 let attr = AttributeMessage::parse(&attr_data, length_size)?;
399 attrs.push(attr);
400 }
401
402 Ok(attrs)
403}
404
405#[cfg(test)]
406mod tests {
407 use super::*;
408 use crate::datatype::{CharacterSet, DatatypeByteOrder, StringPadding};
409
410 fn build_dt_header(class: u8, version: u8, bf: [u8; 3], size: u32) -> Vec<u8> {
412 let mut buf = vec![0u8; 8];
413 buf[0] = (class & 0x0F) | ((version & 0x0F) << 4);
414 buf[1] = bf[0];
415 buf[2] = bf[1];
416 buf[3] = bf[2];
417 buf[4..8].copy_from_slice(&size.to_le_bytes());
418 buf
419 }
420
421 fn build_f64_dt() -> Vec<u8> {
423 let mut buf = build_dt_header(1, 1, [0x00, 0x00, 0x02], 8);
424 let mut props = [0u8; 12];
425 props[2..4].copy_from_slice(&64u16.to_le_bytes()); props[4] = 52; props[5] = 11; props[6] = 0; props[7] = 52; props[8..12].copy_from_slice(&1023u32.to_le_bytes()); buf.extend_from_slice(&props);
432 buf
433 }
434
435 fn build_scalar_ds() -> Vec<u8> {
437 vec![2, 0, 0, 0] }
439
440 fn build_simple_ds_v1(dim: u64) -> Vec<u8> {
442 let mut buf = vec![1u8, 1, 0, 0, 0, 0, 0, 0]; buf.extend_from_slice(&dim.to_le_bytes());
444 buf
445 }
446
447 fn build_string_dt(size: u32) -> Vec<u8> {
449 build_dt_header(3, 1, [0x01, 0, 0], size)
451 }
452
453 #[test]
454 fn parse_v1_attribute_f64_scalar() {
455 let name = b"temp\0";
456 let dt_bytes = build_f64_dt();
457 let ds_bytes = build_scalar_ds();
458
459 let name_size = name.len();
460 let dt_size = dt_bytes.len();
461 let ds_size = ds_bytes.len();
462
463 let mut data = Vec::new();
464 data.push(1); data.push(0); data.extend_from_slice(&(name_size as u16).to_le_bytes());
467 data.extend_from_slice(&(dt_size as u16).to_le_bytes());
468 data.extend_from_slice(&(ds_size as u16).to_le_bytes());
469
470 data.extend_from_slice(name);
472 while data.len() % 8 != 0 || data.len() == 8 {
473 let name_start = 8;
475 let name_padded = pad8(name_size);
476 while data.len() < name_start + name_padded {
477 data.push(0);
478 }
479 break;
480 }
481
482 let dt_start = data.len();
484 data.extend_from_slice(&dt_bytes);
485 let dt_padded = pad8(dt_size);
486 while data.len() < dt_start + dt_padded {
487 data.push(0);
488 }
489
490 let ds_start = data.len();
492 data.extend_from_slice(&ds_bytes);
493 let ds_padded = pad8(ds_size);
494 while data.len() < ds_start + ds_padded {
495 data.push(0);
496 }
497
498 data.extend_from_slice(&98.6f64.to_le_bytes());
500
501 let attr = AttributeMessage::parse(&data, 8).unwrap();
502 assert_eq!(attr.name, "temp");
503 assert_eq!(attr.dataspace.num_elements(), 1);
504 let vals = attr.read_as_f64().unwrap();
505 assert_eq!(vals.len(), 1);
506 assert!((vals[0] - 98.6).abs() < 1e-10);
507 }
508
509 #[test]
510 fn parse_v2_attribute_fixed_string() {
511 let name = b"label\0";
512 let dt_bytes = build_string_dt(5);
513 let ds_bytes = build_scalar_ds();
514
515 let mut data = Vec::new();
516 data.push(2); data.push(0); data.extend_from_slice(&(name.len() as u16).to_le_bytes());
519 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
520 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
521
522 data.extend_from_slice(name);
524 data.extend_from_slice(&dt_bytes);
525 data.extend_from_slice(&ds_bytes);
526
527 data.extend_from_slice(b"hello");
529
530 let attr = AttributeMessage::parse(&data, 8).unwrap();
531 assert_eq!(attr.name, "label");
532 let s = attr.read_as_string().unwrap();
533 assert_eq!(s, "hello");
534 }
535
536 #[test]
537 fn parse_v3_attribute_utf8() {
538 let name = b"note\0";
539 let dt_bytes = build_string_dt(3);
540 let ds_bytes = build_scalar_ds();
541
542 let mut data = Vec::new();
543 data.push(3); data.push(0); data.extend_from_slice(&(name.len() as u16).to_le_bytes());
546 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
547 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
548 data.push(1); data.extend_from_slice(name);
551 data.extend_from_slice(&dt_bytes);
552 data.extend_from_slice(&ds_bytes);
553 data.extend_from_slice(b"abc");
554
555 let attr = AttributeMessage::parse(&data, 8).unwrap();
556 assert_eq!(attr.name, "note");
557 let s = attr.read_as_string().unwrap();
558 assert_eq!(s, "abc");
559 }
560
561 #[test]
562 fn parse_v2_attribute_1d_array() {
563 let name = b"vals\0";
564 let dt_bytes = build_f64_dt();
565 let ds_bytes = build_simple_ds_v1(3);
566
567 let mut data = Vec::new();
568 data.push(2); data.push(0); data.extend_from_slice(&(name.len() as u16).to_le_bytes());
571 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
572 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
573
574 data.extend_from_slice(name);
575 data.extend_from_slice(&dt_bytes);
576 data.extend_from_slice(&ds_bytes);
577
578 data.extend_from_slice(&1.0f64.to_le_bytes());
580 data.extend_from_slice(&2.0f64.to_le_bytes());
581 data.extend_from_slice(&3.0f64.to_le_bytes());
582
583 let attr = AttributeMessage::parse(&data, 8).unwrap();
584 assert_eq!(attr.name, "vals");
585 let vals = attr.read_as_f64().unwrap();
586 assert_eq!(vals, vec![1.0, 2.0, 3.0]);
587 }
588
589 #[test]
590 fn parse_v1_padding_alignment() {
591 let name = b"x\0"; let dt_bytes = build_f64_dt(); let ds_bytes = build_scalar_ds(); let mut data = Vec::new();
597 data.push(1); data.push(0); data.extend_from_slice(&(name.len() as u16).to_le_bytes());
600 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
601 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
602
603 data.extend_from_slice(name);
605 data.resize(8 + pad8(name.len()), 0);
606
607 let dt_start = data.len();
609 data.extend_from_slice(&dt_bytes);
610 data.resize(dt_start + pad8(dt_bytes.len()), 0);
611
612 let ds_start = data.len();
614 data.extend_from_slice(&ds_bytes);
615 data.resize(ds_start + pad8(ds_bytes.len()), 0);
616
617 data.extend_from_slice(&42.0f64.to_le_bytes());
619
620 let attr = AttributeMessage::parse(&data, 8).unwrap();
621 assert_eq!(attr.name, "x");
622 let vals = attr.read_as_f64().unwrap();
623 assert_eq!(vals, vec![42.0]);
624 }
625
626 #[test]
627 fn parse_v2_no_padding() {
628 let name = b"ab\0"; let dt_bytes = build_string_dt(2); let ds_bytes = build_scalar_ds(); let mut data = Vec::new();
634 data.push(2);
635 data.push(0);
636 data.extend_from_slice(&(name.len() as u16).to_le_bytes());
637 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
638 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
639 data.extend_from_slice(name);
640 data.extend_from_slice(&dt_bytes);
641 data.extend_from_slice(&ds_bytes);
642 data.extend_from_slice(b"hi");
643
644 let attr = AttributeMessage::parse(&data, 8).unwrap();
645 assert_eq!(attr.name, "ab");
646 assert_eq!(attr.read_as_string().unwrap(), "hi");
647 }
648
649 #[test]
650 fn truncated_attribute_error() {
651 let data = [1u8]; let err = AttributeMessage::parse(&data, 8).unwrap_err();
653 assert!(matches!(err, FormatError::UnexpectedEof { .. }));
654 }
655
656 #[test]
657 fn invalid_version_error() {
658 let data = [5u8, 0, 0, 0, 0, 0, 0, 0, 0, 0];
659 let err = AttributeMessage::parse(&data, 8).unwrap_err();
660 assert_eq!(err, FormatError::InvalidAttributeVersion(5));
661 }
662
663 #[test]
664 fn extract_attributes_from_header() {
665 let mut msgs = Vec::new();
667 for i in 0..3 {
668 let name = format!("attr{}\0", i);
669 let dt_bytes = build_f64_dt();
670 let ds_bytes = build_scalar_ds();
671
672 let mut attr_data = Vec::new();
673 attr_data.push(2); attr_data.push(0);
675 attr_data.extend_from_slice(&(name.len() as u16).to_le_bytes());
676 attr_data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
677 attr_data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
678 attr_data.extend_from_slice(name.as_bytes());
679 attr_data.extend_from_slice(&dt_bytes);
680 attr_data.extend_from_slice(&ds_bytes);
681 attr_data.extend_from_slice(&((i as f64) * 1.0).to_le_bytes());
682
683 msgs.push(crate::object_header::HeaderMessage {
684 msg_type: MessageType::Attribute,
685 size: attr_data.len(),
686 flags: 0,
687 creation_order: None,
688 data: attr_data,
689 });
690 }
691
692 let header = ObjectHeader {
693 version: 2,
694 messages: msgs,
695 reference_count: None,
696 flags: 0,
697 access_time: None,
698 modification_time: None,
699 change_time: None,
700 birth_time: None,
701 };
702
703 let attrs = extract_attributes(&header, 8).unwrap();
704 assert_eq!(attrs.len(), 3);
705 assert_eq!(attrs[0].name, "attr0");
706 assert_eq!(attrs[1].name, "attr1");
707 assert_eq!(attrs[2].name, "attr2");
708 }
709
710 #[test]
711 fn find_attribute_by_name() {
712 let name = b"target\0";
713 let dt_bytes = build_f64_dt();
714 let ds_bytes = build_scalar_ds();
715
716 let mut attr_data = Vec::new();
717 attr_data.push(2);
718 attr_data.push(0);
719 attr_data.extend_from_slice(&(name.len() as u16).to_le_bytes());
720 attr_data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
721 attr_data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
722 attr_data.extend_from_slice(name);
723 attr_data.extend_from_slice(&dt_bytes);
724 attr_data.extend_from_slice(&ds_bytes);
725 attr_data.extend_from_slice(&99.0f64.to_le_bytes());
726
727 let attr = AttributeMessage::parse(&attr_data, 8).unwrap();
728 let attrs = vec![attr];
729
730 assert!(find_attribute(&attrs, "target").is_some());
731 assert!(find_attribute(&attrs, "missing").is_none());
732 }
733
734 #[test]
735 fn read_as_f64_scalar() {
736 let name = b"v\0";
737 let dt_bytes = build_f64_dt();
738 let ds_bytes = build_scalar_ds();
739
740 let mut data = Vec::new();
741 data.push(2);
742 data.push(0);
743 data.extend_from_slice(&(name.len() as u16).to_le_bytes());
744 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
745 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
746 data.extend_from_slice(name);
747 data.extend_from_slice(&dt_bytes);
748 data.extend_from_slice(&ds_bytes);
749 data.extend_from_slice(&3.14f64.to_le_bytes());
750
751 let attr = AttributeMessage::parse(&data, 8).unwrap();
752 let vals = attr.read_as_f64().unwrap();
753 assert_eq!(vals, vec![3.14]);
754 }
755
756 #[test]
757 fn read_as_string_fixed() {
758 let name = b"s\0";
759 let dt_bytes = build_string_dt(5);
760 let ds_bytes = build_scalar_ds();
761
762 let mut data = Vec::new();
763 data.push(2);
764 data.push(0);
765 data.extend_from_slice(&(name.len() as u16).to_le_bytes());
766 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
767 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
768 data.extend_from_slice(name);
769 data.extend_from_slice(&dt_bytes);
770 data.extend_from_slice(&ds_bytes);
771 data.extend_from_slice(b"world");
772
773 let attr = AttributeMessage::parse(&data, 8).unwrap();
774 assert_eq!(attr.read_as_string().unwrap(), "world");
775 }
776
777 #[test]
778 fn read_as_strings_array() {
779 let name = b"arr\0";
780 let dt_bytes = build_string_dt(4);
781 let ds_bytes = build_simple_ds_v1(2);
782
783 let mut data = Vec::new();
784 data.push(2);
785 data.push(0);
786 data.extend_from_slice(&(name.len() as u16).to_le_bytes());
787 data.extend_from_slice(&(dt_bytes.len() as u16).to_le_bytes());
788 data.extend_from_slice(&(ds_bytes.len() as u16).to_le_bytes());
789 data.extend_from_slice(name);
790 data.extend_from_slice(&dt_bytes);
791 data.extend_from_slice(&ds_bytes);
792 data.extend_from_slice(b"abcdEFGH");
793
794 let attr = AttributeMessage::parse(&data, 8).unwrap();
795 let strs = attr.read_as_strings().unwrap();
796 assert_eq!(strs, vec!["abcd", "EFGH"]);
797 }
798}