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