1use std::fmt;
7use tracing::debug;
8
9use crate::error::{DecodeError, DecodeResult};
10
11pub use crate::epics_decode::decode_string;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct DecodeLimits {
22 pub max_scalar_array: usize,
23 pub max_string_array: usize,
24 pub max_struct_array: usize,
25 pub max_union_array: usize,
26 pub max_variant_array: usize,
27}
28
29impl Default for DecodeLimits {
30 fn default() -> Self {
31 Self {
32 max_scalar_array: 4_000_000,
33 max_string_array: 65_536,
34 max_struct_array: 65_536,
35 max_union_array: 65_536,
36 max_variant_array: 65_536,
37 }
38 }
39}
40
41#[repr(u8)]
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum TypeCode {
45 Null = 0xFF,
46 Boolean = 0x00,
47 Int8 = 0x20,
48 Int16 = 0x21,
49 Int32 = 0x22,
50 Int64 = 0x23,
51 UInt8 = 0x24,
52 UInt16 = 0x25,
53 UInt32 = 0x26,
54 UInt64 = 0x27,
55 Float32 = 0x42,
56 Float64 = 0x43,
57 String = 0x60,
58 Variant = 0xFE, }
61
62impl TypeCode {
63 pub fn from_byte(b: u8) -> Option<Self> {
64 let base = b & 0xE7;
66 match base {
67 0x00 => Some(TypeCode::Boolean),
68 0x20 => Some(TypeCode::Int8),
69 0x21 => Some(TypeCode::Int16),
70 0x22 => Some(TypeCode::Int32),
71 0x23 => Some(TypeCode::Int64),
72 0x24 => Some(TypeCode::UInt8),
73 0x25 => Some(TypeCode::UInt16),
74 0x26 => Some(TypeCode::UInt32),
75 0x27 => Some(TypeCode::UInt64),
76 0x42 => Some(TypeCode::Float32),
77 0x43 => Some(TypeCode::Float64),
78 0x60 => Some(TypeCode::String),
79 _ => None,
80 }
81 }
82
83 pub fn size(&self) -> Option<usize> {
84 match self {
85 TypeCode::Boolean | TypeCode::Int8 | TypeCode::UInt8 => Some(1),
86 TypeCode::Int16 | TypeCode::UInt16 => Some(2),
87 TypeCode::Int32 | TypeCode::UInt32 | TypeCode::Float32 => Some(4),
88 TypeCode::Int64 | TypeCode::UInt64 | TypeCode::Float64 => Some(8),
89 TypeCode::String | TypeCode::Null | TypeCode::Variant => None,
90 }
91 }
92}
93
94#[derive(Debug, Clone, PartialEq)]
96pub enum FieldType {
97 Scalar(TypeCode),
98 ScalarArray(TypeCode),
99 String,
100 StringArray,
101 Structure(StructureDesc),
102 StructureArray(StructureDesc),
103 Union(Vec<FieldDesc>),
104 UnionArray(Vec<FieldDesc>),
105 Variant,
106 VariantArray,
107 BoundedString(u32),
108}
109
110impl FieldType {
111 pub fn type_name(&self) -> &'static str {
112 match self {
113 FieldType::Scalar(tc) => match tc {
114 TypeCode::Boolean => "boolean",
115 TypeCode::Int8 => "byte",
116 TypeCode::Int16 => "short",
117 TypeCode::Int32 => "int",
118 TypeCode::Int64 => "long",
119 TypeCode::UInt8 => "ubyte",
120 TypeCode::UInt16 => "ushort",
121 TypeCode::UInt32 => "uint",
122 TypeCode::UInt64 => "ulong",
123 TypeCode::Float32 => "float",
124 TypeCode::Float64 => "double",
125 TypeCode::String => "string",
126 _ => "unknown",
127 },
128 FieldType::ScalarArray(tc) => match tc {
129 TypeCode::Float64 => "double[]",
130 TypeCode::Float32 => "float[]",
131 TypeCode::Int64 => "long[]",
132 TypeCode::Int32 => "int[]",
133 _ => "array",
134 },
135 FieldType::String => "string",
136 FieldType::StringArray => "string[]",
137 FieldType::Structure(_) => "structure",
138 FieldType::StructureArray(_) => "structure[]",
139 FieldType::Union(_) => "union",
140 FieldType::UnionArray(_) => "union[]",
141 FieldType::Variant => "any",
142 FieldType::VariantArray => "any[]",
143 FieldType::BoundedString(_) => "string",
144 }
145 }
146
147 pub fn heap_size(&self) -> usize {
151 match self {
152 FieldType::Structure(s) | FieldType::StructureArray(s) => s.heap_size(),
153 FieldType::Union(f) | FieldType::UnionArray(f) => {
154 f.capacity() * std::mem::size_of::<FieldDesc>()
155 + f.iter().map(FieldDesc::heap_size).sum::<usize>()
156 }
157 _ => 0,
158 }
159 }
160}
161
162#[derive(Debug, Clone, PartialEq)]
164pub struct FieldDesc {
165 pub name: String,
166 pub field_type: FieldType,
167}
168
169impl FieldDesc {
170 pub fn heap_size(&self) -> usize {
172 self.name.capacity() + self.field_type.heap_size()
173 }
174}
175
176#[derive(Debug, Clone, PartialEq)]
178pub struct StructureDesc {
179 pub struct_id: Option<String>,
180 pub fields: Vec<FieldDesc>,
181}
182
183impl StructureDesc {
184 pub fn new() -> Self {
185 Self {
186 struct_id: None,
187 fields: Vec::new(),
188 }
189 }
190
191 pub fn field(&self, name: &str) -> Option<&FieldDesc> {
193 self.fields.iter().find(|f| f.name == name)
194 }
195
196 pub fn heap_size(&self) -> usize {
204 let id = self.struct_id.as_ref().map_or(0, |s| s.capacity());
205 let fields = self.fields.capacity() * std::mem::size_of::<FieldDesc>()
206 + self.fields.iter().map(FieldDesc::heap_size).sum::<usize>();
207 id + fields
208 }
209}
210
211impl Default for StructureDesc {
212 fn default() -> Self {
213 Self::new()
214 }
215}
216
217impl fmt::Display for StructureDesc {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 fn write_indent(f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
220 for _ in 0..depth {
221 write!(f, " ")?;
222 }
223 Ok(())
224 }
225
226 fn write_field_type(
227 f: &mut fmt::Formatter<'_>,
228 ft: &FieldType,
229 depth: usize,
230 ) -> fmt::Result {
231 match ft {
232 FieldType::Structure(desc) => write_structure(f, desc, depth),
233 FieldType::StructureArray(desc) => {
234 write_structure(f, desc, depth)?;
235 write!(f, "[]")
236 }
237 FieldType::Union(fields) => {
238 writeln!(f, "union")?;
239 for field in fields {
240 write_indent(f, depth + 1)?;
241 write!(f, "{} ", field.name)?;
242 write_field_type(f, &field.field_type, depth + 1)?;
243 writeln!(f)?;
244 }
245 Ok(())
246 }
247 FieldType::UnionArray(fields) => {
248 writeln!(f, "union[]")?;
249 for field in fields {
250 write_indent(f, depth + 1)?;
251 write!(f, "{} ", field.name)?;
252 write_field_type(f, &field.field_type, depth + 1)?;
253 writeln!(f)?;
254 }
255 Ok(())
256 }
257 other => write!(f, "{}", other.type_name()),
258 }
259 }
260
261 fn write_structure(
262 f: &mut fmt::Formatter<'_>,
263 desc: &StructureDesc,
264 depth: usize,
265 ) -> fmt::Result {
266 if let Some(id) = &desc.struct_id {
267 write!(f, "structure «{}»", id)?;
268 } else {
269 write!(f, "structure")?;
270 }
271 if desc.fields.is_empty() {
272 return Ok(());
273 }
274 writeln!(f)?;
275 for field in &desc.fields {
276 write_indent(f, depth + 1)?;
277 write!(f, "{} ", field.name)?;
278 write_field_type(f, &field.field_type, depth + 1)?;
279 writeln!(f)?;
280 }
281 Ok(())
282 }
283
284 write_structure(f, self, 0)
285 }
286}
287
288#[derive(Debug, Clone)]
290pub enum DecodedValue {
291 Null,
292 Boolean(bool),
293 Int8(i8),
294 Int16(i16),
295 Int32(i32),
296 Int64(i64),
297 UInt8(u8),
298 UInt16(u16),
299 UInt32(u32),
300 UInt64(u64),
301 Float32(f32),
302 Float64(f64),
303 String(String),
304 Array(Vec<DecodedValue>),
305 Structure(Vec<(String, DecodedValue)>),
306 Raw(Vec<u8>),
307}
308
309impl fmt::Display for DecodedValue {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 match self {
312 DecodedValue::Null => write!(f, "null"),
313 DecodedValue::Boolean(v) => write!(f, "{}", v),
314 DecodedValue::Int8(v) => write!(f, "{}", v),
315 DecodedValue::Int16(v) => write!(f, "{}", v),
316 DecodedValue::Int32(v) => write!(f, "{}", v),
317 DecodedValue::Int64(v) => write!(f, "{}", v),
318 DecodedValue::UInt8(v) => write!(f, "{}", v),
319 DecodedValue::UInt16(v) => write!(f, "{}", v),
320 DecodedValue::UInt32(v) => write!(f, "{}", v),
321 DecodedValue::UInt64(v) => write!(f, "{}", v),
322 DecodedValue::Float32(v) => write!(f, "{:.6}", v),
323 DecodedValue::Float64(v) => write!(f, "{:.6}", v),
324 DecodedValue::String(v) => write!(f, "\"{}\"", v),
325 DecodedValue::Array(arr) => {
326 write!(f, "[")?;
327 for (i, v) in arr.iter().enumerate() {
328 if i > 0 {
329 write!(f, ", ")?;
330 }
331 write!(f, "{}", v)?;
332 }
333 write!(f, "]")
334 }
335 DecodedValue::Structure(fields) => {
336 write!(f, "{{")?;
337 for (i, (name, val)) in fields.iter().enumerate() {
338 if i > 0 {
339 write!(f, ", ")?;
340 }
341 write!(f, "{}={}", name, val)?;
342 }
343 write!(f, "}}")
344 }
345 DecodedValue::Raw(data) => {
346 if data.len() <= 8 {
347 write!(f, "<{} bytes: {}>", data.len(), hex::encode(data))
348 } else {
349 write!(f, "<{} bytes>", data.len())
350 }
351 }
352 }
353 }
354}
355
356pub struct PvdDecoder {
358 is_be: bool,
359 limits: DecodeLimits,
360 registry: std::cell::RefCell<std::collections::HashMap<u16, FieldType>>,
363}
364
365impl PvdDecoder {
366 pub fn new(is_be: bool) -> Self {
367 Self::with_limits(is_be, DecodeLimits::default())
368 }
369
370 pub fn with_limits(is_be: bool, limits: DecodeLimits) -> Self {
372 Self {
373 is_be,
374 limits,
375 registry: std::cell::RefCell::new(std::collections::HashMap::new()),
376 }
377 }
378
379 pub fn limits(&self) -> &DecodeLimits {
381 &self.limits
382 }
383
384 pub fn decode_size(&self, data: &[u8]) -> DecodeResult<(usize, usize)> {
386 if data.is_empty() {
387 return Err(DecodeError::Truncated {
388 needed: 1,
389 available: 0,
390 });
391 }
392 let first = data[0];
393 if first == 0xFF {
394 return Ok((0, 1)); }
397 if first < 254 {
398 return Ok((first as usize, 1));
399 }
400 if first == 254 {
401 if data.len() < 5 {
403 return Err(DecodeError::Truncated {
404 needed: 5,
405 available: data.len(),
406 });
407 }
408 let size = if self.is_be {
409 u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize
410 } else {
411 u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize
412 };
413 return Ok((size, 5));
414 }
415 Err(DecodeError::Malformed("invalid size prefix 255"))
417 }
418
419 pub fn decode_string(&self, data: &[u8]) -> DecodeResult<(String, usize)> {
421 let (size, size_bytes) = self.decode_size(data)?;
422 if size == 0 {
423 return Ok((String::new(), size_bytes));
424 }
425 if data.len() < size_bytes + size {
426 return Err(DecodeError::Truncated {
427 needed: size_bytes + size,
428 available: data.len(),
429 });
430 }
431 let s = std::str::from_utf8(&data[size_bytes..size_bytes + size])
432 .map_err(|_| DecodeError::Malformed("string is not valid UTF-8"))?;
433 Ok((s.to_string(), size_bytes + size))
434 }
435
436 pub fn parse_field_desc(&self, data: &[u8]) -> DecodeResult<(FieldDesc, usize)> {
438 if data.is_empty() {
439 return Err(DecodeError::Truncated {
440 needed: 1,
441 available: 0,
442 });
443 }
444
445 let mut offset = 0;
446
447 let (name, consumed) = self.decode_string(&data[offset..])?;
449 offset += consumed;
450
451 if offset >= data.len() {
452 return Err(DecodeError::Truncated {
453 needed: offset + 1,
454 available: data.len(),
455 });
456 }
457
458 let (field_type, type_consumed) = self.parse_type_desc(&data[offset..])?;
460 offset += type_consumed;
461
462 Ok((FieldDesc { name, field_type }, offset))
463 }
464
465 fn parse_type_desc(&self, data: &[u8]) -> DecodeResult<(FieldType, usize)> {
467 if data.is_empty() {
468 return Err(DecodeError::Truncated {
469 needed: 1,
470 available: 0,
471 });
472 }
473
474 let type_byte = data[0];
475 let mut offset = 1;
476
477 if type_byte == 0xFF {
479 return Ok((FieldType::Variant, 1));
480 }
481
482 if type_byte == 0xFD {
485 if data.len() < 3 {
486 return Err(DecodeError::Truncated {
487 needed: 3,
488 available: data.len(),
489 });
490 }
491 let key = if self.is_be {
492 u16::from_be_bytes([data[1], data[2]])
493 } else {
494 u16::from_le_bytes([data[1], data[2]])
495 };
496 let (field_type, consumed) = self.parse_type_desc(&data[3..])?;
497 self.registry.borrow_mut().insert(key, field_type.clone());
498 return Ok((field_type, 3 + consumed));
499 }
500
501 if type_byte == 0xFE {
504 if data.len() < 3 {
505 return Err(DecodeError::Truncated {
506 needed: 3,
507 available: data.len(),
508 });
509 }
510 let key = if self.is_be {
511 u16::from_be_bytes([data[1], data[2]])
512 } else {
513 u16::from_le_bytes([data[1], data[2]])
514 };
515 if let Some(ft) = self.registry.borrow().get(&key) {
516 return Ok((ft.clone(), 3));
517 }
518 debug!(
519 "Type descriptor ONLY_ID (0xFE) key={} not found in registry",
520 key
521 );
522 return Err(DecodeError::UnresolvedTypeId(key));
523 }
524
525 if type_byte == 0x80 || type_byte == 0x88 {
527 let is_array = (type_byte & 0x08) != 0;
528 if is_array {
529 if offset >= data.len() {
531 return Err(DecodeError::Truncated {
532 needed: offset + 1,
533 available: data.len(),
534 });
535 }
536 if data[offset] != 0x80 {
537 return Err(DecodeError::UnknownTypeTag(data[offset]));
538 }
539 offset += 1;
540 }
541 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
542 offset += consumed;
543 if is_array {
544 return Ok((FieldType::StructureArray(struct_desc), offset));
545 } else {
546 return Ok((FieldType::Structure(struct_desc), offset));
547 }
548 }
549
550 if type_byte == 0x81 || type_byte == 0x89 {
552 let is_array = (type_byte & 0x08) != 0;
553 if is_array {
554 if offset >= data.len() {
556 return Err(DecodeError::Truncated {
557 needed: offset + 1,
558 available: data.len(),
559 });
560 }
561 if data[offset] != 0x81 {
562 return Err(DecodeError::UnknownTypeTag(data[offset]));
563 }
564 offset += 1;
565 }
566 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
568 offset += consumed;
569 if is_array {
570 return Ok((FieldType::UnionArray(struct_desc.fields), offset));
571 } else {
572 return Ok((FieldType::Union(struct_desc.fields), offset));
573 }
574 }
575
576 if type_byte == 0x82 {
578 return Ok((FieldType::Variant, 1));
579 }
580 if type_byte == 0x8A {
581 return Ok((FieldType::VariantArray, 1));
582 }
583
584 if type_byte == 0x83 || type_byte == 0x86 {
586 let (bound, consumed) = self.decode_size(&data[offset..])?;
587 offset += consumed;
588 return Ok((FieldType::BoundedString(bound as u32), offset));
589 }
590
591 let scalar_or_array = type_byte & 0x18;
594 let is_array = scalar_or_array != 0;
595 if is_array && scalar_or_array != 0x08 {
596 let (_bound, consumed) = self.decode_size(&data[offset..])?;
598 offset += consumed;
599 }
600 let base_type = type_byte & 0xE7;
601
602 if base_type == 0x60 {
604 if is_array {
605 return Ok((FieldType::StringArray, offset));
606 } else {
607 return Ok((FieldType::String, offset));
608 }
609 }
610
611 if let Some(tc) = TypeCode::from_byte(base_type) {
613 if is_array {
614 return Ok((FieldType::ScalarArray(tc), offset));
615 } else {
616 return Ok((FieldType::Scalar(tc), offset));
617 }
618 }
619
620 debug!("Unknown type byte: 0x{:02x}", type_byte);
621 Err(DecodeError::UnknownTypeTag(type_byte))
622 }
623
624 fn parse_structure_desc(&self, data: &[u8]) -> DecodeResult<(StructureDesc, usize)> {
626 let mut offset = 0;
627
628 let (struct_id, consumed) = self.decode_string(&data[offset..])?;
630 offset += consumed;
631
632 let struct_id = if struct_id.is_empty() {
633 None
634 } else {
635 Some(struct_id)
636 };
637
638 let (field_count, consumed) = self.decode_size(&data[offset..])?;
640 offset += consumed;
641
642 let mut fields = Vec::with_capacity(field_count);
643
644 for _ in 0..field_count {
645 if offset >= data.len() {
646 break;
647 }
648 if let Ok((field, consumed)) = self.parse_field_desc(&data[offset..]) {
649 offset += consumed;
650 fields.push(field);
651 } else {
652 break;
653 }
654 }
655
656 Ok((StructureDesc { struct_id, fields }, offset))
657 }
658
659 pub fn parse_introspection(&self, data: &[u8]) -> DecodeResult<StructureDesc> {
661 self.parse_introspection_with_len(data)
662 .map(|(desc, _)| desc)
663 }
664
665 pub fn parse_introspection_with_len(&self, data: &[u8]) -> DecodeResult<(StructureDesc, usize)> {
667 if data.is_empty() {
668 return Err(DecodeError::Truncated {
669 needed: 1,
670 available: 0,
671 });
672 }
673
674 let type_byte = data[0];
676
677 if type_byte == 0x80 {
679 let (desc, consumed) = self.parse_structure_desc(&data[1..])?;
680 return Ok((desc, 1 + consumed));
681 }
682
683 if type_byte == 0xFD {
686 if data.len() < 3 {
687 return Err(DecodeError::Truncated {
688 needed: 3,
689 available: data.len(),
690 });
691 }
692 let key = if self.is_be {
693 u16::from_be_bytes([data[1], data[2]])
694 } else {
695 u16::from_le_bytes([data[1], data[2]])
696 };
697 let (desc, consumed) = self.parse_introspection_with_len(&data[3..])?;
698 self.registry
700 .borrow_mut()
701 .insert(key, FieldType::Structure(desc.clone()));
702 return Ok((desc, 3 + consumed));
703 }
704
705 if type_byte == 0xFE {
708 if data.len() < 3 {
709 return Err(DecodeError::Truncated {
710 needed: 3,
711 available: data.len(),
712 });
713 }
714 let key = if self.is_be {
715 u16::from_be_bytes([data[1], data[2]])
716 } else {
717 u16::from_le_bytes([data[1], data[2]])
718 };
719 if let Some(ft) = self.registry.borrow().get(&key) {
720 if let FieldType::Structure(desc) = ft {
721 return Ok((desc.clone(), 3));
722 }
723 }
724 debug!(
725 "Introspection ONLY_ID (0xFE) key={} not found in registry",
726 key
727 );
728 return Err(DecodeError::UnresolvedTypeId(key));
729 }
730
731 debug!("Unexpected introspection type byte: 0x{:02x}", type_byte);
732 Err(DecodeError::UnknownTypeTag(type_byte))
733 }
734
735 fn decode_scalar(&self, data: &[u8], tc: TypeCode) -> DecodeResult<(DecodedValue, usize)> {
737 let size = tc
738 .size()
739 .ok_or(DecodeError::Malformed("type code has no fixed scalar size"))?;
740 if data.len() < size {
741 return Err(DecodeError::Truncated {
742 needed: size,
743 available: data.len(),
744 });
745 }
746
747 let value = match tc {
748 TypeCode::Boolean => DecodedValue::Boolean(data[0] != 0),
749 TypeCode::Int8 => DecodedValue::Int8(data[0] as i8),
750 TypeCode::UInt8 => DecodedValue::UInt8(data[0]),
751 TypeCode::Int16 => {
752 let v = if self.is_be {
753 i16::from_be_bytes([data[0], data[1]])
754 } else {
755 i16::from_le_bytes([data[0], data[1]])
756 };
757 DecodedValue::Int16(v)
758 }
759 TypeCode::UInt16 => {
760 let v = if self.is_be {
761 u16::from_be_bytes([data[0], data[1]])
762 } else {
763 u16::from_le_bytes([data[0], data[1]])
764 };
765 DecodedValue::UInt16(v)
766 }
767 TypeCode::Int32 => {
768 let v = if self.is_be {
769 i32::from_be_bytes(data[0..4].try_into().unwrap())
770 } else {
771 i32::from_le_bytes(data[0..4].try_into().unwrap())
772 };
773 DecodedValue::Int32(v)
774 }
775 TypeCode::UInt32 => {
776 let v = if self.is_be {
777 u32::from_be_bytes(data[0..4].try_into().unwrap())
778 } else {
779 u32::from_le_bytes(data[0..4].try_into().unwrap())
780 };
781 DecodedValue::UInt32(v)
782 }
783 TypeCode::Int64 => {
784 let v = if self.is_be {
785 i64::from_be_bytes(data[0..8].try_into().unwrap())
786 } else {
787 i64::from_le_bytes(data[0..8].try_into().unwrap())
788 };
789 DecodedValue::Int64(v)
790 }
791 TypeCode::UInt64 => {
792 let v = if self.is_be {
793 u64::from_be_bytes(data[0..8].try_into().unwrap())
794 } else {
795 u64::from_le_bytes(data[0..8].try_into().unwrap())
796 };
797 DecodedValue::UInt64(v)
798 }
799 TypeCode::Float32 => {
800 let v = if self.is_be {
801 f32::from_be_bytes(data[0..4].try_into().unwrap())
802 } else {
803 f32::from_le_bytes(data[0..4].try_into().unwrap())
804 };
805 DecodedValue::Float32(v)
806 }
807 TypeCode::Float64 => {
808 let v = if self.is_be {
809 f64::from_be_bytes(data[0..8].try_into().unwrap())
810 } else {
811 f64::from_le_bytes(data[0..8].try_into().unwrap())
812 };
813 DecodedValue::Float64(v)
814 }
815 _ => {
816 return Err(DecodeError::Malformed(
817 "type code is not a decodable scalar",
818 ))
819 }
820 };
821
822 Ok((value, size))
823 }
824
825 fn check_array_count(
833 &self,
834 count: usize,
835 min_elem: usize,
836 available: usize,
837 kind: &'static str,
838 limit: usize,
839 ) -> DecodeResult<()> {
840 if count > limit {
841 return Err(DecodeError::ArrayTooLarge { kind, count, limit });
842 }
843 let min_bytes = count.saturating_mul(min_elem);
844 if min_bytes > available {
845 return Err(DecodeError::CountExceedsBuffer {
846 count,
847 min_bytes,
848 available,
849 });
850 }
851 Ok(())
852 }
853
854 pub fn decode_value(
856 &self,
857 data: &[u8],
858 field_type: &FieldType,
859 ) -> DecodeResult<(DecodedValue, usize)> {
860 match field_type {
861 FieldType::Scalar(tc) => self.decode_scalar(data, *tc),
862 FieldType::String | FieldType::BoundedString(_) => {
863 let (s, consumed) = self.decode_string(data)?;
864 Ok((DecodedValue::String(s), consumed))
865 }
866 FieldType::ScalarArray(tc) => {
867 let (count, size_consumed) = self.decode_size(data)?;
868 let mut offset = size_consumed;
869 let elem_size = tc.size().unwrap_or(1);
870 self.check_array_count(
871 count,
872 elem_size,
873 data.len() - offset,
874 "scalar array",
875 self.limits.max_scalar_array,
876 )?;
877 let mut values = Vec::with_capacity(count);
878 for _ in 0..count {
879 let (val, consumed) = self.decode_scalar(&data[offset..], *tc)?;
880 values.push(val);
881 offset += consumed;
882 }
883 Ok((DecodedValue::Array(values), offset))
884 }
885 FieldType::StringArray => {
886 let (count, size_consumed) = self.decode_size(data)?;
887 let mut offset = size_consumed;
888 self.check_array_count(
889 count,
890 1,
891 data.len() - offset,
892 "string array",
893 self.limits.max_string_array,
894 )?;
895 let mut values = Vec::with_capacity(count);
896 for _ in 0..count {
897 let (s, consumed) = self.decode_string(&data[offset..])?;
898 values.push(DecodedValue::String(s));
899 offset += consumed;
900 }
901 Ok((DecodedValue::Array(values), offset))
902 }
903 FieldType::Structure(desc) => self.decode_structure(data, desc),
904 FieldType::StructureArray(desc) => {
905 let (count, size_consumed) = self.decode_size(data)?;
906 let mut offset = size_consumed;
907 self.check_array_count(
908 count,
909 1,
910 data.len() - offset,
911 "structure array",
912 self.limits.max_struct_array,
913 )?;
914 let mut values = Vec::with_capacity(count);
915 for _ in 0..count {
916 if offset >= data.len() {
918 return Err(DecodeError::Truncated {
919 needed: offset + 1,
920 available: data.len(),
921 });
922 }
923 let null_indicator = data[offset];
924 offset += 1;
925 if null_indicator == 0 {
926 values.push(DecodedValue::Structure(Vec::new()));
928 continue;
929 }
930 let (item, consumed) = self.decode_structure(&data[offset..], desc)?;
931 values.push(item);
932 offset += consumed;
933 }
934 Ok((DecodedValue::Array(values), offset))
935 }
936 FieldType::Union(fields) => {
937 let (selector, consumed) = self.decode_size(data)?;
938 let field = fields.get(selector).ok_or(DecodeError::UnknownUnionSelector {
939 selector,
940 len: fields.len(),
941 })?;
942 let (value, val_consumed) =
943 self.decode_value(&data[consumed..], &field.field_type)?;
944 Ok((
945 DecodedValue::Structure(vec![(field.name.clone(), value)]),
946 consumed + val_consumed,
947 ))
948 }
949 FieldType::UnionArray(fields) => {
950 let (count, size_consumed) = self.decode_size(data)?;
951 let mut offset = size_consumed;
952 self.check_array_count(
953 count,
954 1,
955 data.len() - offset,
956 "union array",
957 self.limits.max_union_array,
958 )?;
959 let mut values = Vec::with_capacity(count);
960 for _ in 0..count {
961 let (selector, consumed) = self.decode_size(&data[offset..])?;
962 offset += consumed;
963 let field =
964 fields
965 .get(selector)
966 .ok_or(DecodeError::UnknownUnionSelector {
967 selector,
968 len: fields.len(),
969 })?;
970 let (value, val_consumed) =
971 self.decode_value(&data[offset..], &field.field_type)?;
972 offset += val_consumed;
973 values.push(DecodedValue::Structure(vec![(field.name.clone(), value)]));
974 }
975 Ok((DecodedValue::Array(values), offset))
976 }
977 FieldType::Variant => {
978 if data.is_empty() {
979 return Err(DecodeError::Truncated {
980 needed: 1,
981 available: 0,
982 });
983 }
984 if data[0] == 0xFF {
985 return Ok((DecodedValue::Null, 1));
986 }
987 let (variant_type, type_consumed) = self.parse_type_desc(data)?;
988 let (variant_value, value_consumed) =
989 self.decode_value(&data[type_consumed..], &variant_type)?;
990 Ok((variant_value, type_consumed + value_consumed))
991 }
992 FieldType::VariantArray => {
993 let (count, size_consumed) = self.decode_size(data)?;
994 let mut offset = size_consumed;
995 self.check_array_count(
996 count,
997 1,
998 data.len() - offset,
999 "variant array",
1000 self.limits.max_variant_array,
1001 )?;
1002 let mut values = Vec::with_capacity(count);
1003 for _ in 0..count {
1004 let (v, consumed) = self.decode_value(&data[offset..], &FieldType::Variant)?;
1005 values.push(v);
1006 offset += consumed;
1007 }
1008 Ok((DecodedValue::Array(values), offset))
1009 }
1010 }
1011 }
1012
1013 pub fn decode_structure(
1015 &self,
1016 data: &[u8],
1017 desc: &StructureDesc,
1018 ) -> DecodeResult<(DecodedValue, usize)> {
1019 let mut offset = 0;
1020 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
1021
1022 for field in &desc.fields {
1023 if offset >= data.len() {
1024 break;
1025 }
1026 if let Ok((value, consumed)) = self.decode_value(&data[offset..], &field.field_type) {
1027 fields.push((field.name.clone(), value));
1028 offset += consumed;
1029 } else {
1030 break;
1032 }
1033 }
1034
1035 Ok((DecodedValue::Structure(fields), offset))
1036 }
1037
1038 pub fn decode_structure_with_bitset(
1041 &self,
1042 data: &[u8],
1043 desc: &StructureDesc,
1044 ) -> DecodeResult<(DecodedValue, usize)> {
1045 if data.is_empty() {
1046 return Err(DecodeError::Truncated {
1047 needed: 1,
1048 available: 0,
1049 });
1050 }
1051
1052 let mut offset = 0;
1053
1054 let (bitset_size, size_consumed) = self.decode_size(data)?;
1056 offset += size_consumed;
1057
1058 if bitset_size == 0 || offset + bitset_size > data.len() {
1059 return Ok((DecodedValue::Structure(vec![]), offset));
1060 }
1061
1062 let bitset = &data[offset..offset + bitset_size];
1063 offset += bitset_size;
1064
1065 let (value, consumed) =
1066 self.decode_structure_with_bitset_body(&data[offset..], desc, bitset)?;
1067 Ok((value, offset + consumed))
1068 }
1069
1070 pub(crate) fn decode_structure_with_bitset_body(
1071 &self,
1072 data: &[u8],
1073 desc: &StructureDesc,
1074 bitset: &[u8],
1075 ) -> DecodeResult<(DecodedValue, usize)> {
1076 debug!(
1078 "Bitset: {:02x?} (size={}), total_fields={}",
1079 bitset,
1080 bitset.len(),
1081 count_structure_fields(desc)
1082 );
1083 debug!(
1084 "Structure fields: {:?}",
1085 desc.fields.iter().map(|f| &f.name).collect::<Vec<_>>()
1086 );
1087
1088 let mut has_field_bits = false;
1090 if !bitset.is_empty() {
1091 for (i, b) in bitset.iter().enumerate() {
1092 let mask = if i == 0 { *b & !0x01 } else { *b };
1093 if mask != 0 {
1094 has_field_bits = true;
1095 break;
1096 }
1097 }
1098 }
1099 if !has_field_bits && !bitset.is_empty() && (bitset[0] & 0x01) != 0 {
1100 if let Ok((value, consumed)) = self.decode_structure(data, desc) {
1101 return Ok((value, consumed));
1102 }
1103 }
1104
1105 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
1106 let mut offset = 0usize;
1107
1108 fn decode_with_bitset_recursive(
1109 decoder: &PvdDecoder,
1110 data: &[u8],
1111 offset: &mut usize,
1112 desc: &StructureDesc,
1113 bitset: &[u8],
1114 bit_offset: &mut usize,
1115 fields: &mut Vec<(String, DecodedValue)>,
1116 ) -> bool {
1117 for field in &desc.fields {
1118 let byte_idx = *bit_offset / 8;
1119 let bit_idx = *bit_offset % 8;
1120 let current_bit = *bit_offset;
1121 *bit_offset += 1;
1122
1123 let field_present = if byte_idx < bitset.len() {
1124 (bitset[byte_idx] & (1 << bit_idx)) != 0
1125 } else {
1126 false
1127 };
1128
1129 debug!(
1130 "Field '{}' at bit {}: present={}",
1131 field.name, current_bit, field_present
1132 );
1133
1134 if let FieldType::Structure(nested_desc) = &field.field_type {
1135 let child_start_bit = *bit_offset;
1136 let child_field_count = count_structure_fields(nested_desc);
1137
1138 let mut any_child_bits_set = false;
1139 for i in 0..child_field_count {
1140 let check_byte = (child_start_bit + i) / 8;
1141 let check_bit = (child_start_bit + i) % 8;
1142 if check_byte < bitset.len() && (bitset[check_byte] & (1 << check_bit)) != 0
1143 {
1144 any_child_bits_set = true;
1145 break;
1146 }
1147 }
1148
1149 debug!(
1150 "Nested structure '{}': parent_present={}, child_start_bit={}, child_count={}, any_child_bits_set={}",
1151 field.name,
1152 field_present,
1153 child_start_bit,
1154 child_field_count,
1155 any_child_bits_set
1156 );
1157
1158 if field_present && !any_child_bits_set {
1159 *bit_offset += child_field_count;
1160 if *offset < data.len() {
1161 if let Ok((value, consumed)) =
1162 decoder.decode_structure(&data[*offset..], nested_desc)
1163 {
1164 debug!(
1165 "Decoded full nested structure '{}', consumed {} bytes",
1166 field.name, consumed
1167 );
1168 fields.push((field.name.clone(), value));
1169 *offset += consumed;
1170 } else {
1171 debug!("Failed to decode full nested structure '{}'", field.name);
1172 return false;
1173 }
1174 }
1175 } else if any_child_bits_set {
1176 let mut nested_fields: Vec<(String, DecodedValue)> = Vec::new();
1177 if !decode_with_bitset_recursive(
1178 decoder,
1179 data,
1180 offset,
1181 nested_desc,
1182 bitset,
1183 bit_offset,
1184 &mut nested_fields,
1185 ) {
1186 return false;
1187 }
1188 debug!(
1189 "Nested structure '{}' decoded {} fields",
1190 field.name,
1191 nested_fields.len()
1192 );
1193 if !nested_fields.is_empty() {
1194 fields
1195 .push((field.name.clone(), DecodedValue::Structure(nested_fields)));
1196 }
1197 } else {
1198 *bit_offset += child_field_count;
1199 }
1200 } else if field_present {
1201 if *offset >= data.len() {
1202 debug!(
1203 "Data exhausted at offset {} for field '{}'",
1204 *offset, field.name
1205 );
1206 return false;
1207 }
1208 if let Ok((value, consumed)) =
1209 decoder.decode_value(&data[*offset..], &field.field_type)
1210 {
1211 fields.push((field.name.clone(), value));
1212 *offset += consumed;
1213 } else {
1214 return false;
1215 }
1216 }
1217 }
1218 true
1219 }
1220
1221 let mut bit_offset = 1;
1222 decode_with_bitset_recursive(
1223 self,
1224 data,
1225 &mut offset,
1226 desc,
1227 bitset,
1228 &mut bit_offset,
1229 &mut fields,
1230 );
1231 Ok((DecodedValue::Structure(fields), offset))
1232 }
1233}
1234
1235pub(crate) fn count_structure_fields(desc: &StructureDesc) -> usize {
1240 let mut count = 0;
1241 for field in &desc.fields {
1242 count += 1;
1243 if let FieldType::Structure(nested) = &field.field_type {
1244 count += count_structure_fields(nested);
1245 }
1246 }
1247 count
1248}
1249
1250pub fn extract_subfield_desc(desc: &StructureDesc, path: &str) -> Option<StructureDesc> {
1255 if path.is_empty() {
1256 return Some(desc.clone());
1257 }
1258 let mut parts = path.splitn(2, '.');
1259 let head = parts.next()?;
1260 let tail = parts.next().unwrap_or("");
1261 for field in &desc.fields {
1262 if field.name == head {
1263 match &field.field_type {
1264 FieldType::Structure(nested) | FieldType::StructureArray(nested) => {
1265 return extract_subfield_desc(nested, tail);
1266 }
1267 _ => {
1268 if tail.is_empty() {
1269 return Some(StructureDesc {
1270 struct_id: None,
1271 fields: vec![field.clone()],
1272 });
1273 }
1274 return None;
1275 }
1276 }
1277 }
1278 }
1279 None
1280}
1281
1282pub fn format_structure_desc(desc: &StructureDesc) -> String {
1284 let mut parts = Vec::new();
1285 if let Some(ref id) = desc.struct_id {
1286 parts.push(id.clone());
1287 }
1288 for field in &desc.fields {
1289 parts.push(format!("{}:{}", field.name, field.field_type.type_name()));
1290 }
1291 parts.join(", ")
1292}
1293
1294pub fn format_structure_tree(desc: &StructureDesc) -> String {
1295 fn push_fields(out: &mut Vec<String>, fields: &[FieldDesc], indent: usize) {
1296 let prefix = " ".repeat(indent);
1297 for field in fields {
1298 match &field.field_type {
1299 FieldType::Structure(nested) => {
1300 out.push(format!("{}{}: structure", prefix, field.name));
1301 push_fields(out, &nested.fields, indent + 1);
1302 }
1303 FieldType::StructureArray(nested) => {
1304 out.push(format!("{}{}: structure[]", prefix, field.name));
1305 push_fields(out, &nested.fields, indent + 1);
1306 }
1307 FieldType::Union(variants) => {
1308 out.push(format!("{}{}: union", prefix, field.name));
1309 push_fields(out, variants, indent + 1);
1310 }
1311 FieldType::UnionArray(variants) => {
1312 out.push(format!("{}{}: union[]", prefix, field.name));
1313 push_fields(out, variants, indent + 1);
1314 }
1315 FieldType::BoundedString(bound) => {
1316 out.push(format!("{}{}: string<={}", prefix, field.name, bound));
1317 }
1318 _ => {
1319 out.push(format!(
1320 "{}{}: {}",
1321 prefix,
1322 field.name,
1323 field.field_type.type_name()
1324 ));
1325 }
1326 }
1327 }
1328 }
1329
1330 let mut lines = Vec::new();
1331 if let Some(id) = &desc.struct_id {
1332 lines.push(format!("struct {}", id));
1333 } else {
1334 lines.push("struct <anonymous>".to_string());
1335 }
1336 push_fields(&mut lines, &desc.fields, 0);
1337 lines.join("\n")
1338}
1339
1340pub fn extract_nt_scalar_value(decoded: &DecodedValue) -> Option<&DecodedValue> {
1342 if let DecodedValue::Structure(fields) = decoded {
1343 for (name, value) in fields {
1344 if name == "value" {
1345 return Some(value);
1346 }
1347 }
1348 }
1349 None
1350}
1351
1352pub fn format_compact_value(decoded: &DecodedValue) -> String {
1354 match decoded {
1355 DecodedValue::Structure(fields) => {
1356 if fields.is_empty() {
1357 return "{}".to_string();
1358 }
1359
1360 let mut parts = Vec::new();
1361
1362 for (name, val) in fields {
1363 let formatted = format_field_value_compact(name, val);
1364 if !formatted.is_empty() {
1365 parts.push(formatted);
1366 }
1367 }
1368
1369 parts.join(", ")
1370 }
1371 _ => format!("{}", decoded),
1372 }
1373}
1374
1375fn format_field_value_compact(name: &str, val: &DecodedValue) -> String {
1377 match val {
1378 DecodedValue::Structure(fields) => {
1379 match name {
1381 "alarm" => {
1382 let severity = fields.iter().find(|(n, _)| n == "severity");
1384 let message = fields.iter().find(|(n, _)| n == "message");
1385 let mut parts = Vec::new();
1386 if let Some((_, DecodedValue::Int32(s))) = severity {
1387 if *s != 0 {
1388 parts.push(format!("sev={}", s));
1389 }
1390 }
1391 if let Some((_, DecodedValue::String(m))) = message {
1392 if !m.is_empty() {
1393 parts.push(format!("\"{}\"", m));
1394 }
1395 }
1396 if parts.is_empty() {
1397 String::new() } else {
1399 format!("alarm={{{}}}", parts.join(", "))
1400 }
1401 }
1402 "timeStamp" => {
1403 let secs = fields.iter().find(|(n, _)| n == "secondsPastEpoch");
1405 if let Some((_, DecodedValue::Int64(s))) = secs {
1406 format!("ts={}", s)
1407 } else {
1408 String::new()
1409 }
1410 }
1411 "display" | "control" | "valueAlarm" => {
1412 String::new()
1414 }
1415 _ => {
1416 let nested: Vec<String> = fields
1418 .iter()
1419 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1420 .collect();
1421
1422 if nested.is_empty() {
1423 String::new()
1424 } else {
1425 format!("{}={{{}}}", name, nested.join(", "))
1426 }
1427 }
1428 }
1429 }
1430 _ => {
1431 format!("{}={}", name, format_scalar_value(val))
1432 }
1433 }
1434}
1435
1436fn format_scalar_value(val: &DecodedValue) -> String {
1438 match val {
1439 DecodedValue::Null => "null".to_string(),
1440 DecodedValue::Boolean(v) => format!("{}", v),
1441 DecodedValue::Int8(v) => format!("{}", v),
1442 DecodedValue::Int16(v) => format!("{}", v),
1443 DecodedValue::Int32(v) => format!("{}", v),
1444 DecodedValue::Int64(v) => format!("{}", v),
1445 DecodedValue::UInt8(v) => format!("{}", v),
1446 DecodedValue::UInt16(v) => format!("{}", v),
1447 DecodedValue::UInt32(v) => format!("{}", v),
1448 DecodedValue::UInt64(v) => format!("{}", v),
1449 DecodedValue::Float32(v) => format!("{:.4}", v),
1450 DecodedValue::Float64(v) => format!("{:.6}", v),
1451 DecodedValue::String(v) => format!("\"{}\"", v),
1452 DecodedValue::Array(arr) => {
1453 if arr.is_empty() {
1454 "[]".to_string()
1455 } else {
1456 let items: Vec<String> = arr.iter().map(|v| format_scalar_value(v)).collect();
1457 format!("[{}]", items.join(", "))
1458 }
1459 }
1460 DecodedValue::Structure(fields) => {
1461 let nested: Vec<String> = fields
1462 .iter()
1463 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1464 .collect();
1465 format!("{{{}}}", nested.join(", "))
1466 }
1467 DecodedValue::Raw(data) => {
1468 if data.len() <= 4 {
1469 format!("<{}>", hex::encode(data))
1470 } else {
1471 format!("<{}B>", data.len())
1472 }
1473 }
1474 }
1475}
1476
1477#[cfg(test)]
1478mod tests {
1479 use super::*;
1480
1481 #[test]
1482 fn test_decode_size() {
1483 let decoder = PvdDecoder::new(false);
1484
1485 assert_eq!(decoder.decode_size(&[5]), Ok((5, 1)));
1487 assert_eq!(decoder.decode_size(&[253]), Ok((253, 1)));
1488
1489 assert_eq!(
1491 decoder.decode_size(&[254, 0x00, 0x01, 0x00, 0x00]),
1492 Ok((256, 5))
1493 );
1494 }
1495
1496 #[test]
1497 fn decode_size_reports_truncation_rather_than_none() {
1498 let decoder = PvdDecoder::new(false);
1499 let err = decoder.decode_size(&[254, 0x01]).unwrap_err();
1501 assert_eq!(
1502 err,
1503 DecodeError::Truncated {
1504 needed: 5,
1505 available: 2
1506 }
1507 );
1508 }
1509
1510 #[test]
1511 fn decode_size_on_empty_buffer_is_truncated() {
1512 let decoder = PvdDecoder::new(false);
1513 assert_eq!(
1514 decoder.decode_size(&[]).unwrap_err(),
1515 DecodeError::Truncated {
1516 needed: 1,
1517 available: 0
1518 }
1519 );
1520 }
1521
1522 #[test]
1523 fn unknown_type_tag_is_named() {
1524 let decoder = PvdDecoder::new(false);
1525 assert_eq!(
1529 decoder.parse_field_desc(&[0x00, 0x40]).unwrap_err(),
1530 DecodeError::UnknownTypeTag(0x40)
1531 );
1532 }
1533
1534 #[test]
1535 fn limits_are_configurable_and_readable() {
1536 let limits = DecodeLimits {
1537 max_string_array: 7,
1538 ..DecodeLimits::default()
1539 };
1540 let decoder = PvdDecoder::with_limits(false, limits);
1541 assert_eq!(decoder.limits().max_string_array, 7);
1542 assert_eq!(decoder.limits().max_scalar_array, 4_000_000);
1543 }
1544
1545 #[test]
1546 fn default_limits_match_the_documented_values() {
1547 let d = DecodeLimits::default();
1548 assert_eq!(d.max_scalar_array, 4_000_000);
1549 assert_eq!(d.max_string_array, 65_536);
1550 assert_eq!(d.max_struct_array, 65_536);
1551 assert_eq!(d.max_union_array, 65_536);
1552 assert_eq!(d.max_variant_array, 65_536);
1553 }
1554
1555 #[test]
1556 fn test_parse_introspection_full_with_id() {
1557 let decoder = PvdDecoder::new(false);
1558 let data = vec![
1559 0xFD, 0x06, 0x00, 0x80, 0x00, 0x01, 0x05, b'v', b'a', b'l', b'u', b'e', 0x43, ];
1567 let desc = decoder
1568 .parse_introspection(&data)
1569 .expect("parsed introspection");
1570 assert_eq!(desc.fields.len(), 1);
1571 assert_eq!(desc.fields[0].name, "value");
1572 match desc.fields[0].field_type {
1573 FieldType::Scalar(TypeCode::Float64) => {}
1574 _ => panic!("expected float64 value field"),
1575 }
1576 }
1577
1578 #[test]
1579 fn test_decode_string() {
1580 let decoder = PvdDecoder::new(false);
1581
1582 assert_eq!(decoder.decode_string(&[0]), Ok((String::new(), 1)));
1584
1585 let data = [5, b'h', b'e', b'l', b'l', b'o'];
1587 assert_eq!(decoder.decode_string(&data), Ok(("hello".to_string(), 6)));
1588 }
1589
1590 #[test]
1591 fn decode_variant_accepts_full_with_id_type_tag() {
1592 let decoder = PvdDecoder::new(false);
1593 let data = [0xFD, 0x02, 0x00, 0x60, 0x02, b'o', b'k'];
1595 let (value, consumed) = decoder
1596 .decode_value(&data, &FieldType::Variant)
1597 .expect("decode variant");
1598 assert_eq!(consumed, data.len());
1599 assert!(matches!(value, DecodedValue::String(ref s) if s == "ok"));
1600 }
1601
1602 #[test]
1603 fn test_decode_bitset_whole_structure() {
1604 let decoder = PvdDecoder::new(false);
1605 let desc = StructureDesc {
1606 struct_id: None,
1607 fields: vec![FieldDesc {
1608 name: "value".to_string(),
1609 field_type: FieldType::Scalar(TypeCode::Float64),
1610 }],
1611 };
1612 let mut data = Vec::new();
1614 data.push(0x01);
1615 data.push(0x01);
1616 data.extend_from_slice(&1.25f64.to_le_bytes());
1617
1618 let (decoded, _consumed) = decoder
1619 .decode_structure_with_bitset(&data, &desc)
1620 .expect("decoded");
1621 if let DecodedValue::Structure(fields) = decoded {
1622 assert_eq!(fields.len(), 1);
1623 assert_eq!(fields[0].0, "value");
1624 } else {
1625 panic!("expected structure");
1626 }
1627 }
1628
1629 #[test]
1630 fn format_structure_tree_includes_nested_fields() {
1631 let desc = StructureDesc {
1632 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1633 fields: vec![
1634 FieldDesc {
1635 name: "value".to_string(),
1636 field_type: FieldType::Scalar(TypeCode::Float64),
1637 },
1638 FieldDesc {
1639 name: "alarm".to_string(),
1640 field_type: FieldType::Structure(StructureDesc {
1641 struct_id: None,
1642 fields: vec![
1643 FieldDesc {
1644 name: "severity".to_string(),
1645 field_type: FieldType::Scalar(TypeCode::Int32),
1646 },
1647 FieldDesc {
1648 name: "message".to_string(),
1649 field_type: FieldType::String,
1650 },
1651 ],
1652 }),
1653 },
1654 ],
1655 };
1656
1657 let rendered = format_structure_tree(&desc);
1658 assert!(rendered.contains("struct epics:nt/NTScalar:1.0"));
1659 assert!(rendered.contains("value: double"));
1660 assert!(rendered.contains("alarm: structure"));
1661 assert!(rendered.contains("severity: int"));
1662 assert!(rendered.contains("message: string"));
1663 }
1664
1665 #[test]
1666 fn decode_string_array_not_capped_at_100_items() {
1667 fn encode_size(size: usize) -> Vec<u8> {
1668 if size == 0 {
1669 return vec![0x00];
1670 }
1671 if size < 254 {
1672 return vec![size as u8];
1673 }
1674 let mut out = vec![0xFE];
1675 out.extend_from_slice(&(size as u32).to_le_bytes());
1676 out
1677 }
1678
1679 let item_count = 150usize;
1680 let mut raw = encode_size(item_count);
1681 for idx in 0..item_count {
1682 let s = format!("PV:{}", idx);
1683 raw.extend_from_slice(&encode_size(s.len()));
1684 raw.extend_from_slice(s.as_bytes());
1685 }
1686
1687 let decoder = PvdDecoder::new(false);
1688 let (decoded, _consumed) = decoder
1689 .decode_value(&raw, &FieldType::StringArray)
1690 .expect("decoded");
1691
1692 let DecodedValue::Array(items) = decoded else {
1693 panic!("expected decoded array");
1694 };
1695 assert_eq!(items.len(), item_count);
1696 }
1697
1698 fn size_prefix(n: u32) -> Vec<u8> {
1700 let mut v = vec![254u8];
1701 v.extend_from_slice(&n.to_le_bytes());
1702 v
1703 }
1704
1705 #[test]
1706 fn oversized_string_array_errors_instead_of_truncating() {
1707 let limits = DecodeLimits {
1708 max_string_array: 4,
1709 ..DecodeLimits::default()
1710 };
1711 let decoder = PvdDecoder::with_limits(false, limits);
1712 let mut data = size_prefix(9);
1713 for _ in 0..9 {
1714 data.push(1); data.push(b'x');
1716 }
1717 assert_eq!(
1718 decoder
1719 .decode_value(&data, &FieldType::StringArray)
1720 .unwrap_err(),
1721 DecodeError::ArrayTooLarge {
1722 kind: "string array",
1723 count: 9,
1724 limit: 4
1725 }
1726 );
1727 }
1728
1729 #[test]
1730 fn count_larger_than_the_buffer_is_rejected_before_allocating() {
1731 let decoder = PvdDecoder::new(false);
1732 let data = size_prefix(4_000_000_000);
1734 let err = decoder
1735 .decode_value(&data, &FieldType::ScalarArray(TypeCode::Int32))
1736 .unwrap_err();
1737 assert_eq!(
1740 err,
1741 DecodeError::ArrayTooLarge {
1742 kind: "scalar array",
1743 count: 4_000_000_000,
1744 limit: 4_000_000,
1745 }
1746 );
1747 }
1748
1749 #[test]
1750 fn count_within_the_limit_but_beyond_the_buffer_is_rejected() {
1751 let decoder = PvdDecoder::new(false);
1752 let mut data = size_prefix(1000);
1754 data.extend_from_slice(&[0u8; 8]);
1755 assert_eq!(
1756 decoder
1757 .decode_value(&data, &FieldType::ScalarArray(TypeCode::Int32))
1758 .unwrap_err(),
1759 DecodeError::CountExceedsBuffer {
1760 count: 1000,
1761 min_bytes: 4000,
1762 available: 8
1763 }
1764 );
1765 }
1766
1767 #[test]
1768 fn oversized_struct_array_errors() {
1769 let limits = DecodeLimits {
1770 max_struct_array: 2,
1771 ..DecodeLimits::default()
1772 };
1773 let decoder = PvdDecoder::with_limits(false, limits);
1774 let mut inner = StructureDesc::new();
1775 inner.fields.push(FieldDesc {
1776 name: "a".to_string(),
1777 field_type: FieldType::Scalar(TypeCode::Int8),
1778 });
1779 let mut data = size_prefix(5);
1780 for _ in 0..5 {
1781 data.push(1); data.push(0); }
1784 assert_eq!(
1785 decoder
1786 .decode_value(&data, &FieldType::StructureArray(inner))
1787 .unwrap_err(),
1788 DecodeError::ArrayTooLarge {
1789 kind: "structure array",
1790 count: 5,
1791 limit: 2
1792 }
1793 );
1794 }
1795
1796 #[test]
1801 fn truncated_array_no_longer_desyncs_the_following_field() {
1802 let limits = DecodeLimits {
1803 max_string_array: 2,
1804 ..DecodeLimits::default()
1805 };
1806 let decoder = PvdDecoder::with_limits(false, limits);
1807
1808 let mut desc = StructureDesc::new();
1809 desc.fields.push(FieldDesc {
1810 name: "names".to_string(),
1811 field_type: FieldType::StringArray,
1812 });
1813 desc.fields.push(FieldDesc {
1814 name: "count".to_string(),
1815 field_type: FieldType::Scalar(TypeCode::Int32),
1816 });
1817
1818 let mut data = size_prefix(4);
1819 for _ in 0..4 {
1820 data.push(1);
1821 data.push(b'x');
1822 }
1823 data.extend_from_slice(&7i32.to_le_bytes());
1824
1825 let (value, _) = decoder.decode_structure(&data, &desc).unwrap();
1828 let DecodedValue::Structure(fields) = value else {
1829 panic!("expected a structure");
1830 };
1831 assert!(
1832 fields.iter().all(|(name, _)| name != "count"),
1833 "a desynced 'count' must not be reported: got {fields:?}"
1834 );
1835
1836 let loose = PvdDecoder::new(false);
1840 let (value, consumed) = loose.decode_structure(&data, &desc).unwrap();
1841 let DecodedValue::Structure(fields) = value else {
1842 panic!("expected a structure");
1843 };
1844 assert_eq!(consumed, data.len());
1845 assert_eq!(fields.len(), 2);
1846 assert_eq!(fields[1].0, "count");
1847 assert!(
1848 matches!(fields[1].1, DecodedValue::Int32(7)),
1849 "expected count == 7, got {:?}",
1850 fields[1].1
1851 );
1852 }
1853
1854 #[test]
1855 fn raising_the_limit_lets_the_same_payload_decode() {
1856 let mut data = size_prefix(5);
1857 for _ in 0..5 {
1858 data.push(1);
1859 data.push(b'x');
1860 }
1861 let strict = PvdDecoder::with_limits(
1862 false,
1863 DecodeLimits {
1864 max_string_array: 2,
1865 ..DecodeLimits::default()
1866 },
1867 );
1868 assert!(strict.decode_value(&data, &FieldType::StringArray).is_err());
1869
1870 let loose = PvdDecoder::new(false);
1871 let (value, _) = loose.decode_value(&data, &FieldType::StringArray).unwrap();
1872 let DecodedValue::Array(items) = value else {
1873 panic!("expected an array");
1874 };
1875 assert_eq!(items.len(), 5);
1876 }
1877}