1#[cfg(not(feature = "std"))]
7use alloc::{boxed::Box, string::String, vec, vec::Vec};
8
9use byteorder::{ByteOrder, LittleEndian};
10
11use crate::error::FormatError;
12
13#[derive(Debug, Clone, PartialEq)]
15pub enum DatatypeByteOrder {
16 LittleEndian,
17 BigEndian,
18 Vax,
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub enum StringPadding {
24 NullTerminate,
25 NullPad,
26 SpacePad,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum CharacterSet {
32 Ascii,
33 Utf8,
34}
35
36#[derive(Debug, Clone, PartialEq)]
38pub enum ReferenceType {
39 Object,
40 DatasetRegion,
41}
42
43#[derive(Debug, Clone, PartialEq)]
45pub struct CompoundMember {
46 pub name: String,
48 pub byte_offset: u64,
50 pub datatype: Datatype,
52}
53
54#[derive(Debug, Clone, PartialEq)]
56pub struct EnumMember {
57 pub name: String,
59 pub value: Vec<u8>,
61}
62
63#[derive(Debug, Clone, PartialEq)]
65pub enum Datatype {
66 FixedPoint {
68 size: u32,
69 byte_order: DatatypeByteOrder,
70 signed: bool,
71 bit_offset: u16,
72 bit_precision: u16,
73 },
74 FloatingPoint {
76 size: u32,
77 byte_order: DatatypeByteOrder,
78 bit_offset: u16,
79 bit_precision: u16,
80 exponent_location: u8,
81 exponent_size: u8,
82 mantissa_location: u8,
83 mantissa_size: u8,
84 exponent_bias: u32,
85 },
86 Time {
88 size: u32,
89 bit_precision: u16,
90 },
91 String {
93 size: u32,
94 padding: StringPadding,
95 charset: CharacterSet,
96 },
97 BitField {
99 size: u32,
100 byte_order: DatatypeByteOrder,
101 bit_offset: u16,
102 bit_precision: u16,
103 },
104 Opaque {
106 size: u32,
107 tag: Vec<u8>,
108 },
109 Compound {
111 size: u32,
112 members: Vec<CompoundMember>,
113 },
114 Reference {
116 size: u32,
117 ref_type: ReferenceType,
118 },
119 Enumeration {
121 size: u32,
122 base_type: Box<Datatype>,
123 members: Vec<EnumMember>,
124 },
125 VariableLength {
127 is_string: bool,
128 padding: Option<StringPadding>,
129 charset: Option<CharacterSet>,
130 base_type: Box<Datatype>,
131 },
132 Array {
134 base_type: Box<Datatype>,
135 dimensions: Vec<u32>,
136 },
137}
138
139fn ensure_len(data: &[u8], offset: usize, needed: usize) -> Result<(), FormatError> {
140 match offset.checked_add(needed) {
141 Some(end) if end <= data.len() => Ok(()),
142 _ => Err(FormatError::UnexpectedEof {
143 expected: offset.saturating_add(needed),
144 available: data.len(),
145 }),
146 }
147}
148
149fn parse_string_padding(val: u8) -> Result<StringPadding, FormatError> {
150 match val {
151 0 => Ok(StringPadding::NullTerminate),
152 1 => Ok(StringPadding::NullPad),
153 2 => Ok(StringPadding::SpacePad),
154 _ => Err(FormatError::InvalidStringPadding(val)),
155 }
156}
157
158fn parse_charset(val: u8) -> Result<CharacterSet, FormatError> {
159 match val {
160 0 => Ok(CharacterSet::Ascii),
161 1 => Ok(CharacterSet::Utf8),
162 _ => Err(FormatError::InvalidCharacterSet(val)),
163 }
164}
165
166fn read_null_terminated_string(data: &[u8], offset: usize) -> Result<(String, usize), FormatError> {
169 if offset >= data.len() {
170 return Err(FormatError::UnexpectedEof {
171 expected: offset + 1,
172 available: data.len(),
173 });
174 }
175 let remaining = &data[offset..];
176 let null_pos = remaining
177 .iter()
178 .position(|&b| b == 0)
179 .ok_or(FormatError::UnexpectedEof {
180 expected: offset + 1,
181 available: data.len(),
182 })?;
183 let name = String::from_utf8_lossy(&remaining[..null_pos]).into_owned();
184 Ok((name, null_pos + 1))
185}
186
187fn offset_bytes_for_size(compound_size: u32) -> usize {
189 if compound_size <= 0xFF {
190 1
191 } else if compound_size <= 0xFFFF {
192 2
193 } else {
194 4
195 }
196}
197
198fn read_uint(data: &[u8], offset: usize, nbytes: usize) -> Result<u64, FormatError> {
200 ensure_len(data, offset, nbytes)?;
201 let slice = &data[offset..offset + nbytes];
202 Ok(match nbytes {
203 1 => slice[0] as u64,
204 2 => LittleEndian::read_u16(slice) as u64,
205 4 => LittleEndian::read_u32(slice) as u64,
206 8 => LittleEndian::read_u64(slice),
207 _ => {
208 return Err(FormatError::UnexpectedEof {
209 expected: offset + nbytes,
210 available: data.len(),
211 });
212 }
213 })
214}
215
216impl Datatype {
217 pub fn parse(data: &[u8]) -> Result<(Datatype, usize), FormatError> {
221 ensure_len(data, 0, 8)?;
223
224 let class_and_version = data[0];
225 let class_id = class_and_version & 0x0F;
226 let version = (class_and_version >> 4) & 0x0F;
227
228 let bf0 = data[1];
230 let bf1 = data[2];
231 let bf2 = data[3];
232 let _bit_field_24 = (bf0 as u32) | ((bf1 as u32) << 8) | ((bf2 as u32) << 16);
233
234 let size = LittleEndian::read_u32(&data[4..8]);
235 let mut pos = 8;
236
237 match class_id {
238 0 => {
239 ensure_len(data, pos, 4)?;
241 let byte_order = if bf0 & 0x01 == 0 {
242 DatatypeByteOrder::LittleEndian
243 } else {
244 DatatypeByteOrder::BigEndian
245 };
246 let signed = (bf0 >> 3) & 0x01 == 1;
247 let bit_offset = LittleEndian::read_u16(&data[pos..pos + 2]);
248 let bit_precision = LittleEndian::read_u16(&data[pos + 2..pos + 4]);
249 pos += 4;
250 Ok((
251 Datatype::FixedPoint {
252 size,
253 byte_order,
254 signed,
255 bit_offset,
256 bit_precision,
257 },
258 pos,
259 ))
260 }
261 1 => {
262 ensure_len(data, pos, 12)?;
264 let bo_low = bf0 & 0x01;
265 let bo_high = (bf0 >> 6) & 0x01;
266 let byte_order = match (bo_high, bo_low) {
267 (0, 0) => DatatypeByteOrder::LittleEndian,
268 (0, 1) => DatatypeByteOrder::BigEndian,
269 (1, 0) => DatatypeByteOrder::Vax,
270 (1, 1) => DatatypeByteOrder::Vax,
271 _ => unreachable!(),
272 };
273 let bit_offset = LittleEndian::read_u16(&data[pos..pos + 2]);
274 let bit_precision = LittleEndian::read_u16(&data[pos + 2..pos + 4]);
275 let exponent_location = data[pos + 4];
276 let exponent_size = data[pos + 5];
277 let mantissa_location = data[pos + 6];
278 let mantissa_size = data[pos + 7];
279 let exponent_bias = LittleEndian::read_u32(&data[pos + 8..pos + 12]);
280 pos += 12;
281 Ok((
282 Datatype::FloatingPoint {
283 size,
284 byte_order,
285 bit_offset,
286 bit_precision,
287 exponent_location,
288 exponent_size,
289 mantissa_location,
290 mantissa_size,
291 exponent_bias,
292 },
293 pos,
294 ))
295 }
296 2 => {
297 ensure_len(data, pos, 2)?;
299 let bit_precision = LittleEndian::read_u16(&data[pos..pos + 2]);
300 pos += 2;
301 Ok((Datatype::Time { size, bit_precision }, pos))
302 }
303 3 => {
304 let padding_val = bf0 & 0x0F;
306 let charset_val = (bf0 >> 4) & 0x0F;
307 let padding = parse_string_padding(padding_val)?;
308 let charset = parse_charset(charset_val)?;
309 Ok((Datatype::String { size, padding, charset }, pos))
310 }
311 4 => {
312 ensure_len(data, pos, 4)?;
314 let byte_order = if bf0 & 0x01 == 0 {
315 DatatypeByteOrder::LittleEndian
316 } else {
317 DatatypeByteOrder::BigEndian
318 };
319 let bit_offset = LittleEndian::read_u16(&data[pos..pos + 2]);
320 let bit_precision = LittleEndian::read_u16(&data[pos + 2..pos + 4]);
321 pos += 4;
322 Ok((
323 Datatype::BitField {
324 size,
325 byte_order,
326 bit_offset,
327 bit_precision,
328 },
329 pos,
330 ))
331 }
332 5 => {
333 let tag_len = bf0 as usize;
335 ensure_len(data, pos, tag_len)?;
336 let tag = data[pos..pos + tag_len].to_vec();
337 let padded = (tag_len + 7) & !7;
339 let pos = 8 + padded; Ok((Datatype::Opaque { size, tag }, pos))
341 }
342 6 => {
343 let num_members = (bf0 as u16) | ((bf1 as u16) << 8);
345 let mut members = Vec::with_capacity(num_members as usize);
346
347 if version == 3 || version == 4 {
348 let ob = offset_bytes_for_size(size);
349 for _ in 0..num_members {
350 let (name, name_len) = read_null_terminated_string(data, pos)?;
351 pos += name_len;
352 let byte_offset = read_uint(data, pos, ob)?;
353 pos += ob;
354 let (member_dt, consumed) = Datatype::parse(&data[pos..])?;
355 pos += consumed;
356 members.push(CompoundMember {
357 name,
358 byte_offset,
359 datatype: member_dt,
360 });
361 }
362 } else if version == 1 || version == 2 {
363 for _ in 0..num_members {
366 let (name, name_len) = read_null_terminated_string(data, pos)?;
367 pos += name_len;
368 if version == 1 {
370 let total_name_bytes = name_len;
371 let padded = (total_name_bytes + 7) & !7;
372 pos = pos - name_len + padded;
373 }
374 ensure_len(data, pos, 4)?;
375 let byte_offset = LittleEndian::read_u32(&data[pos..pos + 4]) as u64;
376 pos += 4;
377 ensure_len(data, pos, 24)?;
379 pos += 24;
380 let (member_dt, consumed) = Datatype::parse(&data[pos..])?;
381 pos += consumed;
382 members.push(CompoundMember {
383 name,
384 byte_offset,
385 datatype: member_dt,
386 });
387 }
388 } else {
389 return Err(FormatError::InvalidDatatypeVersion {
390 class: class_id,
391 version,
392 });
393 }
394
395 Ok((Datatype::Compound { size, members }, pos))
396 }
397 7 => {
398 let ref_type_val = bf0 & 0x0F;
400 let ref_type = match ref_type_val {
401 0 => ReferenceType::Object,
402 1 => ReferenceType::DatasetRegion,
403 _ => return Err(FormatError::InvalidReferenceType(ref_type_val)),
404 };
405 Ok((Datatype::Reference { size, ref_type }, pos))
406 }
407 8 => {
408 let num_members = (bf0 as u16) | ((bf1 as u16) << 8);
410 let (base_type, base_consumed) = Datatype::parse(&data[pos..])?;
412 pos += base_consumed;
413 let base_size = base_type.type_size();
414 let mut members = Vec::with_capacity(num_members as usize);
415 let mut member_names = Vec::with_capacity(num_members as usize);
419 for _ in 0..num_members {
420 let (name, name_len) = read_null_terminated_string(data, pos)?;
421 if version < 3 {
422 let padded = (name_len + 7) & !7;
423 pos += padded;
424 } else {
425 pos += name_len;
426 }
427 member_names.push(name);
428 }
429 for name in &member_names {
431 ensure_len(data, pos, base_size as usize)?;
432 let value = data[pos..pos + base_size as usize].to_vec();
433 pos += base_size as usize;
434 members.push(EnumMember {
435 name: name.clone(),
436 value,
437 });
438 }
439 Ok((
440 Datatype::Enumeration {
441 size,
442 base_type: Box::new(base_type),
443 members,
444 },
445 pos,
446 ))
447 }
448 9 => {
449 let vl_type = bf0 & 0x0F;
451 let is_string = vl_type == 1;
452 let padding = if is_string {
453 let pad_val = (bf0 >> 4) & 0x0F;
454 Some(parse_string_padding(pad_val)?)
455 } else {
456 None
457 };
458 let charset = if is_string {
459 let cs_val = bf1 & 0x0F;
460 Some(parse_charset(cs_val)?)
461 } else {
462 None
463 };
464 let (base_type, consumed) = Datatype::parse(&data[pos..])?;
465 pos += consumed;
466 Ok((
467 Datatype::VariableLength {
468 is_string,
469 padding,
470 charset,
471 base_type: Box::new(base_type),
472 },
473 pos,
474 ))
475 }
476 10 => {
477 if version == 2 {
479 ensure_len(data, pos, 4)?;
480 let ndims = data[pos] as usize;
481 pos += 4; ensure_len(data, pos, ndims * 4 + ndims * 4)?;
483 let mut dimensions = Vec::with_capacity(ndims);
484 for _ in 0..ndims {
485 dimensions.push(LittleEndian::read_u32(&data[pos..pos + 4]));
486 pos += 4;
487 }
488 pos += ndims * 4;
490 let (base_type, consumed) = Datatype::parse(&data[pos..])?;
491 pos += consumed;
492 Ok((
493 Datatype::Array {
494 base_type: Box::new(base_type),
495 dimensions,
496 },
497 pos,
498 ))
499 } else if version == 3 {
500 ensure_len(data, pos, 1)?;
501 let ndims = data[pos] as usize;
502 pos += 1;
503 ensure_len(data, pos, ndims * 4)?;
504 let mut dimensions = Vec::with_capacity(ndims);
505 for _ in 0..ndims {
506 dimensions.push(LittleEndian::read_u32(&data[pos..pos + 4]));
507 pos += 4;
508 }
509 let (base_type, consumed) = Datatype::parse(&data[pos..])?;
510 pos += consumed;
511 Ok((
512 Datatype::Array {
513 base_type: Box::new(base_type),
514 dimensions,
515 },
516 pos,
517 ))
518 } else {
519 Err(FormatError::InvalidDatatypeVersion {
520 class: class_id,
521 version,
522 })
523 }
524 }
525 11 => {
526 let num_members = (bf0 as u16) | ((bf1 as u16) << 8);
532 let mut members = Vec::with_capacity(num_members as usize);
533 let ob = offset_bytes_for_size(size);
534 for _ in 0..num_members {
535 let (name, name_len) = read_null_terminated_string(data, pos)?;
536 pos += name_len;
537 let byte_offset = read_uint(data, pos, ob)?;
538 pos += ob;
539 let (member_dt, consumed) = Datatype::parse(&data[pos..])?;
540 pos += consumed;
541 members.push(CompoundMember {
542 name,
543 byte_offset,
544 datatype: member_dt,
545 });
546 }
547 Ok((Datatype::Compound { size, members }, pos))
548 }
549 _ => Err(FormatError::InvalidDatatypeClass(class_id)),
550 }
551 }
552
553 pub fn serialize(&self) -> Vec<u8> {
555 match self {
556 Datatype::FixedPoint { size, byte_order, signed, bit_offset, bit_precision } => {
557 let mut bf0 = 0u8;
558 if matches!(byte_order, DatatypeByteOrder::BigEndian) { bf0 |= 0x01; }
559 if *signed { bf0 |= 0x08; }
560 let mut buf = Self::build_header(0, 1, [bf0, 0, 0], *size);
561 buf.extend_from_slice(&bit_offset.to_le_bytes());
562 buf.extend_from_slice(&bit_precision.to_le_bytes());
563 buf
564 }
565 Datatype::FloatingPoint { size, byte_order, bit_offset, bit_precision,
566 exponent_location, exponent_size, mantissa_location, mantissa_size, exponent_bias } => {
567 let mut bf0 = 0x20u8; match byte_order {
569 DatatypeByteOrder::BigEndian => { bf0 |= 0x01; }
570 DatatypeByteOrder::Vax => { bf0 |= 0x40; }
571 _ => {}
572 }
573 let bf1 = 0x3fu8; let mut buf = Self::build_header(1, 1, [bf0, bf1, 0], *size);
576 buf.extend_from_slice(&bit_offset.to_le_bytes());
577 buf.extend_from_slice(&bit_precision.to_le_bytes());
578 buf.push(*exponent_location);
579 buf.push(*exponent_size);
580 buf.push(*mantissa_location);
581 buf.push(*mantissa_size);
582 buf.extend_from_slice(&exponent_bias.to_le_bytes());
583 buf
584 }
585 Datatype::String { size, padding, charset } => {
586 let pad_val = match padding {
587 StringPadding::NullTerminate => 0,
588 StringPadding::NullPad => 1,
589 StringPadding::SpacePad => 2,
590 };
591 let cs_val = match charset {
592 CharacterSet::Ascii => 0,
593 CharacterSet::Utf8 => 1,
594 };
595 let bf0 = pad_val | (cs_val << 4);
596 Self::build_header(3, 1, [bf0, 0, 0], *size)
597 }
598 Datatype::VariableLength { is_string, padding, charset, base_type } => {
599 let mut bf0 = if *is_string { 0x01u8 } else { 0x00 };
600 if *is_string {
601 if let Some(p) = padding {
602 let pv = match p {
603 StringPadding::NullTerminate => 0,
604 StringPadding::NullPad => 1,
605 StringPadding::SpacePad => 2,
606 };
607 bf0 |= pv << 4;
608 }
609 }
610 let bf1 = if *is_string {
611 charset.as_ref().map_or(0, |c| match c {
612 CharacterSet::Ascii => 0,
613 CharacterSet::Utf8 => 1,
614 })
615 } else { 0 };
616 let mut buf = Self::build_header(9, 1, [bf0, bf1, 0], 16);
617 buf.extend_from_slice(&base_type.serialize());
618 buf
619 }
620 Datatype::Compound { size, members } => {
621 let num = members.len() as u16;
622 let bf0 = (num & 0xFF) as u8;
623 let bf1 = ((num >> 8) & 0xFF) as u8;
624 let mut buf = Self::build_header(6, 3, [bf0, bf1, 0], *size);
625 let ob = offset_bytes_for_size(*size);
626 for m in members {
627 buf.extend_from_slice(m.name.as_bytes());
629 buf.push(0);
630 match ob {
632 1 => buf.push(m.byte_offset as u8),
633 2 => buf.extend_from_slice(&(m.byte_offset as u16).to_le_bytes()),
634 _ => buf.extend_from_slice(&(m.byte_offset as u32).to_le_bytes()),
635 }
636 buf.extend_from_slice(&m.datatype.serialize());
638 }
639 buf
640 }
641 Datatype::Enumeration { size, base_type, members } => {
642 let num = members.len() as u16;
643 let bf0 = (num & 0xFF) as u8;
644 let bf1 = ((num >> 8) & 0xFF) as u8;
645 let mut buf = Self::build_header(8, 3, [bf0, bf1, 0], *size);
646 buf.extend_from_slice(&base_type.serialize());
648 for m in members {
650 buf.extend_from_slice(m.name.as_bytes());
651 buf.push(0);
652 }
653 for m in members {
655 buf.extend_from_slice(&m.value);
656 }
657 buf
658 }
659 Datatype::Array { base_type, dimensions } => {
660 let mut buf = Self::build_header(10, 3, [0, 0, 0], self.type_size());
661 buf.push(dimensions.len() as u8);
662 for &d in dimensions {
663 buf.extend_from_slice(&d.to_le_bytes());
664 }
665 buf.extend_from_slice(&base_type.serialize());
666 buf
667 }
668 _ => {
669 Vec::new()
670 }
671 }
672 }
673
674 fn build_header(class: u8, version: u8, bf: [u8; 3], size: u32) -> Vec<u8> {
675 let mut buf = vec![0u8; 8];
676 buf[0] = (class & 0x0F) | ((version & 0x0F) << 4);
677 buf[1] = bf[0];
678 buf[2] = bf[1];
679 buf[3] = bf[2];
680 buf[4..8].copy_from_slice(&size.to_le_bytes());
681 buf
682 }
683
684 pub fn type_size(&self) -> u32 {
686 match self {
687 Datatype::FixedPoint { size, .. } => *size,
688 Datatype::FloatingPoint { size, .. } => *size,
689 Datatype::Time { size, .. } => *size,
690 Datatype::String { size, .. } => *size,
691 Datatype::BitField { size, .. } => *size,
692 Datatype::Opaque { size, .. } => *size,
693 Datatype::Compound { size, .. } => *size,
694 Datatype::Reference { size, .. } => *size,
695 Datatype::Enumeration { size, .. } => *size,
696 Datatype::VariableLength { .. } => 16, Datatype::Array { base_type, dimensions } => {
698 let elem_count: u32 = dimensions.iter().copied()
699 .fold(1u32, |a, b| a.saturating_mul(b));
700 base_type.type_size().saturating_mul(elem_count)
701 }
702 }
703 }
704}
705
706#[cfg(test)]
708fn build_dt_header(class: u8, version: u8, bf: [u8; 3], size: u32) -> Vec<u8> {
709 let mut buf = vec![0u8; 8];
710 buf[0] = (class & 0x0F) | ((version & 0x0F) << 4);
711 buf[1] = bf[0];
712 buf[2] = bf[1];
713 buf[3] = bf[2];
714 LittleEndian::write_u32(&mut buf[4..8], size);
715 buf
716}
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 fn build_fixed_point(size: u32, be: bool, signed: bool, bit_offset: u16, bit_precision: u16) -> Vec<u8> {
724 let bf0 = if be { 0x01 } else { 0x00 } | if signed { 0x08 } else { 0x00 };
725 let mut buf = build_dt_header(0, 1, [bf0, 0, 0], size);
726 let mut props = [0u8; 4];
727 LittleEndian::write_u16(&mut props[0..2], bit_offset);
728 LittleEndian::write_u16(&mut props[2..4], bit_precision);
729 buf.extend_from_slice(&props);
730 buf
731 }
732
733 fn build_float(size: u32, exp_loc: u8, exp_size: u8, mant_loc: u8, mant_size: u8, exp_bias: u32) -> Vec<u8> {
735 let bf0 = 0x00u8;
737 let bf1 = 0x00u8;
738 let bf2 = 0x02u8; let mut buf = build_dt_header(1, 1, [bf0, bf1, bf2], size);
741 let mut props = [0u8; 12];
742 LittleEndian::write_u16(&mut props[0..2], 0); LittleEndian::write_u16(&mut props[2..4], (size * 8) as u16); props[4] = exp_loc;
745 props[5] = exp_size;
746 props[6] = mant_loc;
747 props[7] = mant_size;
748 LittleEndian::write_u32(&mut props[8..12], exp_bias);
749 buf.extend_from_slice(&props);
750 buf
751 }
752
753 #[test]
754 fn test_fixed_point_u8() {
755 let data = build_fixed_point(1, false, false, 0, 8);
756 let (dt, consumed) = Datatype::parse(&data).unwrap();
757 assert_eq!(consumed, 12);
758 assert_eq!(dt, Datatype::FixedPoint {
759 size: 1,
760 byte_order: DatatypeByteOrder::LittleEndian,
761 signed: false,
762 bit_offset: 0,
763 bit_precision: 8,
764 });
765 }
766
767 #[test]
768 fn test_fixed_point_i16_le() {
769 let data = build_fixed_point(2, false, true, 0, 16);
770 let (dt, _) = Datatype::parse(&data).unwrap();
771 assert_eq!(dt, Datatype::FixedPoint {
772 size: 2,
773 byte_order: DatatypeByteOrder::LittleEndian,
774 signed: true,
775 bit_offset: 0,
776 bit_precision: 16,
777 });
778 }
779
780 #[test]
781 fn test_fixed_point_u32_be() {
782 let data = build_fixed_point(4, true, false, 0, 32);
783 let (dt, _) = Datatype::parse(&data).unwrap();
784 match &dt {
785 Datatype::FixedPoint { byte_order, signed, size, .. } => {
786 assert_eq!(*byte_order, DatatypeByteOrder::BigEndian);
787 assert!(!signed);
788 assert_eq!(*size, 4);
789 }
790 _ => panic!("expected FixedPoint"),
791 }
792 }
793
794 #[test]
795 fn test_fixed_point_i64_le() {
796 let data = build_fixed_point(8, false, true, 0, 64);
797 let (dt, _) = Datatype::parse(&data).unwrap();
798 assert_eq!(dt, Datatype::FixedPoint {
799 size: 8,
800 byte_order: DatatypeByteOrder::LittleEndian,
801 signed: true,
802 bit_offset: 0,
803 bit_precision: 64,
804 });
805 }
806
807 #[test]
808 fn test_float_f32_le() {
809 let data = build_float(4, 23, 8, 0, 23, 127);
811 let (dt, consumed) = Datatype::parse(&data).unwrap();
812 assert_eq!(consumed, 20);
813 assert_eq!(dt, Datatype::FloatingPoint {
814 size: 4,
815 byte_order: DatatypeByteOrder::LittleEndian,
816 bit_offset: 0,
817 bit_precision: 32,
818 exponent_location: 23,
819 exponent_size: 8,
820 mantissa_location: 0,
821 mantissa_size: 23,
822 exponent_bias: 127,
823 });
824 }
825
826 #[test]
827 fn test_float_f64_le() {
828 let data = build_float(8, 52, 11, 0, 52, 1023);
829 let (dt, _) = Datatype::parse(&data).unwrap();
830 assert_eq!(dt, Datatype::FloatingPoint {
831 size: 8,
832 byte_order: DatatypeByteOrder::LittleEndian,
833 bit_offset: 0,
834 bit_precision: 64,
835 exponent_location: 52,
836 exponent_size: 11,
837 mantissa_location: 0,
838 mantissa_size: 52,
839 exponent_bias: 1023,
840 });
841 }
842
843 #[test]
844 fn test_string_null_terminated_ascii() {
845 let buf = build_dt_header(3, 1, [0x00, 0, 0], 10); let (dt, consumed) = Datatype::parse(&buf).unwrap();
847 assert_eq!(consumed, 8);
848 assert_eq!(dt, Datatype::String {
849 size: 10,
850 padding: StringPadding::NullTerminate,
851 charset: CharacterSet::Ascii,
852 });
853 }
854
855 #[test]
856 fn test_string_space_padded_utf8() {
857 let buf = build_dt_header(3, 1, [0x12, 0, 0], 32);
859 let (dt, _) = Datatype::parse(&buf).unwrap();
860 assert_eq!(dt, Datatype::String {
861 size: 32,
862 padding: StringPadding::SpacePad,
863 charset: CharacterSet::Utf8,
864 });
865 }
866
867 #[test]
868 fn test_opaque() {
869 let mut buf = build_dt_header(5, 1, [4, 0, 0], 64);
871 buf.extend_from_slice(b"BLOB");
872 buf.extend_from_slice(&[0, 0, 0, 0]);
874 let (dt, consumed) = Datatype::parse(&buf).unwrap();
875 assert_eq!(consumed, 16); assert_eq!(dt, Datatype::Opaque {
877 size: 64,
878 tag: b"BLOB".to_vec(),
879 });
880 }
881
882 #[test]
883 fn test_compound_v3_two_members() {
884 let mut buf = build_dt_header(6, 3, [2, 0, 0], 12); buf.extend_from_slice(b"x\0");
889 buf.push(0); buf.extend_from_slice(&build_fixed_point(4, false, false, 0, 32));
891 buf.extend_from_slice(b"y\0");
893 buf.push(4); buf.extend_from_slice(&build_float(8, 52, 11, 0, 52, 1023));
895
896 let (dt, _) = Datatype::parse(&buf).unwrap();
897 match dt {
898 Datatype::Compound { size, members } => {
899 assert_eq!(size, 12);
900 assert_eq!(members.len(), 2);
901 assert_eq!(members[0].name, "x");
902 assert_eq!(members[0].byte_offset, 0);
903 assert_eq!(members[1].name, "y");
904 assert_eq!(members[1].byte_offset, 4);
905 match &members[0].datatype {
906 Datatype::FixedPoint { size: 4, signed: false, .. } => {}
907 other => panic!("expected u32, got {other:?}"),
908 }
909 match &members[1].datatype {
910 Datatype::FloatingPoint { size: 8, .. } => {}
911 other => panic!("expected f64, got {other:?}"),
912 }
913 }
914 _ => panic!("expected Compound"),
915 }
916 }
917
918 #[test]
919 fn test_reference_object() {
920 let buf = build_dt_header(7, 1, [0, 0, 0], 8);
921 let (dt, _) = Datatype::parse(&buf).unwrap();
922 assert_eq!(dt, Datatype::Reference {
923 size: 8,
924 ref_type: ReferenceType::Object,
925 });
926 }
927
928 #[test]
929 fn test_reference_region() {
930 let buf = build_dt_header(7, 1, [1, 0, 0], 12);
931 let (dt, _) = Datatype::parse(&buf).unwrap();
932 assert_eq!(dt, Datatype::Reference {
933 size: 12,
934 ref_type: ReferenceType::DatasetRegion,
935 });
936 }
937
938 #[test]
939 fn test_enumeration() {
940 let mut buf = build_dt_header(8, 3, [3, 0, 0], 4); buf.extend_from_slice(&build_fixed_point(4, false, true, 0, 32));
944 buf.extend_from_slice(b"RED\0");
946 buf.extend_from_slice(b"GREEN\0");
947 buf.extend_from_slice(b"BLUE\0");
948 buf.extend_from_slice(&0i32.to_le_bytes());
950 buf.extend_from_slice(&1i32.to_le_bytes());
951 buf.extend_from_slice(&2i32.to_le_bytes());
952
953 let (dt, _) = Datatype::parse(&buf).unwrap();
954 match dt {
955 Datatype::Enumeration { size, base_type, members } => {
956 assert_eq!(size, 4);
957 assert_eq!(members.len(), 3);
958 assert_eq!(members[0].name, "RED");
959 assert_eq!(members[0].value, 0i32.to_le_bytes().to_vec());
960 assert_eq!(members[1].name, "GREEN");
961 assert_eq!(members[1].value, 1i32.to_le_bytes().to_vec());
962 assert_eq!(members[2].name, "BLUE");
963 assert_eq!(members[2].value, 2i32.to_le_bytes().to_vec());
964 match *base_type {
965 Datatype::FixedPoint { signed: true, size: 4, .. } => {}
966 other => panic!("expected i32, got {other:?}"),
967 }
968 }
969 _ => panic!("expected Enumeration"),
970 }
971 }
972
973 #[test]
974 fn test_variable_length_string_utf8() {
975 let mut buf = build_dt_header(9, 1, [0x01, 0x01, 0], 16);
979 buf.extend_from_slice(&build_fixed_point(1, false, false, 0, 8));
981
982 let (dt, _) = Datatype::parse(&buf).unwrap();
983 match dt {
984 Datatype::VariableLength { is_string, padding, charset, base_type } => {
985 assert!(is_string);
986 assert_eq!(padding, Some(StringPadding::NullTerminate));
987 assert_eq!(charset, Some(CharacterSet::Utf8));
988 assert_eq!(base_type.type_size(), 1);
989 }
990 _ => panic!("expected VariableLength"),
991 }
992 }
993
994 #[test]
995 fn test_variable_length_sequence_f32() {
996 let mut buf = build_dt_header(9, 1, [0x00, 0x00, 0], 16);
999 buf.extend_from_slice(&build_float(4, 23, 8, 0, 23, 127));
1001
1002 let (dt, _) = Datatype::parse(&buf).unwrap();
1003 match dt {
1004 Datatype::VariableLength { is_string, padding, charset, base_type } => {
1005 assert!(!is_string);
1006 assert_eq!(padding, None);
1007 assert_eq!(charset, None);
1008 assert_eq!(base_type.type_size(), 4);
1009 }
1010 _ => panic!("expected VariableLength"),
1011 }
1012 }
1013
1014 #[test]
1015 fn test_array_2d() {
1016 let mut buf = build_dt_header(10, 3, [0, 0, 0], 48); buf.push(2); buf.extend_from_slice(&3u32.to_le_bytes()); buf.extend_from_slice(&4u32.to_le_bytes()); buf.extend_from_slice(&build_fixed_point(4, false, true, 0, 32));
1023
1024 let (dt, _) = Datatype::parse(&buf).unwrap();
1025 match dt {
1026 Datatype::Array { base_type, dimensions } => {
1027 assert_eq!(dimensions, vec![3, 4]);
1028 match *base_type {
1029 Datatype::FixedPoint { size: 4, signed: true, .. } => {}
1030 other => panic!("expected i32, got {other:?}"),
1031 }
1032 }
1033 _ => panic!("expected Array"),
1034 }
1035 }
1036
1037 #[test]
1038 fn test_bitfield() {
1039 let mut buf = build_dt_header(4, 1, [0, 0, 0], 2); let mut props = [0u8; 4];
1041 LittleEndian::write_u16(&mut props[0..2], 0);
1042 LittleEndian::write_u16(&mut props[2..4], 16);
1043 buf.extend_from_slice(&props);
1044
1045 let (dt, _) = Datatype::parse(&buf).unwrap();
1046 assert_eq!(dt, Datatype::BitField {
1047 size: 2,
1048 byte_order: DatatypeByteOrder::LittleEndian,
1049 bit_offset: 0,
1050 bit_precision: 16,
1051 });
1052 }
1053
1054 #[test]
1055 fn test_time() {
1056 let mut buf = build_dt_header(2, 1, [0, 0, 0], 8);
1057 let mut props = [0u8; 2];
1058 LittleEndian::write_u16(&mut props[0..2], 64);
1059 buf.extend_from_slice(&props);
1060
1061 let (dt, consumed) = Datatype::parse(&buf).unwrap();
1062 assert_eq!(consumed, 10);
1063 assert_eq!(dt, Datatype::Time {
1064 size: 8,
1065 bit_precision: 64,
1066 });
1067 }
1068
1069 #[test]
1070 fn test_nested_compound_array_enum() {
1071 let mut enum_bytes = build_dt_header(8, 3, [2, 0, 0], 4); enum_bytes.extend_from_slice(&build_fixed_point(4, false, true, 0, 32)); enum_bytes.extend_from_slice(b"A\0");
1076 enum_bytes.extend_from_slice(b"B\0");
1077 enum_bytes.extend_from_slice(&0i32.to_le_bytes());
1078 enum_bytes.extend_from_slice(&1i32.to_le_bytes());
1079
1080 let mut array_bytes = build_dt_header(10, 3, [0, 0, 0], 8); array_bytes.push(1); array_bytes.extend_from_slice(&2u32.to_le_bytes()); array_bytes.extend_from_slice(&enum_bytes);
1085
1086 let mut buf = build_dt_header(6, 3, [1, 0, 0], 8); buf.extend_from_slice(b"data\0");
1089 buf.push(0); buf.extend_from_slice(&array_bytes);
1091
1092 let (dt, _) = Datatype::parse(&buf).unwrap();
1093 match dt {
1094 Datatype::Compound { members, .. } => {
1095 assert_eq!(members.len(), 1);
1096 assert_eq!(members[0].name, "data");
1097 match &members[0].datatype {
1098 Datatype::Array { dimensions, base_type } => {
1099 assert_eq!(dimensions, &[2]);
1100 match base_type.as_ref() {
1101 Datatype::Enumeration { members, .. } => {
1102 assert_eq!(members.len(), 2);
1103 assert_eq!(members[0].name, "A");
1104 assert_eq!(members[1].name, "B");
1105 }
1106 other => panic!("expected Enum, got {other:?}"),
1107 }
1108 }
1109 other => panic!("expected Array, got {other:?}"),
1110 }
1111 }
1112 _ => panic!("expected Compound"),
1113 }
1114 }
1115
1116 #[test]
1117 fn test_error_invalid_class() {
1118 let buf = build_dt_header(13, 1, [0, 0, 0], 4);
1119 let err = Datatype::parse(&buf).unwrap_err();
1120 assert_eq!(err, FormatError::InvalidDatatypeClass(13));
1121 }
1122
1123 #[test]
1124 fn test_error_truncated_data() {
1125 let buf = [0u8; 4]; let err = Datatype::parse(&buf).unwrap_err();
1127 match err {
1128 FormatError::UnexpectedEof { .. } => {}
1129 other => panic!("expected UnexpectedEof, got {other:?}"),
1130 }
1131 }
1132
1133 #[test]
1134 fn test_error_invalid_string_padding() {
1135 let buf = build_dt_header(3, 1, [0x03, 0, 0], 10); let err = Datatype::parse(&buf).unwrap_err();
1137 assert_eq!(err, FormatError::InvalidStringPadding(3));
1138 }
1139
1140 #[test]
1141 fn test_error_invalid_charset() {
1142 let buf = build_dt_header(3, 1, [0x20, 0, 0], 10); let err = Datatype::parse(&buf).unwrap_err();
1144 assert_eq!(err, FormatError::InvalidCharacterSet(2));
1145 }
1146
1147 #[test]
1148 fn test_error_invalid_reference_type() {
1149 let buf = build_dt_header(7, 1, [5, 0, 0], 8);
1150 let err = Datatype::parse(&buf).unwrap_err();
1151 assert_eq!(err, FormatError::InvalidReferenceType(5));
1152 }
1153
1154 #[test]
1155 fn serialize_parse_compound_roundtrip() {
1156 let dt = Datatype::Compound {
1157 size: 20,
1158 members: vec![
1159 CompoundMember {
1160 name: "x".to_string(),
1161 byte_offset: 0,
1162 datatype: Datatype::FloatingPoint {
1163 size: 8, byte_order: DatatypeByteOrder::LittleEndian,
1164 bit_offset: 0, bit_precision: 64,
1165 exponent_location: 52, exponent_size: 11,
1166 mantissa_location: 0, mantissa_size: 52, exponent_bias: 1023,
1167 },
1168 },
1169 CompoundMember {
1170 name: "y".to_string(),
1171 byte_offset: 8,
1172 datatype: Datatype::FloatingPoint {
1173 size: 8, byte_order: DatatypeByteOrder::LittleEndian,
1174 bit_offset: 0, bit_precision: 64,
1175 exponent_location: 52, exponent_size: 11,
1176 mantissa_location: 0, mantissa_size: 52, exponent_bias: 1023,
1177 },
1178 },
1179 CompoundMember {
1180 name: "id".to_string(),
1181 byte_offset: 16,
1182 datatype: Datatype::FixedPoint {
1183 size: 4, byte_order: DatatypeByteOrder::LittleEndian,
1184 signed: true, bit_offset: 0, bit_precision: 32,
1185 },
1186 },
1187 ],
1188 };
1189 let bytes = dt.serialize();
1190 let (parsed, _) = Datatype::parse(&bytes).unwrap();
1191 assert_eq!(parsed, dt);
1192 }
1193
1194 #[test]
1195 fn serialize_parse_enum_roundtrip() {
1196 let dt = Datatype::Enumeration {
1197 size: 4,
1198 base_type: Box::new(Datatype::FixedPoint {
1199 size: 4, byte_order: DatatypeByteOrder::LittleEndian,
1200 signed: true, bit_offset: 0, bit_precision: 32,
1201 }),
1202 members: vec![
1203 EnumMember { name: "RED".to_string(), value: 0i32.to_le_bytes().to_vec() },
1204 EnumMember { name: "GREEN".to_string(), value: 1i32.to_le_bytes().to_vec() },
1205 EnumMember { name: "BLUE".to_string(), value: 2i32.to_le_bytes().to_vec() },
1206 ],
1207 };
1208 let bytes = dt.serialize();
1209 let (parsed, _) = Datatype::parse(&bytes).unwrap();
1210 assert_eq!(parsed, dt);
1211 }
1212
1213 #[test]
1214 fn serialize_parse_array_roundtrip() {
1215 let dt = Datatype::Array {
1216 base_type: Box::new(Datatype::FloatingPoint {
1217 size: 8, byte_order: DatatypeByteOrder::LittleEndian,
1218 bit_offset: 0, bit_precision: 64,
1219 exponent_location: 52, exponent_size: 11,
1220 mantissa_location: 0, mantissa_size: 52, exponent_bias: 1023,
1221 }),
1222 dimensions: vec![3],
1223 };
1224 let bytes = dt.serialize();
1225 let (parsed, _) = Datatype::parse(&bytes).unwrap();
1226 assert_eq!(parsed, dt);
1227 }
1228
1229 #[test]
1230 fn test_type_size() {
1231 let dt = Datatype::FixedPoint {
1232 size: 4, byte_order: DatatypeByteOrder::LittleEndian,
1233 signed: true, bit_offset: 0, bit_precision: 32,
1234 };
1235 assert_eq!(dt.type_size(), 4);
1236
1237 let dt = Datatype::Array {
1238 base_type: Box::new(Datatype::FixedPoint {
1239 size: 4, byte_order: DatatypeByteOrder::LittleEndian,
1240 signed: true, bit_offset: 0, bit_precision: 32,
1241 }),
1242 dimensions: vec![3, 4],
1243 };
1244 assert_eq!(dt.type_size(), 48);
1245 }
1246}