1use std::fmt;
7use tracing::debug;
8
9pub use crate::epics_decode::decode_string;
12
13#[repr(u8)]
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum TypeCode {
17 Null = 0xFF,
18 Boolean = 0x00,
19 Int8 = 0x20,
20 Int16 = 0x21,
21 Int32 = 0x22,
22 Int64 = 0x23,
23 UInt8 = 0x24,
24 UInt16 = 0x25,
25 UInt32 = 0x26,
26 UInt64 = 0x27,
27 Float32 = 0x42,
28 Float64 = 0x43,
29 String = 0x60,
30 Variant = 0xFE, }
33
34impl TypeCode {
35 pub fn from_byte(b: u8) -> Option<Self> {
36 let base = b & 0xE7;
38 match base {
39 0x00 => Some(TypeCode::Boolean),
40 0x20 => Some(TypeCode::Int8),
41 0x21 => Some(TypeCode::Int16),
42 0x22 => Some(TypeCode::Int32),
43 0x23 => Some(TypeCode::Int64),
44 0x24 => Some(TypeCode::UInt8),
45 0x25 => Some(TypeCode::UInt16),
46 0x26 => Some(TypeCode::UInt32),
47 0x27 => Some(TypeCode::UInt64),
48 0x42 => Some(TypeCode::Float32),
49 0x43 => Some(TypeCode::Float64),
50 0x60 => Some(TypeCode::String),
51 _ => None,
52 }
53 }
54
55 pub fn size(&self) -> Option<usize> {
56 match self {
57 TypeCode::Boolean | TypeCode::Int8 | TypeCode::UInt8 => Some(1),
58 TypeCode::Int16 | TypeCode::UInt16 => Some(2),
59 TypeCode::Int32 | TypeCode::UInt32 | TypeCode::Float32 => Some(4),
60 TypeCode::Int64 | TypeCode::UInt64 | TypeCode::Float64 => Some(8),
61 TypeCode::String | TypeCode::Null | TypeCode::Variant => None,
62 }
63 }
64}
65
66#[derive(Debug, Clone)]
68pub enum FieldType {
69 Scalar(TypeCode),
70 ScalarArray(TypeCode),
71 String,
72 StringArray,
73 Structure(StructureDesc),
74 StructureArray(StructureDesc),
75 Union(Vec<FieldDesc>),
76 UnionArray(Vec<FieldDesc>),
77 Variant,
78 VariantArray,
79 BoundedString(u32),
80}
81
82impl FieldType {
83 pub fn type_name(&self) -> &'static str {
84 match self {
85 FieldType::Scalar(tc) => match tc {
86 TypeCode::Boolean => "boolean",
87 TypeCode::Int8 => "byte",
88 TypeCode::Int16 => "short",
89 TypeCode::Int32 => "int",
90 TypeCode::Int64 => "long",
91 TypeCode::UInt8 => "ubyte",
92 TypeCode::UInt16 => "ushort",
93 TypeCode::UInt32 => "uint",
94 TypeCode::UInt64 => "ulong",
95 TypeCode::Float32 => "float",
96 TypeCode::Float64 => "double",
97 TypeCode::String => "string",
98 _ => "unknown",
99 },
100 FieldType::ScalarArray(tc) => match tc {
101 TypeCode::Float64 => "double[]",
102 TypeCode::Float32 => "float[]",
103 TypeCode::Int64 => "long[]",
104 TypeCode::Int32 => "int[]",
105 _ => "array",
106 },
107 FieldType::String => "string",
108 FieldType::StringArray => "string[]",
109 FieldType::Structure(_) => "structure",
110 FieldType::StructureArray(_) => "structure[]",
111 FieldType::Union(_) => "union",
112 FieldType::UnionArray(_) => "union[]",
113 FieldType::Variant => "any",
114 FieldType::VariantArray => "any[]",
115 FieldType::BoundedString(_) => "string",
116 }
117 }
118
119 pub fn heap_size(&self) -> usize {
123 match self {
124 FieldType::Structure(s) | FieldType::StructureArray(s) => s.heap_size(),
125 FieldType::Union(f) | FieldType::UnionArray(f) => {
126 f.capacity() * std::mem::size_of::<FieldDesc>()
127 + f.iter().map(FieldDesc::heap_size).sum::<usize>()
128 }
129 _ => 0,
130 }
131 }
132}
133
134#[derive(Debug, Clone)]
136pub struct FieldDesc {
137 pub name: String,
138 pub field_type: FieldType,
139}
140
141impl FieldDesc {
142 pub fn heap_size(&self) -> usize {
144 self.name.capacity() + self.field_type.heap_size()
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct StructureDesc {
151 pub struct_id: Option<String>,
152 pub fields: Vec<FieldDesc>,
153}
154
155impl StructureDesc {
156 pub fn new() -> Self {
157 Self {
158 struct_id: None,
159 fields: Vec::new(),
160 }
161 }
162
163 pub fn field(&self, name: &str) -> Option<&FieldDesc> {
165 self.fields.iter().find(|f| f.name == name)
166 }
167
168 pub fn heap_size(&self) -> usize {
176 let id = self.struct_id.as_ref().map_or(0, |s| s.capacity());
177 let fields = self.fields.capacity() * std::mem::size_of::<FieldDesc>()
178 + self.fields.iter().map(FieldDesc::heap_size).sum::<usize>();
179 id + fields
180 }
181}
182
183impl Default for StructureDesc {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189impl fmt::Display for StructureDesc {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 fn write_indent(f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
192 for _ in 0..depth {
193 write!(f, " ")?;
194 }
195 Ok(())
196 }
197
198 fn write_field_type(
199 f: &mut fmt::Formatter<'_>,
200 ft: &FieldType,
201 depth: usize,
202 ) -> fmt::Result {
203 match ft {
204 FieldType::Structure(desc) => write_structure(f, desc, depth),
205 FieldType::StructureArray(desc) => {
206 write_structure(f, desc, depth)?;
207 write!(f, "[]")
208 }
209 FieldType::Union(fields) => {
210 writeln!(f, "union")?;
211 for field in fields {
212 write_indent(f, depth + 1)?;
213 write!(f, "{} ", field.name)?;
214 write_field_type(f, &field.field_type, depth + 1)?;
215 writeln!(f)?;
216 }
217 Ok(())
218 }
219 FieldType::UnionArray(fields) => {
220 writeln!(f, "union[]")?;
221 for field in fields {
222 write_indent(f, depth + 1)?;
223 write!(f, "{} ", field.name)?;
224 write_field_type(f, &field.field_type, depth + 1)?;
225 writeln!(f)?;
226 }
227 Ok(())
228 }
229 other => write!(f, "{}", other.type_name()),
230 }
231 }
232
233 fn write_structure(
234 f: &mut fmt::Formatter<'_>,
235 desc: &StructureDesc,
236 depth: usize,
237 ) -> fmt::Result {
238 if let Some(id) = &desc.struct_id {
239 write!(f, "structure «{}»", id)?;
240 } else {
241 write!(f, "structure")?;
242 }
243 if desc.fields.is_empty() {
244 return Ok(());
245 }
246 writeln!(f)?;
247 for field in &desc.fields {
248 write_indent(f, depth + 1)?;
249 write!(f, "{} ", field.name)?;
250 write_field_type(f, &field.field_type, depth + 1)?;
251 writeln!(f)?;
252 }
253 Ok(())
254 }
255
256 write_structure(f, self, 0)
257 }
258}
259
260#[derive(Debug, Clone)]
262pub enum DecodedValue {
263 Null,
264 Boolean(bool),
265 Int8(i8),
266 Int16(i16),
267 Int32(i32),
268 Int64(i64),
269 UInt8(u8),
270 UInt16(u16),
271 UInt32(u32),
272 UInt64(u64),
273 Float32(f32),
274 Float64(f64),
275 String(String),
276 Array(Vec<DecodedValue>),
277 Structure(Vec<(String, DecodedValue)>),
278 Raw(Vec<u8>),
279}
280
281impl fmt::Display for DecodedValue {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 match self {
284 DecodedValue::Null => write!(f, "null"),
285 DecodedValue::Boolean(v) => write!(f, "{}", v),
286 DecodedValue::Int8(v) => write!(f, "{}", v),
287 DecodedValue::Int16(v) => write!(f, "{}", v),
288 DecodedValue::Int32(v) => write!(f, "{}", v),
289 DecodedValue::Int64(v) => write!(f, "{}", v),
290 DecodedValue::UInt8(v) => write!(f, "{}", v),
291 DecodedValue::UInt16(v) => write!(f, "{}", v),
292 DecodedValue::UInt32(v) => write!(f, "{}", v),
293 DecodedValue::UInt64(v) => write!(f, "{}", v),
294 DecodedValue::Float32(v) => write!(f, "{:.6}", v),
295 DecodedValue::Float64(v) => write!(f, "{:.6}", v),
296 DecodedValue::String(v) => write!(f, "\"{}\"", v),
297 DecodedValue::Array(arr) => {
298 write!(f, "[")?;
299 for (i, v) in arr.iter().enumerate() {
300 if i > 0 {
301 write!(f, ", ")?;
302 }
303 write!(f, "{}", v)?;
304 }
305 write!(f, "]")
306 }
307 DecodedValue::Structure(fields) => {
308 write!(f, "{{")?;
309 for (i, (name, val)) in fields.iter().enumerate() {
310 if i > 0 {
311 write!(f, ", ")?;
312 }
313 write!(f, "{}={}", name, val)?;
314 }
315 write!(f, "}}")
316 }
317 DecodedValue::Raw(data) => {
318 if data.len() <= 8 {
319 write!(f, "<{} bytes: {}>", data.len(), hex::encode(data))
320 } else {
321 write!(f, "<{} bytes>", data.len())
322 }
323 }
324 }
325 }
326}
327
328pub struct PvdDecoder {
330 is_be: bool,
331 registry: std::cell::RefCell<std::collections::HashMap<u16, FieldType>>,
334}
335
336impl PvdDecoder {
337 pub fn new(is_be: bool) -> Self {
338 Self {
339 is_be,
340 registry: std::cell::RefCell::new(std::collections::HashMap::new()),
341 }
342 }
343
344 pub fn decode_size(&self, data: &[u8]) -> Option<(usize, usize)> {
346 if data.is_empty() {
347 return None;
348 }
349 let first = data[0];
350 if first == 0xFF {
351 return Some((0, 1)); }
354 if first < 254 {
355 return Some((first as usize, 1));
356 }
357 if first == 254 {
358 if data.len() < 5 {
360 return None;
361 }
362 let size = if self.is_be {
363 u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize
364 } else {
365 u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize
366 };
367 return Some((size, 5));
368 }
369 None
371 }
372
373 pub fn decode_string(&self, data: &[u8]) -> Option<(String, usize)> {
375 let (size, size_bytes) = self.decode_size(data)?;
376 if size == 0 {
377 return Some((String::new(), size_bytes));
378 }
379 if data.len() < size_bytes + size {
380 return None;
381 }
382 let s = std::str::from_utf8(&data[size_bytes..size_bytes + size]).ok()?;
383 Some((s.to_string(), size_bytes + size))
384 }
385
386 pub fn parse_field_desc(&self, data: &[u8]) -> Option<(FieldDesc, usize)> {
388 if data.is_empty() {
389 return None;
390 }
391
392 let mut offset = 0;
393
394 let (name, consumed) = self.decode_string(&data[offset..])?;
396 offset += consumed;
397
398 if offset >= data.len() {
399 return None;
400 }
401
402 let (field_type, type_consumed) = self.parse_type_desc(&data[offset..])?;
404 offset += type_consumed;
405
406 Some((FieldDesc { name, field_type }, offset))
407 }
408
409 fn parse_type_desc(&self, data: &[u8]) -> Option<(FieldType, usize)> {
411 if data.is_empty() {
412 return None;
413 }
414
415 let type_byte = data[0];
416 let mut offset = 1;
417
418 if type_byte == 0xFF {
420 return Some((FieldType::Variant, 1));
421 }
422
423 if type_byte == 0xFD {
426 if data.len() < 3 {
427 return None;
428 }
429 let key = if self.is_be {
430 u16::from_be_bytes([data[1], data[2]])
431 } else {
432 u16::from_le_bytes([data[1], data[2]])
433 };
434 if let Some((field_type, consumed)) = self.parse_type_desc(&data[3..]) {
435 self.registry.borrow_mut().insert(key, field_type.clone());
436 return Some((field_type, 3 + consumed));
437 }
438 return None;
439 }
440
441 if type_byte == 0xFE {
444 if data.len() < 3 {
445 return None;
446 }
447 let key = if self.is_be {
448 u16::from_be_bytes([data[1], data[2]])
449 } else {
450 u16::from_le_bytes([data[1], data[2]])
451 };
452 if let Some(ft) = self.registry.borrow().get(&key) {
453 return Some((ft.clone(), 3));
454 }
455 debug!(
456 "Type descriptor ONLY_ID (0xFE) key={} not found in registry",
457 key
458 );
459 return None;
460 }
461
462 if type_byte == 0x80 || type_byte == 0x88 {
464 let is_array = (type_byte & 0x08) != 0;
465 if is_array {
466 if offset >= data.len() || data[offset] != 0x80 {
468 return None;
469 }
470 offset += 1;
471 }
472 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
473 offset += consumed;
474 if is_array {
475 return Some((FieldType::StructureArray(struct_desc), offset));
476 } else {
477 return Some((FieldType::Structure(struct_desc), offset));
478 }
479 }
480
481 if type_byte == 0x81 || type_byte == 0x89 {
483 let is_array = (type_byte & 0x08) != 0;
484 if is_array {
485 if offset >= data.len() || data[offset] != 0x81 {
487 return None;
488 }
489 offset += 1;
490 }
491 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
493 offset += consumed;
494 if is_array {
495 return Some((FieldType::UnionArray(struct_desc.fields), offset));
496 } else {
497 return Some((FieldType::Union(struct_desc.fields), offset));
498 }
499 }
500
501 if type_byte == 0x82 {
503 return Some((FieldType::Variant, 1));
504 }
505 if type_byte == 0x8A {
506 return Some((FieldType::VariantArray, 1));
507 }
508
509 if type_byte == 0x83 || type_byte == 0x86 {
511 let (bound, consumed) = self.decode_size(&data[offset..])?;
512 offset += consumed;
513 return Some((FieldType::BoundedString(bound as u32), offset));
514 }
515
516 let scalar_or_array = type_byte & 0x18;
519 let is_array = scalar_or_array != 0;
520 if is_array && scalar_or_array != 0x08 {
521 let (_bound, consumed) = self.decode_size(&data[offset..])?;
523 offset += consumed;
524 }
525 let base_type = type_byte & 0xE7;
526
527 if base_type == 0x60 {
529 if is_array {
530 return Some((FieldType::StringArray, offset));
531 } else {
532 return Some((FieldType::String, offset));
533 }
534 }
535
536 if let Some(tc) = TypeCode::from_byte(base_type) {
538 if is_array {
539 return Some((FieldType::ScalarArray(tc), offset));
540 } else {
541 return Some((FieldType::Scalar(tc), offset));
542 }
543 }
544
545 debug!("Unknown type byte: 0x{:02x}", type_byte);
546 None
547 }
548
549 fn parse_structure_desc(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
551 let mut offset = 0;
552
553 let (struct_id, consumed) = self.decode_string(&data[offset..])?;
555 offset += consumed;
556
557 let struct_id = if struct_id.is_empty() {
558 None
559 } else {
560 Some(struct_id)
561 };
562
563 let (field_count, consumed) = self.decode_size(&data[offset..])?;
565 offset += consumed;
566
567 let mut fields = Vec::with_capacity(field_count);
568
569 for _ in 0..field_count {
570 if offset >= data.len() {
571 break;
572 }
573 if let Some((field, consumed)) = self.parse_field_desc(&data[offset..]) {
574 offset += consumed;
575 fields.push(field);
576 } else {
577 break;
578 }
579 }
580
581 Some((StructureDesc { struct_id, fields }, offset))
582 }
583
584 pub fn parse_introspection(&self, data: &[u8]) -> Option<StructureDesc> {
586 self.parse_introspection_with_len(data)
587 .map(|(desc, _)| desc)
588 }
589
590 pub fn parse_introspection_with_len(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
592 if data.is_empty() {
593 return None;
594 }
595
596 let type_byte = data[0];
598
599 if type_byte == 0x80 {
601 let (desc, consumed) = self.parse_structure_desc(&data[1..])?;
602 return Some((desc, 1 + consumed));
603 }
604
605 if type_byte == 0xFD {
608 if data.len() < 3 {
609 return None;
610 }
611 let key = if self.is_be {
612 u16::from_be_bytes([data[1], data[2]])
613 } else {
614 u16::from_le_bytes([data[1], data[2]])
615 };
616 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[3..]) {
617 if !desc.fields.is_empty() {
619 self.registry
620 .borrow_mut()
621 .insert(key, FieldType::Structure(desc.clone()));
622 } else {
623 self.registry
624 .borrow_mut()
625 .insert(key, FieldType::Structure(desc.clone()));
626 }
627 return Some((desc, 3 + consumed));
628 }
629 return None;
630 }
631
632 if type_byte == 0xFE {
635 if data.len() < 3 {
636 return None;
637 }
638 let key = if self.is_be {
639 u16::from_be_bytes([data[1], data[2]])
640 } else {
641 u16::from_le_bytes([data[1], data[2]])
642 };
643 if let Some(ft) = self.registry.borrow().get(&key) {
644 if let FieldType::Structure(desc) = ft {
645 return Some((desc.clone(), 3));
646 }
647 }
648 debug!(
649 "Introspection ONLY_ID (0xFE) key={} not found in registry",
650 key
651 );
652 return None;
653 }
654
655 debug!("Unexpected introspection type byte: 0x{:02x}", type_byte);
656 None
657 }
658
659 fn decode_scalar(&self, data: &[u8], tc: TypeCode) -> Option<(DecodedValue, usize)> {
661 let size = tc.size()?;
662 if data.len() < size {
663 return None;
664 }
665
666 let value = match tc {
667 TypeCode::Boolean => DecodedValue::Boolean(data[0] != 0),
668 TypeCode::Int8 => DecodedValue::Int8(data[0] as i8),
669 TypeCode::UInt8 => DecodedValue::UInt8(data[0]),
670 TypeCode::Int16 => {
671 let v = if self.is_be {
672 i16::from_be_bytes([data[0], data[1]])
673 } else {
674 i16::from_le_bytes([data[0], data[1]])
675 };
676 DecodedValue::Int16(v)
677 }
678 TypeCode::UInt16 => {
679 let v = if self.is_be {
680 u16::from_be_bytes([data[0], data[1]])
681 } else {
682 u16::from_le_bytes([data[0], data[1]])
683 };
684 DecodedValue::UInt16(v)
685 }
686 TypeCode::Int32 => {
687 let v = if self.is_be {
688 i32::from_be_bytes(data[0..4].try_into().unwrap())
689 } else {
690 i32::from_le_bytes(data[0..4].try_into().unwrap())
691 };
692 DecodedValue::Int32(v)
693 }
694 TypeCode::UInt32 => {
695 let v = if self.is_be {
696 u32::from_be_bytes(data[0..4].try_into().unwrap())
697 } else {
698 u32::from_le_bytes(data[0..4].try_into().unwrap())
699 };
700 DecodedValue::UInt32(v)
701 }
702 TypeCode::Int64 => {
703 let v = if self.is_be {
704 i64::from_be_bytes(data[0..8].try_into().unwrap())
705 } else {
706 i64::from_le_bytes(data[0..8].try_into().unwrap())
707 };
708 DecodedValue::Int64(v)
709 }
710 TypeCode::UInt64 => {
711 let v = if self.is_be {
712 u64::from_be_bytes(data[0..8].try_into().unwrap())
713 } else {
714 u64::from_le_bytes(data[0..8].try_into().unwrap())
715 };
716 DecodedValue::UInt64(v)
717 }
718 TypeCode::Float32 => {
719 let v = if self.is_be {
720 f32::from_be_bytes(data[0..4].try_into().unwrap())
721 } else {
722 f32::from_le_bytes(data[0..4].try_into().unwrap())
723 };
724 DecodedValue::Float32(v)
725 }
726 TypeCode::Float64 => {
727 let v = if self.is_be {
728 f64::from_be_bytes(data[0..8].try_into().unwrap())
729 } else {
730 f64::from_le_bytes(data[0..8].try_into().unwrap())
731 };
732 DecodedValue::Float64(v)
733 }
734 _ => return None,
735 };
736
737 Some((value, size))
738 }
739
740 pub fn decode_value(
742 &self,
743 data: &[u8],
744 field_type: &FieldType,
745 ) -> Option<(DecodedValue, usize)> {
746 match field_type {
747 FieldType::Scalar(tc) => self.decode_scalar(data, *tc),
748 FieldType::String | FieldType::BoundedString(_) => {
749 let (s, consumed) = self.decode_string(data)?;
750 Some((DecodedValue::String(s), consumed))
751 }
752 FieldType::ScalarArray(tc) => {
753 let (count, size_consumed) = self.decode_size(data)?;
754 let mut offset = size_consumed;
755 let limit = count.min(4_000_000);
756 let mut values = Vec::with_capacity(limit);
757 let elem_size = tc.size().unwrap_or(1);
758 for _ in 0..limit {
759 if let Some((val, consumed)) = self.decode_scalar(&data[offset..], *tc) {
760 values.push(val);
761 offset += consumed;
762 } else {
763 break;
764 }
765 }
766 let remaining = count.saturating_sub(limit);
769 offset += remaining * elem_size;
770 Some((DecodedValue::Array(values), offset))
771 }
772 FieldType::StringArray => {
773 let (count, size_consumed) = self.decode_size(data)?;
774 let mut offset = size_consumed;
775 let max_items = count.min(4096);
776 let mut values = Vec::with_capacity(max_items);
777 for _ in 0..max_items {
778 if let Some((s, consumed)) = self.decode_string(&data[offset..]) {
779 values.push(DecodedValue::String(s));
780 offset += consumed;
781 } else {
782 break;
783 }
784 }
785 Some((DecodedValue::Array(values), offset))
786 }
787 FieldType::Structure(desc) => self.decode_structure(data, desc),
788 FieldType::StructureArray(desc) => {
789 let (count, size_consumed) = self.decode_size(data)?;
790 let mut offset = size_consumed;
791 let mut values = Vec::with_capacity(count.min(256));
792 for _ in 0..count.min(256) {
793 if offset >= data.len() {
795 return None;
796 }
797 let null_indicator = data[offset];
798 offset += 1;
799 if null_indicator == 0 {
800 values.push(DecodedValue::Structure(Vec::new()));
802 continue;
803 }
804 let (item, consumed) = self.decode_structure(&data[offset..], desc)?;
805 values.push(item);
806 offset += consumed;
807 }
808 Some((DecodedValue::Array(values), offset))
809 }
810 FieldType::Union(fields) => {
811 let (selector, consumed) = self.decode_size(data)?;
812 let field = fields.get(selector)?;
813 let (value, val_consumed) =
814 self.decode_value(&data[consumed..], &field.field_type)?;
815 Some((
816 DecodedValue::Structure(vec![(field.name.clone(), value)]),
817 consumed + val_consumed,
818 ))
819 }
820 FieldType::UnionArray(fields) => {
821 let (count, size_consumed) = self.decode_size(data)?;
822 let mut offset = size_consumed;
823 let mut values = Vec::with_capacity(count.min(128));
824 for _ in 0..count.min(128) {
825 let (selector, consumed) = self.decode_size(&data[offset..])?;
826 offset += consumed;
827 let field = fields.get(selector)?;
828 let (value, val_consumed) =
829 self.decode_value(&data[offset..], &field.field_type)?;
830 offset += val_consumed;
831 values.push(DecodedValue::Structure(vec![(field.name.clone(), value)]));
832 }
833 Some((DecodedValue::Array(values), offset))
834 }
835 FieldType::Variant => {
836 if data.is_empty() {
837 return None;
838 }
839 if data[0] == 0xFF {
840 return Some((DecodedValue::Null, 1));
841 }
842 let (variant_type, type_consumed) = self.parse_type_desc(data)?;
843 let (variant_value, value_consumed) =
844 self.decode_value(&data[type_consumed..], &variant_type)?;
845 Some((variant_value, type_consumed + value_consumed))
846 }
847 FieldType::VariantArray => {
848 let (count, size_consumed) = self.decode_size(data)?;
849 let mut offset = size_consumed;
850 let mut values = Vec::with_capacity(count.min(128));
851 for _ in 0..count.min(128) {
852 let (v, consumed) = self.decode_value(&data[offset..], &FieldType::Variant)?;
853 values.push(v);
854 offset += consumed;
855 }
856 Some((DecodedValue::Array(values), offset))
857 }
858 }
859 }
860
861 pub fn decode_structure(
863 &self,
864 data: &[u8],
865 desc: &StructureDesc,
866 ) -> Option<(DecodedValue, usize)> {
867 let mut offset = 0;
868 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
869
870 for field in &desc.fields {
871 if offset >= data.len() {
872 break;
873 }
874 if let Some((value, consumed)) = self.decode_value(&data[offset..], &field.field_type) {
875 fields.push((field.name.clone(), value));
876 offset += consumed;
877 } else {
878 break;
880 }
881 }
882
883 Some((DecodedValue::Structure(fields), offset))
884 }
885
886 pub fn decode_structure_with_bitset(
889 &self,
890 data: &[u8],
891 desc: &StructureDesc,
892 ) -> Option<(DecodedValue, usize)> {
893 if data.is_empty() {
894 return None;
895 }
896
897 let mut offset = 0;
898
899 let (bitset_size, size_consumed) = self.decode_size(data)?;
901 offset += size_consumed;
902
903 if bitset_size == 0 || offset + bitset_size > data.len() {
904 return Some((DecodedValue::Structure(vec![]), offset));
905 }
906
907 let bitset = &data[offset..offset + bitset_size];
908 offset += bitset_size;
909
910 let (value, consumed) =
911 self.decode_structure_with_bitset_body(&data[offset..], desc, bitset)?;
912 Some((value, offset + consumed))
913 }
914
915 pub fn decode_structure_with_bitset_and_overrun(
917 &self,
918 data: &[u8],
919 desc: &StructureDesc,
920 ) -> Option<(DecodedValue, usize)> {
921 if data.is_empty() {
922 return None;
923 }
924 let mut offset = 0usize;
925 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
926 offset += consumed1;
927 if offset + changed_size > data.len() {
928 return None;
929 }
930 let changed = &data[offset..offset + changed_size];
931 offset += changed_size;
932
933 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
934 offset += consumed2;
935 if offset + overrun_size > data.len() {
936 return None;
937 }
938 offset += overrun_size;
939
940 let (value, consumed) =
941 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
942 Some((value, offset + consumed))
943 }
944
945 pub fn decode_structure_with_bitset_then_overrun(
947 &self,
948 data: &[u8],
949 desc: &StructureDesc,
950 ) -> Option<(DecodedValue, usize)> {
951 if data.is_empty() {
952 return None;
953 }
954 let mut offset = 0usize;
955 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
956 offset += consumed1;
957 if offset + changed_size > data.len() {
958 return None;
959 }
960 let changed = &data[offset..offset + changed_size];
961 offset += changed_size;
962
963 let (value, consumed) =
964 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
965 offset += consumed;
966
967 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
968 offset += consumed2;
969 if offset + overrun_size > data.len() {
970 return None;
971 }
972 offset += overrun_size;
973
974 Some((value, offset))
975 }
976
977 fn decode_structure_with_bitset_body(
978 &self,
979 data: &[u8],
980 desc: &StructureDesc,
981 bitset: &[u8],
982 ) -> Option<(DecodedValue, usize)> {
983 debug!(
985 "Bitset: {:02x?} (size={}), total_fields={}",
986 bitset,
987 bitset.len(),
988 count_structure_fields(desc)
989 );
990 debug!(
991 "Structure fields: {:?}",
992 desc.fields.iter().map(|f| &f.name).collect::<Vec<_>>()
993 );
994
995 let mut has_field_bits = false;
997 if !bitset.is_empty() {
998 for (i, b) in bitset.iter().enumerate() {
999 let mask = if i == 0 { *b & !0x01 } else { *b };
1000 if mask != 0 {
1001 has_field_bits = true;
1002 break;
1003 }
1004 }
1005 }
1006 if !has_field_bits && !bitset.is_empty() && (bitset[0] & 0x01) != 0 {
1007 if let Some((value, consumed)) = self.decode_structure(data, desc) {
1008 return Some((value, consumed));
1009 }
1010 }
1011
1012 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
1013 let mut offset = 0usize;
1014
1015 fn decode_with_bitset_recursive(
1016 decoder: &PvdDecoder,
1017 data: &[u8],
1018 offset: &mut usize,
1019 desc: &StructureDesc,
1020 bitset: &[u8],
1021 bit_offset: &mut usize,
1022 fields: &mut Vec<(String, DecodedValue)>,
1023 ) -> bool {
1024 for field in &desc.fields {
1025 let byte_idx = *bit_offset / 8;
1026 let bit_idx = *bit_offset % 8;
1027 let current_bit = *bit_offset;
1028 *bit_offset += 1;
1029
1030 let field_present = if byte_idx < bitset.len() {
1031 (bitset[byte_idx] & (1 << bit_idx)) != 0
1032 } else {
1033 false
1034 };
1035
1036 debug!(
1037 "Field '{}' at bit {}: present={}",
1038 field.name, current_bit, field_present
1039 );
1040
1041 if let FieldType::Structure(nested_desc) = &field.field_type {
1042 let child_start_bit = *bit_offset;
1043 let child_field_count = count_structure_fields(nested_desc);
1044
1045 let mut any_child_bits_set = false;
1046 for i in 0..child_field_count {
1047 let check_byte = (child_start_bit + i) / 8;
1048 let check_bit = (child_start_bit + i) % 8;
1049 if check_byte < bitset.len() && (bitset[check_byte] & (1 << check_bit)) != 0
1050 {
1051 any_child_bits_set = true;
1052 break;
1053 }
1054 }
1055
1056 debug!(
1057 "Nested structure '{}': parent_present={}, child_start_bit={}, child_count={}, any_child_bits_set={}",
1058 field.name,
1059 field_present,
1060 child_start_bit,
1061 child_field_count,
1062 any_child_bits_set
1063 );
1064
1065 if field_present && !any_child_bits_set {
1066 *bit_offset += child_field_count;
1067 if *offset < data.len() {
1068 if let Some((value, consumed)) =
1069 decoder.decode_structure(&data[*offset..], nested_desc)
1070 {
1071 debug!(
1072 "Decoded full nested structure '{}', consumed {} bytes",
1073 field.name, consumed
1074 );
1075 fields.push((field.name.clone(), value));
1076 *offset += consumed;
1077 } else {
1078 debug!("Failed to decode full nested structure '{}'", field.name);
1079 return false;
1080 }
1081 }
1082 } else if any_child_bits_set {
1083 let mut nested_fields: Vec<(String, DecodedValue)> = Vec::new();
1084 if !decode_with_bitset_recursive(
1085 decoder,
1086 data,
1087 offset,
1088 nested_desc,
1089 bitset,
1090 bit_offset,
1091 &mut nested_fields,
1092 ) {
1093 return false;
1094 }
1095 debug!(
1096 "Nested structure '{}' decoded {} fields",
1097 field.name,
1098 nested_fields.len()
1099 );
1100 if !nested_fields.is_empty() {
1101 fields
1102 .push((field.name.clone(), DecodedValue::Structure(nested_fields)));
1103 }
1104 } else {
1105 *bit_offset += child_field_count;
1106 }
1107 } else if field_present {
1108 if *offset >= data.len() {
1109 debug!(
1110 "Data exhausted at offset {} for field '{}'",
1111 *offset, field.name
1112 );
1113 return false;
1114 }
1115 if let Some((value, consumed)) =
1116 decoder.decode_value(&data[*offset..], &field.field_type)
1117 {
1118 fields.push((field.name.clone(), value));
1119 *offset += consumed;
1120 } else {
1121 return false;
1122 }
1123 }
1124 }
1125 true
1126 }
1127
1128 let mut bit_offset = 1;
1129 decode_with_bitset_recursive(
1130 self,
1131 data,
1132 &mut offset,
1133 desc,
1134 bitset,
1135 &mut bit_offset,
1136 &mut fields,
1137 );
1138 Some((DecodedValue::Structure(fields), offset))
1139 }
1140}
1141
1142fn count_structure_fields(desc: &StructureDesc) -> usize {
1144 let mut count = 0;
1145 for field in &desc.fields {
1146 count += 1;
1147 if let FieldType::Structure(nested) = &field.field_type {
1148 count += count_structure_fields(nested);
1149 }
1150 }
1151 count
1152}
1153
1154pub fn extract_subfield_desc(desc: &StructureDesc, path: &str) -> Option<StructureDesc> {
1159 if path.is_empty() {
1160 return Some(desc.clone());
1161 }
1162 let mut parts = path.splitn(2, '.');
1163 let head = parts.next()?;
1164 let tail = parts.next().unwrap_or("");
1165 for field in &desc.fields {
1166 if field.name == head {
1167 match &field.field_type {
1168 FieldType::Structure(nested) | FieldType::StructureArray(nested) => {
1169 return extract_subfield_desc(nested, tail);
1170 }
1171 _ => {
1172 if tail.is_empty() {
1173 return Some(StructureDesc {
1174 struct_id: None,
1175 fields: vec![field.clone()],
1176 });
1177 }
1178 return None;
1179 }
1180 }
1181 }
1182 }
1183 None
1184}
1185
1186pub fn format_structure_desc(desc: &StructureDesc) -> String {
1188 let mut parts = Vec::new();
1189 if let Some(ref id) = desc.struct_id {
1190 parts.push(id.clone());
1191 }
1192 for field in &desc.fields {
1193 parts.push(format!("{}:{}", field.name, field.field_type.type_name()));
1194 }
1195 parts.join(", ")
1196}
1197
1198pub fn format_structure_tree(desc: &StructureDesc) -> String {
1199 fn push_fields(out: &mut Vec<String>, fields: &[FieldDesc], indent: usize) {
1200 let prefix = " ".repeat(indent);
1201 for field in fields {
1202 match &field.field_type {
1203 FieldType::Structure(nested) => {
1204 out.push(format!("{}{}: structure", prefix, field.name));
1205 push_fields(out, &nested.fields, indent + 1);
1206 }
1207 FieldType::StructureArray(nested) => {
1208 out.push(format!("{}{}: structure[]", prefix, field.name));
1209 push_fields(out, &nested.fields, indent + 1);
1210 }
1211 FieldType::Union(variants) => {
1212 out.push(format!("{}{}: union", prefix, field.name));
1213 push_fields(out, variants, indent + 1);
1214 }
1215 FieldType::UnionArray(variants) => {
1216 out.push(format!("{}{}: union[]", prefix, field.name));
1217 push_fields(out, variants, indent + 1);
1218 }
1219 FieldType::BoundedString(bound) => {
1220 out.push(format!("{}{}: string<={}", prefix, field.name, bound));
1221 }
1222 _ => {
1223 out.push(format!(
1224 "{}{}: {}",
1225 prefix,
1226 field.name,
1227 field.field_type.type_name()
1228 ));
1229 }
1230 }
1231 }
1232 }
1233
1234 let mut lines = Vec::new();
1235 if let Some(id) = &desc.struct_id {
1236 lines.push(format!("struct {}", id));
1237 } else {
1238 lines.push("struct <anonymous>".to_string());
1239 }
1240 push_fields(&mut lines, &desc.fields, 0);
1241 lines.join("\n")
1242}
1243
1244pub fn extract_nt_scalar_value(decoded: &DecodedValue) -> Option<&DecodedValue> {
1246 if let DecodedValue::Structure(fields) = decoded {
1247 for (name, value) in fields {
1248 if name == "value" {
1249 return Some(value);
1250 }
1251 }
1252 }
1253 None
1254}
1255
1256pub fn format_compact_value(decoded: &DecodedValue) -> String {
1258 match decoded {
1259 DecodedValue::Structure(fields) => {
1260 if fields.is_empty() {
1261 return "{}".to_string();
1262 }
1263
1264 let mut parts = Vec::new();
1265
1266 for (name, val) in fields {
1267 let formatted = format_field_value_compact(name, val);
1268 if !formatted.is_empty() {
1269 parts.push(formatted);
1270 }
1271 }
1272
1273 parts.join(", ")
1274 }
1275 _ => format!("{}", decoded),
1276 }
1277}
1278
1279fn format_field_value_compact(name: &str, val: &DecodedValue) -> String {
1281 match val {
1282 DecodedValue::Structure(fields) => {
1283 match name {
1285 "alarm" => {
1286 let severity = fields.iter().find(|(n, _)| n == "severity");
1288 let message = fields.iter().find(|(n, _)| n == "message");
1289 let mut parts = Vec::new();
1290 if let Some((_, DecodedValue::Int32(s))) = severity {
1291 if *s != 0 {
1292 parts.push(format!("sev={}", s));
1293 }
1294 }
1295 if let Some((_, DecodedValue::String(m))) = message {
1296 if !m.is_empty() {
1297 parts.push(format!("\"{}\"", m));
1298 }
1299 }
1300 if parts.is_empty() {
1301 String::new() } else {
1303 format!("alarm={{{}}}", parts.join(", "))
1304 }
1305 }
1306 "timeStamp" => {
1307 let secs = fields.iter().find(|(n, _)| n == "secondsPastEpoch");
1309 if let Some((_, DecodedValue::Int64(s))) = secs {
1310 format!("ts={}", s)
1311 } else {
1312 String::new()
1313 }
1314 }
1315 "display" | "control" | "valueAlarm" => {
1316 String::new()
1318 }
1319 _ => {
1320 let nested: Vec<String> = fields
1322 .iter()
1323 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1324 .collect();
1325
1326 if nested.is_empty() {
1327 String::new()
1328 } else {
1329 format!("{}={{{}}}", name, nested.join(", "))
1330 }
1331 }
1332 }
1333 }
1334 _ => {
1335 format!("{}={}", name, format_scalar_value(val))
1336 }
1337 }
1338}
1339
1340fn format_scalar_value(val: &DecodedValue) -> String {
1342 match val {
1343 DecodedValue::Null => "null".to_string(),
1344 DecodedValue::Boolean(v) => format!("{}", v),
1345 DecodedValue::Int8(v) => format!("{}", v),
1346 DecodedValue::Int16(v) => format!("{}", v),
1347 DecodedValue::Int32(v) => format!("{}", v),
1348 DecodedValue::Int64(v) => format!("{}", v),
1349 DecodedValue::UInt8(v) => format!("{}", v),
1350 DecodedValue::UInt16(v) => format!("{}", v),
1351 DecodedValue::UInt32(v) => format!("{}", v),
1352 DecodedValue::UInt64(v) => format!("{}", v),
1353 DecodedValue::Float32(v) => format!("{:.4}", v),
1354 DecodedValue::Float64(v) => format!("{:.6}", v),
1355 DecodedValue::String(v) => format!("\"{}\"", v),
1356 DecodedValue::Array(arr) => {
1357 if arr.is_empty() {
1358 "[]".to_string()
1359 } else {
1360 let items: Vec<String> = arr.iter().map(|v| format_scalar_value(v)).collect();
1361 format!("[{}]", items.join(", "))
1362 }
1363 }
1364 DecodedValue::Structure(fields) => {
1365 let nested: Vec<String> = fields
1366 .iter()
1367 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1368 .collect();
1369 format!("{{{}}}", nested.join(", "))
1370 }
1371 DecodedValue::Raw(data) => {
1372 if data.len() <= 4 {
1373 format!("<{}>", hex::encode(data))
1374 } else {
1375 format!("<{}B>", data.len())
1376 }
1377 }
1378 }
1379}
1380
1381#[cfg(test)]
1382mod tests {
1383 use super::*;
1384
1385 #[test]
1386 fn test_decode_size() {
1387 let decoder = PvdDecoder::new(false);
1388
1389 assert_eq!(decoder.decode_size(&[5]), Some((5, 1)));
1391 assert_eq!(decoder.decode_size(&[253]), Some((253, 1)));
1392
1393 assert_eq!(
1395 decoder.decode_size(&[254, 0x00, 0x01, 0x00, 0x00]),
1396 Some((256, 5))
1397 );
1398 }
1399
1400 #[test]
1401 fn test_parse_introspection_full_with_id() {
1402 let decoder = PvdDecoder::new(false);
1403 let data = vec![
1404 0xFD, 0x06, 0x00, 0x80, 0x00, 0x01, 0x05, b'v', b'a', b'l', b'u', b'e', 0x43, ];
1412 let desc = decoder
1413 .parse_introspection(&data)
1414 .expect("parsed introspection");
1415 assert_eq!(desc.fields.len(), 1);
1416 assert_eq!(desc.fields[0].name, "value");
1417 match desc.fields[0].field_type {
1418 FieldType::Scalar(TypeCode::Float64) => {}
1419 _ => panic!("expected float64 value field"),
1420 }
1421 }
1422
1423 #[test]
1424 fn test_decode_string() {
1425 let decoder = PvdDecoder::new(false);
1426
1427 assert_eq!(decoder.decode_string(&[0]), Some((String::new(), 1)));
1429
1430 let data = [5, b'h', b'e', b'l', b'l', b'o'];
1432 assert_eq!(decoder.decode_string(&data), Some(("hello".to_string(), 6)));
1433 }
1434
1435 #[test]
1436 fn decode_variant_accepts_full_with_id_type_tag() {
1437 let decoder = PvdDecoder::new(false);
1438 let data = [0xFD, 0x02, 0x00, 0x60, 0x02, b'o', b'k'];
1440 let (value, consumed) = decoder
1441 .decode_value(&data, &FieldType::Variant)
1442 .expect("decode variant");
1443 assert_eq!(consumed, data.len());
1444 assert!(matches!(value, DecodedValue::String(ref s) if s == "ok"));
1445 }
1446
1447 #[test]
1448 fn test_decode_bitset_whole_structure() {
1449 let decoder = PvdDecoder::new(false);
1450 let desc = StructureDesc {
1451 struct_id: None,
1452 fields: vec![FieldDesc {
1453 name: "value".to_string(),
1454 field_type: FieldType::Scalar(TypeCode::Float64),
1455 }],
1456 };
1457 let mut data = Vec::new();
1459 data.push(0x01);
1460 data.push(0x01);
1461 data.extend_from_slice(&1.25f64.to_le_bytes());
1462
1463 let (decoded, _consumed) = decoder
1464 .decode_structure_with_bitset(&data, &desc)
1465 .expect("decoded");
1466 if let DecodedValue::Structure(fields) = decoded {
1467 assert_eq!(fields.len(), 1);
1468 assert_eq!(fields[0].0, "value");
1469 } else {
1470 panic!("expected structure");
1471 }
1472 }
1473
1474 #[test]
1475 fn format_structure_tree_includes_nested_fields() {
1476 let desc = StructureDesc {
1477 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1478 fields: vec![
1479 FieldDesc {
1480 name: "value".to_string(),
1481 field_type: FieldType::Scalar(TypeCode::Float64),
1482 },
1483 FieldDesc {
1484 name: "alarm".to_string(),
1485 field_type: FieldType::Structure(StructureDesc {
1486 struct_id: None,
1487 fields: vec![
1488 FieldDesc {
1489 name: "severity".to_string(),
1490 field_type: FieldType::Scalar(TypeCode::Int32),
1491 },
1492 FieldDesc {
1493 name: "message".to_string(),
1494 field_type: FieldType::String,
1495 },
1496 ],
1497 }),
1498 },
1499 ],
1500 };
1501
1502 let rendered = format_structure_tree(&desc);
1503 assert!(rendered.contains("struct epics:nt/NTScalar:1.0"));
1504 assert!(rendered.contains("value: double"));
1505 assert!(rendered.contains("alarm: structure"));
1506 assert!(rendered.contains("severity: int"));
1507 assert!(rendered.contains("message: string"));
1508 }
1509
1510 #[test]
1511 fn decode_string_array_not_capped_at_100_items() {
1512 fn encode_size(size: usize) -> Vec<u8> {
1513 if size == 0 {
1514 return vec![0x00];
1515 }
1516 if size < 254 {
1517 return vec![size as u8];
1518 }
1519 let mut out = vec![0xFE];
1520 out.extend_from_slice(&(size as u32).to_le_bytes());
1521 out
1522 }
1523
1524 let item_count = 150usize;
1525 let mut raw = encode_size(item_count);
1526 for idx in 0..item_count {
1527 let s = format!("PV:{}", idx);
1528 raw.extend_from_slice(&encode_size(s.len()));
1529 raw.extend_from_slice(s.as_bytes());
1530 }
1531
1532 let decoder = PvdDecoder::new(false);
1533 let (decoded, _consumed) = decoder
1534 .decode_value(&raw, &FieldType::StringArray)
1535 .expect("decoded");
1536
1537 let DecodedValue::Array(items) = decoded else {
1538 panic!("expected decoded array");
1539 };
1540 assert_eq!(items.len(), item_count);
1541 }
1542}