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
120#[derive(Debug, Clone)]
122pub struct FieldDesc {
123 pub name: String,
124 pub field_type: FieldType,
125}
126
127#[derive(Debug, Clone)]
129pub struct StructureDesc {
130 pub struct_id: Option<String>,
131 pub fields: Vec<FieldDesc>,
132}
133
134impl StructureDesc {
135 pub fn new() -> Self {
136 Self {
137 struct_id: None,
138 fields: Vec::new(),
139 }
140 }
141
142 pub fn field(&self, name: &str) -> Option<&FieldDesc> {
144 self.fields.iter().find(|f| f.name == name)
145 }
146}
147
148impl Default for StructureDesc {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154impl fmt::Display for StructureDesc {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 fn write_indent(f: &mut fmt::Formatter<'_>, depth: usize) -> fmt::Result {
157 for _ in 0..depth {
158 write!(f, " ")?;
159 }
160 Ok(())
161 }
162
163 fn write_field_type(
164 f: &mut fmt::Formatter<'_>,
165 ft: &FieldType,
166 depth: usize,
167 ) -> fmt::Result {
168 match ft {
169 FieldType::Structure(desc) => write_structure(f, desc, depth),
170 FieldType::StructureArray(desc) => {
171 write_structure(f, desc, depth)?;
172 write!(f, "[]")
173 }
174 FieldType::Union(fields) => {
175 writeln!(f, "union")?;
176 for field in fields {
177 write_indent(f, depth + 1)?;
178 write!(f, "{} ", field.name)?;
179 write_field_type(f, &field.field_type, depth + 1)?;
180 writeln!(f)?;
181 }
182 Ok(())
183 }
184 FieldType::UnionArray(fields) => {
185 writeln!(f, "union[]")?;
186 for field in fields {
187 write_indent(f, depth + 1)?;
188 write!(f, "{} ", field.name)?;
189 write_field_type(f, &field.field_type, depth + 1)?;
190 writeln!(f)?;
191 }
192 Ok(())
193 }
194 other => write!(f, "{}", other.type_name()),
195 }
196 }
197
198 fn write_structure(
199 f: &mut fmt::Formatter<'_>,
200 desc: &StructureDesc,
201 depth: usize,
202 ) -> fmt::Result {
203 if let Some(id) = &desc.struct_id {
204 write!(f, "structure «{}»", id)?;
205 } else {
206 write!(f, "structure")?;
207 }
208 if desc.fields.is_empty() {
209 return Ok(());
210 }
211 writeln!(f)?;
212 for field in &desc.fields {
213 write_indent(f, depth + 1)?;
214 write!(f, "{} ", field.name)?;
215 write_field_type(f, &field.field_type, depth + 1)?;
216 writeln!(f)?;
217 }
218 Ok(())
219 }
220
221 write_structure(f, self, 0)
222 }
223}
224
225#[derive(Debug, Clone)]
227pub enum DecodedValue {
228 Null,
229 Boolean(bool),
230 Int8(i8),
231 Int16(i16),
232 Int32(i32),
233 Int64(i64),
234 UInt8(u8),
235 UInt16(u16),
236 UInt32(u32),
237 UInt64(u64),
238 Float32(f32),
239 Float64(f64),
240 String(String),
241 Array(Vec<DecodedValue>),
242 Structure(Vec<(String, DecodedValue)>),
243 Raw(Vec<u8>),
244}
245
246impl fmt::Display for DecodedValue {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 match self {
249 DecodedValue::Null => write!(f, "null"),
250 DecodedValue::Boolean(v) => write!(f, "{}", v),
251 DecodedValue::Int8(v) => write!(f, "{}", v),
252 DecodedValue::Int16(v) => write!(f, "{}", v),
253 DecodedValue::Int32(v) => write!(f, "{}", v),
254 DecodedValue::Int64(v) => write!(f, "{}", v),
255 DecodedValue::UInt8(v) => write!(f, "{}", v),
256 DecodedValue::UInt16(v) => write!(f, "{}", v),
257 DecodedValue::UInt32(v) => write!(f, "{}", v),
258 DecodedValue::UInt64(v) => write!(f, "{}", v),
259 DecodedValue::Float32(v) => write!(f, "{:.6}", v),
260 DecodedValue::Float64(v) => write!(f, "{:.6}", v),
261 DecodedValue::String(v) => write!(f, "\"{}\"", v),
262 DecodedValue::Array(arr) => {
263 write!(f, "[")?;
264 for (i, v) in arr.iter().enumerate() {
265 if i > 0 {
266 write!(f, ", ")?;
267 }
268 if i >= 5 {
269 write!(f, "... ({} total)", arr.len())?;
270 break;
271 }
272 write!(f, "{}", v)?;
273 }
274 write!(f, "]")
275 }
276 DecodedValue::Structure(fields) => {
277 write!(f, "{{")?;
278 for (i, (name, val)) in fields.iter().enumerate() {
279 if i > 0 {
280 write!(f, ", ")?;
281 }
282 write!(f, "{}={}", name, val)?;
283 }
284 write!(f, "}}")
285 }
286 DecodedValue::Raw(data) => {
287 if data.len() <= 8 {
288 write!(f, "<{} bytes: {}>", data.len(), hex::encode(data))
289 } else {
290 write!(f, "<{} bytes>", data.len())
291 }
292 }
293 }
294 }
295}
296
297pub struct PvdDecoder {
299 is_be: bool,
300}
301
302impl PvdDecoder {
303 pub fn new(is_be: bool) -> Self {
304 Self { is_be }
305 }
306
307 pub fn decode_size(&self, data: &[u8]) -> Option<(usize, usize)> {
309 if data.is_empty() {
310 return None;
311 }
312 let first = data[0];
313 if first == 0xFF {
314 return Some((0, 1)); }
317 if first < 254 {
318 return Some((first as usize, 1));
319 }
320 if first == 254 {
321 if data.len() < 5 {
323 return None;
324 }
325 let size = if self.is_be {
326 u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize
327 } else {
328 u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize
329 };
330 return Some((size, 5));
331 }
332 None
334 }
335
336 pub fn decode_string(&self, data: &[u8]) -> Option<(String, usize)> {
338 let (size, size_bytes) = self.decode_size(data)?;
339 if size == 0 {
340 return Some((String::new(), size_bytes));
341 }
342 if data.len() < size_bytes + size {
343 return None;
344 }
345 let s = std::str::from_utf8(&data[size_bytes..size_bytes + size]).ok()?;
346 Some((s.to_string(), size_bytes + size))
347 }
348
349 pub fn parse_field_desc(&self, data: &[u8]) -> Option<(FieldDesc, usize)> {
351 if data.is_empty() {
352 return None;
353 }
354
355 let mut offset = 0;
356
357 let (name, consumed) = self.decode_string(&data[offset..])?;
359 offset += consumed;
360
361 if offset >= data.len() {
362 return None;
363 }
364
365 let (field_type, type_consumed) = self.parse_type_desc(&data[offset..])?;
367 offset += type_consumed;
368
369 Some((FieldDesc { name, field_type }, offset))
370 }
371
372 fn parse_type_desc(&self, data: &[u8]) -> Option<(FieldType, usize)> {
374 if data.is_empty() {
375 return None;
376 }
377
378 let type_byte = data[0];
379 let mut offset = 1;
380
381 if type_byte == 0xFF {
383 return Some((FieldType::Variant, 1));
384 }
385
386 if type_byte == 0xFD {
389 if data.len() < 4 {
390 return None;
391 }
392 if let Some((field_type, consumed)) = self.parse_type_desc(&data[3..]) {
394 return Some((field_type, 3 + consumed));
395 }
396 if let Some((field_type, consumed)) = self.parse_type_desc(&data[1..]) {
398 return Some((field_type, 1 + consumed));
399 }
400 return None;
401 }
402
403 if type_byte == 0xFE {
406 debug!("Type descriptor uses ONLY_ID (0xFE) without registry context");
407 return None;
408 }
409
410 if type_byte == 0x80 || type_byte == 0x88 {
412 let is_array = (type_byte & 0x08) != 0;
413 if is_array {
414 if offset >= data.len() || data[offset] != 0x80 {
416 return None;
417 }
418 offset += 1;
419 }
420 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
421 offset += consumed;
422 if is_array {
423 return Some((FieldType::StructureArray(struct_desc), offset));
424 } else {
425 return Some((FieldType::Structure(struct_desc), offset));
426 }
427 }
428
429 if type_byte == 0x81 || type_byte == 0x89 {
431 let is_array = (type_byte & 0x08) != 0;
432 if is_array {
433 if offset >= data.len() || data[offset] != 0x81 {
435 return None;
436 }
437 offset += 1;
438 }
439 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
441 offset += consumed;
442 if is_array {
443 return Some((FieldType::UnionArray(struct_desc.fields), offset));
444 } else {
445 return Some((FieldType::Union(struct_desc.fields), offset));
446 }
447 }
448
449 if type_byte == 0x82 {
451 return Some((FieldType::Variant, 1));
452 }
453 if type_byte == 0x8A {
454 return Some((FieldType::VariantArray, 1));
455 }
456
457 if type_byte == 0x83 || type_byte == 0x86 {
459 let (bound, consumed) = self.decode_size(&data[offset..])?;
460 offset += consumed;
461 return Some((FieldType::BoundedString(bound as u32), offset));
462 }
463
464 let scalar_or_array = type_byte & 0x18;
467 let is_array = scalar_or_array != 0;
468 if is_array && scalar_or_array != 0x08 {
469 let (_bound, consumed) = self.decode_size(&data[offset..])?;
471 offset += consumed;
472 }
473 let base_type = type_byte & 0xE7;
474
475 if base_type == 0x60 {
477 if is_array {
478 return Some((FieldType::StringArray, offset));
479 } else {
480 return Some((FieldType::String, offset));
481 }
482 }
483
484 if let Some(tc) = TypeCode::from_byte(base_type) {
486 if is_array {
487 return Some((FieldType::ScalarArray(tc), offset));
488 } else {
489 return Some((FieldType::Scalar(tc), offset));
490 }
491 }
492
493 debug!("Unknown type byte: 0x{:02x}", type_byte);
494 None
495 }
496
497 fn parse_structure_desc(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
499 let mut offset = 0;
500
501 let (struct_id, consumed) = self.decode_string(&data[offset..])?;
503 offset += consumed;
504
505 let struct_id = if struct_id.is_empty() {
506 None
507 } else {
508 Some(struct_id)
509 };
510
511 let (field_count, consumed) = self.decode_size(&data[offset..])?;
513 offset += consumed;
514
515 let mut fields = Vec::with_capacity(field_count);
516
517 for _ in 0..field_count {
518 if offset >= data.len() {
519 break;
520 }
521 if let Some((field, consumed)) = self.parse_field_desc(&data[offset..]) {
522 offset += consumed;
523 fields.push(field);
524 } else {
525 break;
526 }
527 }
528
529 Some((StructureDesc { struct_id, fields }, offset))
530 }
531
532 pub fn parse_introspection(&self, data: &[u8]) -> Option<StructureDesc> {
534 self.parse_introspection_with_len(data)
535 .map(|(desc, _)| desc)
536 }
537
538 pub fn parse_introspection_with_len(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
540 if data.is_empty() {
541 return None;
542 }
543
544 let type_byte = data[0];
546
547 if type_byte == 0x80 {
549 let (desc, consumed) = self.parse_structure_desc(&data[1..])?;
550 return Some((desc, 1 + consumed));
551 }
552
553 if type_byte == 0xFD {
556 if data.len() < 4 {
557 return None;
558 }
559 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[3..]) {
561 return Some((desc, 3 + consumed));
562 }
563 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[1..]) {
565 return Some((desc, 1 + consumed));
566 }
567 return None;
568 }
569
570 if type_byte == 0xFE {
573 debug!(
574 "Introspection uses ONLY_ID (0xFE), but no registry is available in this decoder"
575 );
576 return None;
577 }
578
579 debug!("Unexpected introspection type byte: 0x{:02x}", type_byte);
580 None
581 }
582
583 fn decode_scalar(&self, data: &[u8], tc: TypeCode) -> Option<(DecodedValue, usize)> {
585 let size = tc.size()?;
586 if data.len() < size {
587 return None;
588 }
589
590 let value = match tc {
591 TypeCode::Boolean => DecodedValue::Boolean(data[0] != 0),
592 TypeCode::Int8 => DecodedValue::Int8(data[0] as i8),
593 TypeCode::UInt8 => DecodedValue::UInt8(data[0]),
594 TypeCode::Int16 => {
595 let v = if self.is_be {
596 i16::from_be_bytes([data[0], data[1]])
597 } else {
598 i16::from_le_bytes([data[0], data[1]])
599 };
600 DecodedValue::Int16(v)
601 }
602 TypeCode::UInt16 => {
603 let v = if self.is_be {
604 u16::from_be_bytes([data[0], data[1]])
605 } else {
606 u16::from_le_bytes([data[0], data[1]])
607 };
608 DecodedValue::UInt16(v)
609 }
610 TypeCode::Int32 => {
611 let v = if self.is_be {
612 i32::from_be_bytes(data[0..4].try_into().unwrap())
613 } else {
614 i32::from_le_bytes(data[0..4].try_into().unwrap())
615 };
616 DecodedValue::Int32(v)
617 }
618 TypeCode::UInt32 => {
619 let v = if self.is_be {
620 u32::from_be_bytes(data[0..4].try_into().unwrap())
621 } else {
622 u32::from_le_bytes(data[0..4].try_into().unwrap())
623 };
624 DecodedValue::UInt32(v)
625 }
626 TypeCode::Int64 => {
627 let v = if self.is_be {
628 i64::from_be_bytes(data[0..8].try_into().unwrap())
629 } else {
630 i64::from_le_bytes(data[0..8].try_into().unwrap())
631 };
632 DecodedValue::Int64(v)
633 }
634 TypeCode::UInt64 => {
635 let v = if self.is_be {
636 u64::from_be_bytes(data[0..8].try_into().unwrap())
637 } else {
638 u64::from_le_bytes(data[0..8].try_into().unwrap())
639 };
640 DecodedValue::UInt64(v)
641 }
642 TypeCode::Float32 => {
643 let v = if self.is_be {
644 f32::from_be_bytes(data[0..4].try_into().unwrap())
645 } else {
646 f32::from_le_bytes(data[0..4].try_into().unwrap())
647 };
648 DecodedValue::Float32(v)
649 }
650 TypeCode::Float64 => {
651 let v = if self.is_be {
652 f64::from_be_bytes(data[0..8].try_into().unwrap())
653 } else {
654 f64::from_le_bytes(data[0..8].try_into().unwrap())
655 };
656 DecodedValue::Float64(v)
657 }
658 _ => return None,
659 };
660
661 Some((value, size))
662 }
663
664 pub fn decode_value(
666 &self,
667 data: &[u8],
668 field_type: &FieldType,
669 ) -> Option<(DecodedValue, usize)> {
670 match field_type {
671 FieldType::Scalar(tc) => self.decode_scalar(data, *tc),
672 FieldType::String | FieldType::BoundedString(_) => {
673 let (s, consumed) = self.decode_string(data)?;
674 Some((DecodedValue::String(s), consumed))
675 }
676 FieldType::ScalarArray(tc) => {
677 let (count, size_consumed) = self.decode_size(data)?;
678 let mut offset = size_consumed;
679 let limit = count.min(4_000_000);
680 let mut values = Vec::with_capacity(limit);
681 let elem_size = tc.size().unwrap_or(1);
682 for _ in 0..limit {
683 if let Some((val, consumed)) = self.decode_scalar(&data[offset..], *tc) {
684 values.push(val);
685 offset += consumed;
686 } else {
687 break;
688 }
689 }
690 let remaining = count.saturating_sub(limit);
693 offset += remaining * elem_size;
694 Some((DecodedValue::Array(values), offset))
695 }
696 FieldType::StringArray => {
697 let (count, size_consumed) = self.decode_size(data)?;
698 let mut offset = size_consumed;
699 let max_items = count.min(4096);
700 let mut values = Vec::with_capacity(max_items);
701 for _ in 0..max_items {
702 if let Some((s, consumed)) = self.decode_string(&data[offset..]) {
703 values.push(DecodedValue::String(s));
704 offset += consumed;
705 } else {
706 break;
707 }
708 }
709 Some((DecodedValue::Array(values), offset))
710 }
711 FieldType::Structure(desc) => self.decode_structure(data, desc),
712 FieldType::StructureArray(desc) => {
713 let (count, size_consumed) = self.decode_size(data)?;
714 let mut offset = size_consumed;
715 let mut values = Vec::with_capacity(count.min(256));
716 for _ in 0..count.min(256) {
717 if offset >= data.len() {
719 return None;
720 }
721 let null_indicator = data[offset];
722 offset += 1;
723 if null_indicator == 0 {
724 values.push(DecodedValue::Structure(Vec::new()));
726 continue;
727 }
728 let (item, consumed) = self.decode_structure(&data[offset..], desc)?;
729 values.push(item);
730 offset += consumed;
731 }
732 Some((DecodedValue::Array(values), offset))
733 }
734 FieldType::Union(fields) => {
735 let (selector, consumed) = self.decode_size(data)?;
736 let field = fields.get(selector)?;
737 let (value, val_consumed) =
738 self.decode_value(&data[consumed..], &field.field_type)?;
739 Some((
740 DecodedValue::Structure(vec![(field.name.clone(), value)]),
741 consumed + val_consumed,
742 ))
743 }
744 FieldType::UnionArray(fields) => {
745 let (count, size_consumed) = self.decode_size(data)?;
746 let mut offset = size_consumed;
747 let mut values = Vec::with_capacity(count.min(128));
748 for _ in 0..count.min(128) {
749 let (selector, consumed) = self.decode_size(&data[offset..])?;
750 offset += consumed;
751 let field = fields.get(selector)?;
752 let (value, val_consumed) =
753 self.decode_value(&data[offset..], &field.field_type)?;
754 offset += val_consumed;
755 values.push(DecodedValue::Structure(vec![(field.name.clone(), value)]));
756 }
757 Some((DecodedValue::Array(values), offset))
758 }
759 FieldType::Variant => {
760 if data.is_empty() {
761 return None;
762 }
763 if data[0] == 0xFF {
764 return Some((DecodedValue::Null, 1));
765 }
766 let (variant_type, type_consumed) = self.parse_type_desc(data)?;
767 let (variant_value, value_consumed) =
768 self.decode_value(&data[type_consumed..], &variant_type)?;
769 Some((variant_value, type_consumed + value_consumed))
770 }
771 FieldType::VariantArray => {
772 let (count, size_consumed) = self.decode_size(data)?;
773 let mut offset = size_consumed;
774 let mut values = Vec::with_capacity(count.min(128));
775 for _ in 0..count.min(128) {
776 let (v, consumed) = self.decode_value(&data[offset..], &FieldType::Variant)?;
777 values.push(v);
778 offset += consumed;
779 }
780 Some((DecodedValue::Array(values), offset))
781 }
782 }
783 }
784
785 pub fn decode_structure(
787 &self,
788 data: &[u8],
789 desc: &StructureDesc,
790 ) -> Option<(DecodedValue, usize)> {
791 let mut offset = 0;
792 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
793
794 for field in &desc.fields {
795 if offset >= data.len() {
796 break;
797 }
798 if let Some((value, consumed)) = self.decode_value(&data[offset..], &field.field_type) {
799 fields.push((field.name.clone(), value));
800 offset += consumed;
801 } else {
802 break;
804 }
805 }
806
807 Some((DecodedValue::Structure(fields), offset))
808 }
809
810 pub fn decode_structure_with_bitset(
813 &self,
814 data: &[u8],
815 desc: &StructureDesc,
816 ) -> Option<(DecodedValue, usize)> {
817 if data.is_empty() {
818 return None;
819 }
820
821 let mut offset = 0;
822
823 let (bitset_size, size_consumed) = self.decode_size(data)?;
825 offset += size_consumed;
826
827 if bitset_size == 0 || offset + bitset_size > data.len() {
828 return Some((DecodedValue::Structure(vec![]), offset));
829 }
830
831 let bitset = &data[offset..offset + bitset_size];
832 offset += bitset_size;
833
834 let (value, consumed) =
835 self.decode_structure_with_bitset_body(&data[offset..], desc, bitset)?;
836 Some((value, offset + consumed))
837 }
838
839 pub fn decode_structure_with_bitset_and_overrun(
841 &self,
842 data: &[u8],
843 desc: &StructureDesc,
844 ) -> Option<(DecodedValue, usize)> {
845 if data.is_empty() {
846 return None;
847 }
848 let mut offset = 0usize;
849 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
850 offset += consumed1;
851 if offset + changed_size > data.len() {
852 return None;
853 }
854 let changed = &data[offset..offset + changed_size];
855 offset += changed_size;
856
857 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
858 offset += consumed2;
859 if offset + overrun_size > data.len() {
860 return None;
861 }
862 offset += overrun_size;
863
864 let (value, consumed) =
865 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
866 Some((value, offset + consumed))
867 }
868
869 pub fn decode_structure_with_bitset_then_overrun(
871 &self,
872 data: &[u8],
873 desc: &StructureDesc,
874 ) -> Option<(DecodedValue, usize)> {
875 if data.is_empty() {
876 return None;
877 }
878 let mut offset = 0usize;
879 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
880 offset += consumed1;
881 if offset + changed_size > data.len() {
882 return None;
883 }
884 let changed = &data[offset..offset + changed_size];
885 offset += changed_size;
886
887 let (value, consumed) =
888 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
889 offset += consumed;
890
891 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
892 offset += consumed2;
893 if offset + overrun_size > data.len() {
894 return None;
895 }
896 offset += overrun_size;
897
898 Some((value, offset))
899 }
900
901 fn decode_structure_with_bitset_body(
902 &self,
903 data: &[u8],
904 desc: &StructureDesc,
905 bitset: &[u8],
906 ) -> Option<(DecodedValue, usize)> {
907 debug!(
909 "Bitset: {:02x?} (size={}), total_fields={}",
910 bitset,
911 bitset.len(),
912 count_structure_fields(desc)
913 );
914 debug!(
915 "Structure fields: {:?}",
916 desc.fields.iter().map(|f| &f.name).collect::<Vec<_>>()
917 );
918
919 let mut has_field_bits = false;
921 if !bitset.is_empty() {
922 for (i, b) in bitset.iter().enumerate() {
923 let mask = if i == 0 { *b & !0x01 } else { *b };
924 if mask != 0 {
925 has_field_bits = true;
926 break;
927 }
928 }
929 }
930 if !has_field_bits && !bitset.is_empty() && (bitset[0] & 0x01) != 0 {
931 if let Some((value, consumed)) = self.decode_structure(data, desc) {
932 return Some((value, consumed));
933 }
934 }
935
936 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
937 let mut offset = 0usize;
938
939 fn decode_with_bitset_recursive(
940 decoder: &PvdDecoder,
941 data: &[u8],
942 offset: &mut usize,
943 desc: &StructureDesc,
944 bitset: &[u8],
945 bit_offset: &mut usize,
946 fields: &mut Vec<(String, DecodedValue)>,
947 ) -> bool {
948 for field in &desc.fields {
949 let byte_idx = *bit_offset / 8;
950 let bit_idx = *bit_offset % 8;
951 let current_bit = *bit_offset;
952 *bit_offset += 1;
953
954 let field_present = if byte_idx < bitset.len() {
955 (bitset[byte_idx] & (1 << bit_idx)) != 0
956 } else {
957 false
958 };
959
960 debug!(
961 "Field '{}' at bit {}: present={}",
962 field.name, current_bit, field_present
963 );
964
965 if let FieldType::Structure(nested_desc) = &field.field_type {
966 let child_start_bit = *bit_offset;
967 let child_field_count = count_structure_fields(nested_desc);
968
969 let mut any_child_bits_set = false;
970 for i in 0..child_field_count {
971 let check_byte = (child_start_bit + i) / 8;
972 let check_bit = (child_start_bit + i) % 8;
973 if check_byte < bitset.len() && (bitset[check_byte] & (1 << check_bit)) != 0
974 {
975 any_child_bits_set = true;
976 break;
977 }
978 }
979
980 debug!(
981 "Nested structure '{}': parent_present={}, child_start_bit={}, child_count={}, any_child_bits_set={}",
982 field.name, field_present, child_start_bit, child_field_count, any_child_bits_set
983 );
984
985 if field_present && !any_child_bits_set {
986 *bit_offset += child_field_count;
987 if *offset < data.len() {
988 if let Some((value, consumed)) =
989 decoder.decode_structure(&data[*offset..], nested_desc)
990 {
991 debug!(
992 "Decoded full nested structure '{}', consumed {} bytes",
993 field.name, consumed
994 );
995 fields.push((field.name.clone(), value));
996 *offset += consumed;
997 } else {
998 debug!("Failed to decode full nested structure '{}'", field.name);
999 return false;
1000 }
1001 }
1002 } else if any_child_bits_set {
1003 let mut nested_fields: Vec<(String, DecodedValue)> = Vec::new();
1004 if !decode_with_bitset_recursive(
1005 decoder,
1006 data,
1007 offset,
1008 nested_desc,
1009 bitset,
1010 bit_offset,
1011 &mut nested_fields,
1012 ) {
1013 return false;
1014 }
1015 debug!(
1016 "Nested structure '{}' decoded {} fields",
1017 field.name,
1018 nested_fields.len()
1019 );
1020 if !nested_fields.is_empty() {
1021 fields
1022 .push((field.name.clone(), DecodedValue::Structure(nested_fields)));
1023 }
1024 } else {
1025 *bit_offset += child_field_count;
1026 }
1027 } else if field_present {
1028 if *offset >= data.len() {
1029 debug!(
1030 "Data exhausted at offset {} for field '{}'",
1031 *offset, field.name
1032 );
1033 return false;
1034 }
1035 if let Some((value, consumed)) =
1036 decoder.decode_value(&data[*offset..], &field.field_type)
1037 {
1038 fields.push((field.name.clone(), value));
1039 *offset += consumed;
1040 } else {
1041 return false;
1042 }
1043 }
1044 }
1045 true
1046 }
1047
1048 let mut bit_offset = 1;
1049 decode_with_bitset_recursive(
1050 self,
1051 data,
1052 &mut offset,
1053 desc,
1054 bitset,
1055 &mut bit_offset,
1056 &mut fields,
1057 );
1058 Some((DecodedValue::Structure(fields), offset))
1059 }
1060}
1061
1062fn count_structure_fields(desc: &StructureDesc) -> usize {
1064 let mut count = 0;
1065 for field in &desc.fields {
1066 count += 1;
1067 if let FieldType::Structure(nested) = &field.field_type {
1068 count += count_structure_fields(nested);
1069 }
1070 }
1071 count
1072}
1073
1074pub fn extract_subfield_desc(desc: &StructureDesc, path: &str) -> Option<StructureDesc> {
1079 if path.is_empty() {
1080 return Some(desc.clone());
1081 }
1082 let mut parts = path.splitn(2, '.');
1083 let head = parts.next()?;
1084 let tail = parts.next().unwrap_or("");
1085 for field in &desc.fields {
1086 if field.name == head {
1087 match &field.field_type {
1088 FieldType::Structure(nested) | FieldType::StructureArray(nested) => {
1089 return extract_subfield_desc(nested, tail);
1090 }
1091 _ => {
1092 if tail.is_empty() {
1093 return Some(StructureDesc {
1094 struct_id: None,
1095 fields: vec![field.clone()],
1096 });
1097 }
1098 return None;
1099 }
1100 }
1101 }
1102 }
1103 None
1104}
1105
1106pub fn format_structure_desc(desc: &StructureDesc) -> String {
1108 let mut parts = Vec::new();
1109 if let Some(ref id) = desc.struct_id {
1110 parts.push(id.clone());
1111 }
1112 for field in &desc.fields {
1113 parts.push(format!("{}:{}", field.name, field.field_type.type_name()));
1114 }
1115 parts.join(", ")
1116}
1117
1118pub fn format_structure_tree(desc: &StructureDesc) -> String {
1119 fn push_fields(out: &mut Vec<String>, fields: &[FieldDesc], indent: usize) {
1120 let prefix = " ".repeat(indent);
1121 for field in fields {
1122 match &field.field_type {
1123 FieldType::Structure(nested) => {
1124 out.push(format!("{}{}: structure", prefix, field.name));
1125 push_fields(out, &nested.fields, indent + 1);
1126 }
1127 FieldType::StructureArray(nested) => {
1128 out.push(format!("{}{}: structure[]", prefix, field.name));
1129 push_fields(out, &nested.fields, indent + 1);
1130 }
1131 FieldType::Union(variants) => {
1132 out.push(format!("{}{}: union", prefix, field.name));
1133 push_fields(out, variants, indent + 1);
1134 }
1135 FieldType::UnionArray(variants) => {
1136 out.push(format!("{}{}: union[]", prefix, field.name));
1137 push_fields(out, variants, indent + 1);
1138 }
1139 FieldType::BoundedString(bound) => {
1140 out.push(format!("{}{}: string<={}", prefix, field.name, bound));
1141 }
1142 _ => {
1143 out.push(format!(
1144 "{}{}: {}",
1145 prefix,
1146 field.name,
1147 field.field_type.type_name()
1148 ));
1149 }
1150 }
1151 }
1152 }
1153
1154 let mut lines = Vec::new();
1155 if let Some(id) = &desc.struct_id {
1156 lines.push(format!("struct {}", id));
1157 } else {
1158 lines.push("struct <anonymous>".to_string());
1159 }
1160 push_fields(&mut lines, &desc.fields, 0);
1161 lines.join("\n")
1162}
1163
1164pub fn extract_nt_scalar_value(decoded: &DecodedValue) -> Option<&DecodedValue> {
1166 if let DecodedValue::Structure(fields) = decoded {
1167 for (name, value) in fields {
1168 if name == "value" {
1169 return Some(value);
1170 }
1171 }
1172 }
1173 None
1174}
1175
1176pub fn format_compact_value(decoded: &DecodedValue) -> String {
1178 match decoded {
1179 DecodedValue::Structure(fields) => {
1180 if fields.is_empty() {
1181 return "{}".to_string();
1182 }
1183
1184 let mut parts = Vec::new();
1185
1186 for (name, val) in fields {
1187 let formatted = format_field_value_compact(name, val);
1188 if !formatted.is_empty() {
1189 parts.push(formatted);
1190 }
1191 }
1192
1193 parts.join(", ")
1194 }
1195 _ => format!("{}", decoded),
1196 }
1197}
1198
1199fn format_field_value_compact(name: &str, val: &DecodedValue) -> String {
1201 match val {
1202 DecodedValue::Structure(fields) => {
1203 match name {
1205 "alarm" => {
1206 let severity = fields.iter().find(|(n, _)| n == "severity");
1208 let message = fields.iter().find(|(n, _)| n == "message");
1209 let mut parts = Vec::new();
1210 if let Some((_, DecodedValue::Int32(s))) = severity {
1211 if *s != 0 {
1212 parts.push(format!("sev={}", s));
1213 }
1214 }
1215 if let Some((_, DecodedValue::String(m))) = message {
1216 if !m.is_empty() {
1217 parts.push(format!("\"{}\"", m));
1218 }
1219 }
1220 if parts.is_empty() {
1221 String::new() } else {
1223 format!("alarm={{{}}}", parts.join(", "))
1224 }
1225 }
1226 "timeStamp" => {
1227 let secs = fields.iter().find(|(n, _)| n == "secondsPastEpoch");
1229 if let Some((_, DecodedValue::Int64(s))) = secs {
1230 format!("ts={}", s)
1231 } else {
1232 String::new()
1233 }
1234 }
1235 "display" | "control" | "valueAlarm" => {
1236 String::new()
1238 }
1239 _ => {
1240 let nested: Vec<String> = fields
1242 .iter()
1243 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1244 .collect();
1245
1246 if nested.is_empty() {
1247 String::new()
1248 } else {
1249 format!("{}={{{}}}", name, nested.join(", "))
1250 }
1251 }
1252 }
1253 }
1254 _ => {
1255 format!("{}={}", name, format_scalar_value(val))
1256 }
1257 }
1258}
1259
1260fn format_scalar_value(val: &DecodedValue) -> String {
1262 match val {
1263 DecodedValue::Null => "null".to_string(),
1264 DecodedValue::Boolean(v) => format!("{}", v),
1265 DecodedValue::Int8(v) => format!("{}", v),
1266 DecodedValue::Int16(v) => format!("{}", v),
1267 DecodedValue::Int32(v) => format!("{}", v),
1268 DecodedValue::Int64(v) => format!("{}", v),
1269 DecodedValue::UInt8(v) => format!("{}", v),
1270 DecodedValue::UInt16(v) => format!("{}", v),
1271 DecodedValue::UInt32(v) => format!("{}", v),
1272 DecodedValue::UInt64(v) => format!("{}", v),
1273 DecodedValue::Float32(v) => format!("{:.4}", v),
1274 DecodedValue::Float64(v) => format!("{:.6}", v),
1275 DecodedValue::String(v) => format!("\"{}\"", v),
1276 DecodedValue::Array(arr) => {
1277 if arr.is_empty() {
1278 "[]".to_string()
1279 } else if arr.len() <= 3 {
1280 let items: Vec<String> = arr.iter().map(|v| format_scalar_value(v)).collect();
1281 format!("[{}]", items.join(", "))
1282 } else {
1283 format!("[{} items]", arr.len())
1284 }
1285 }
1286 DecodedValue::Structure(fields) => {
1287 let nested: Vec<String> = fields
1288 .iter()
1289 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1290 .collect();
1291 format!("{{{}}}", nested.join(", "))
1292 }
1293 DecodedValue::Raw(data) => {
1294 if data.len() <= 4 {
1295 format!("<{}>", hex::encode(data))
1296 } else {
1297 format!("<{}B>", data.len())
1298 }
1299 }
1300 }
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305 use super::*;
1306
1307 #[test]
1308 fn test_decode_size() {
1309 let decoder = PvdDecoder::new(false);
1310
1311 assert_eq!(decoder.decode_size(&[5]), Some((5, 1)));
1313 assert_eq!(decoder.decode_size(&[253]), Some((253, 1)));
1314
1315 assert_eq!(
1317 decoder.decode_size(&[254, 0x00, 0x01, 0x00, 0x00]),
1318 Some((256, 5))
1319 );
1320 }
1321
1322 #[test]
1323 fn test_parse_introspection_full_with_id() {
1324 let decoder = PvdDecoder::new(false);
1325 let data = vec![
1326 0xFD, 0x06, 0x00, 0x80, 0x00, 0x01, 0x05, b'v', b'a', b'l', b'u', b'e', 0x43, ];
1334 let desc = decoder
1335 .parse_introspection(&data)
1336 .expect("parsed introspection");
1337 assert_eq!(desc.fields.len(), 1);
1338 assert_eq!(desc.fields[0].name, "value");
1339 match desc.fields[0].field_type {
1340 FieldType::Scalar(TypeCode::Float64) => {}
1341 _ => panic!("expected float64 value field"),
1342 }
1343 }
1344
1345 #[test]
1346 fn test_decode_string() {
1347 let decoder = PvdDecoder::new(false);
1348
1349 assert_eq!(decoder.decode_string(&[0]), Some((String::new(), 1)));
1351
1352 let data = [5, b'h', b'e', b'l', b'l', b'o'];
1354 assert_eq!(decoder.decode_string(&data), Some(("hello".to_string(), 6)));
1355 }
1356
1357 #[test]
1358 fn decode_variant_accepts_full_with_id_type_tag() {
1359 let decoder = PvdDecoder::new(false);
1360 let data = [0xFD, 0x02, 0x00, 0x60, 0x02, b'o', b'k'];
1362 let (value, consumed) = decoder
1363 .decode_value(&data, &FieldType::Variant)
1364 .expect("decode variant");
1365 assert_eq!(consumed, data.len());
1366 assert!(matches!(value, DecodedValue::String(ref s) if s == "ok"));
1367 }
1368
1369 #[test]
1370 fn test_decode_bitset_whole_structure() {
1371 let decoder = PvdDecoder::new(false);
1372 let desc = StructureDesc {
1373 struct_id: None,
1374 fields: vec![FieldDesc {
1375 name: "value".to_string(),
1376 field_type: FieldType::Scalar(TypeCode::Float64),
1377 }],
1378 };
1379 let mut data = Vec::new();
1381 data.push(0x01);
1382 data.push(0x01);
1383 data.extend_from_slice(&1.25f64.to_le_bytes());
1384
1385 let (decoded, _consumed) = decoder
1386 .decode_structure_with_bitset(&data, &desc)
1387 .expect("decoded");
1388 if let DecodedValue::Structure(fields) = decoded {
1389 assert_eq!(fields.len(), 1);
1390 assert_eq!(fields[0].0, "value");
1391 } else {
1392 panic!("expected structure");
1393 }
1394 }
1395
1396 #[test]
1397 fn format_structure_tree_includes_nested_fields() {
1398 let desc = StructureDesc {
1399 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1400 fields: vec![
1401 FieldDesc {
1402 name: "value".to_string(),
1403 field_type: FieldType::Scalar(TypeCode::Float64),
1404 },
1405 FieldDesc {
1406 name: "alarm".to_string(),
1407 field_type: FieldType::Structure(StructureDesc {
1408 struct_id: None,
1409 fields: vec![
1410 FieldDesc {
1411 name: "severity".to_string(),
1412 field_type: FieldType::Scalar(TypeCode::Int32),
1413 },
1414 FieldDesc {
1415 name: "message".to_string(),
1416 field_type: FieldType::String,
1417 },
1418 ],
1419 }),
1420 },
1421 ],
1422 };
1423
1424 let rendered = format_structure_tree(&desc);
1425 assert!(rendered.contains("struct epics:nt/NTScalar:1.0"));
1426 assert!(rendered.contains("value: double"));
1427 assert!(rendered.contains("alarm: structure"));
1428 assert!(rendered.contains("severity: int"));
1429 assert!(rendered.contains("message: string"));
1430 }
1431
1432 #[test]
1433 fn decode_string_array_not_capped_at_100_items() {
1434 fn encode_size(size: usize) -> Vec<u8> {
1435 if size == 0 {
1436 return vec![0x00];
1437 }
1438 if size < 254 {
1439 return vec![size as u8];
1440 }
1441 let mut out = vec![0xFE];
1442 out.extend_from_slice(&(size as u32).to_le_bytes());
1443 out
1444 }
1445
1446 let item_count = 150usize;
1447 let mut raw = encode_size(item_count);
1448 for idx in 0..item_count {
1449 let s = format!("PV:{}", idx);
1450 raw.extend_from_slice(&encode_size(s.len()));
1451 raw.extend_from_slice(s.as_bytes());
1452 }
1453
1454 let decoder = PvdDecoder::new(false);
1455 let (decoded, _consumed) = decoder
1456 .decode_value(&raw, &FieldType::StringArray)
1457 .expect("decoded");
1458
1459 let DecodedValue::Array(items) = decoded else {
1460 panic!("expected decoded array");
1461 };
1462 assert_eq!(items.len(), item_count);
1463 }
1464}