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 write!(f, "{}", v)?;
269 }
270 write!(f, "]")
271 }
272 DecodedValue::Structure(fields) => {
273 write!(f, "{{")?;
274 for (i, (name, val)) in fields.iter().enumerate() {
275 if i > 0 {
276 write!(f, ", ")?;
277 }
278 write!(f, "{}={}", name, val)?;
279 }
280 write!(f, "}}")
281 }
282 DecodedValue::Raw(data) => {
283 if data.len() <= 8 {
284 write!(f, "<{} bytes: {}>", data.len(), hex::encode(data))
285 } else {
286 write!(f, "<{} bytes>", data.len())
287 }
288 }
289 }
290 }
291}
292
293pub struct PvdDecoder {
295 is_be: bool,
296 registry: std::cell::RefCell<std::collections::HashMap<u16, FieldType>>,
299}
300
301impl PvdDecoder {
302 pub fn new(is_be: bool) -> Self {
303 Self {
304 is_be,
305 registry: std::cell::RefCell::new(std::collections::HashMap::new()),
306 }
307 }
308
309 pub fn decode_size(&self, data: &[u8]) -> Option<(usize, usize)> {
311 if data.is_empty() {
312 return None;
313 }
314 let first = data[0];
315 if first == 0xFF {
316 return Some((0, 1)); }
319 if first < 254 {
320 return Some((first as usize, 1));
321 }
322 if first == 254 {
323 if data.len() < 5 {
325 return None;
326 }
327 let size = if self.is_be {
328 u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize
329 } else {
330 u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize
331 };
332 return Some((size, 5));
333 }
334 None
336 }
337
338 pub fn decode_string(&self, data: &[u8]) -> Option<(String, usize)> {
340 let (size, size_bytes) = self.decode_size(data)?;
341 if size == 0 {
342 return Some((String::new(), size_bytes));
343 }
344 if data.len() < size_bytes + size {
345 return None;
346 }
347 let s = std::str::from_utf8(&data[size_bytes..size_bytes + size]).ok()?;
348 Some((s.to_string(), size_bytes + size))
349 }
350
351 pub fn parse_field_desc(&self, data: &[u8]) -> Option<(FieldDesc, usize)> {
353 if data.is_empty() {
354 return None;
355 }
356
357 let mut offset = 0;
358
359 let (name, consumed) = self.decode_string(&data[offset..])?;
361 offset += consumed;
362
363 if offset >= data.len() {
364 return None;
365 }
366
367 let (field_type, type_consumed) = self.parse_type_desc(&data[offset..])?;
369 offset += type_consumed;
370
371 Some((FieldDesc { name, field_type }, offset))
372 }
373
374 fn parse_type_desc(&self, data: &[u8]) -> Option<(FieldType, usize)> {
376 if data.is_empty() {
377 return None;
378 }
379
380 let type_byte = data[0];
381 let mut offset = 1;
382
383 if type_byte == 0xFF {
385 return Some((FieldType::Variant, 1));
386 }
387
388 if type_byte == 0xFD {
391 if data.len() < 3 {
392 return None;
393 }
394 let key = if self.is_be {
395 u16::from_be_bytes([data[1], data[2]])
396 } else {
397 u16::from_le_bytes([data[1], data[2]])
398 };
399 if let Some((field_type, consumed)) = self.parse_type_desc(&data[3..]) {
400 self.registry.borrow_mut().insert(key, field_type.clone());
401 return Some((field_type, 3 + consumed));
402 }
403 return None;
404 }
405
406 if type_byte == 0xFE {
409 if data.len() < 3 {
410 return None;
411 }
412 let key = if self.is_be {
413 u16::from_be_bytes([data[1], data[2]])
414 } else {
415 u16::from_le_bytes([data[1], data[2]])
416 };
417 if let Some(ft) = self.registry.borrow().get(&key) {
418 return Some((ft.clone(), 3));
419 }
420 debug!("Type descriptor ONLY_ID (0xFE) key={} not found in registry", key);
421 return None;
422 }
423
424 if type_byte == 0x80 || type_byte == 0x88 {
426 let is_array = (type_byte & 0x08) != 0;
427 if is_array {
428 if offset >= data.len() || data[offset] != 0x80 {
430 return None;
431 }
432 offset += 1;
433 }
434 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
435 offset += consumed;
436 if is_array {
437 return Some((FieldType::StructureArray(struct_desc), offset));
438 } else {
439 return Some((FieldType::Structure(struct_desc), offset));
440 }
441 }
442
443 if type_byte == 0x81 || type_byte == 0x89 {
445 let is_array = (type_byte & 0x08) != 0;
446 if is_array {
447 if offset >= data.len() || data[offset] != 0x81 {
449 return None;
450 }
451 offset += 1;
452 }
453 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
455 offset += consumed;
456 if is_array {
457 return Some((FieldType::UnionArray(struct_desc.fields), offset));
458 } else {
459 return Some((FieldType::Union(struct_desc.fields), offset));
460 }
461 }
462
463 if type_byte == 0x82 {
465 return Some((FieldType::Variant, 1));
466 }
467 if type_byte == 0x8A {
468 return Some((FieldType::VariantArray, 1));
469 }
470
471 if type_byte == 0x83 || type_byte == 0x86 {
473 let (bound, consumed) = self.decode_size(&data[offset..])?;
474 offset += consumed;
475 return Some((FieldType::BoundedString(bound as u32), offset));
476 }
477
478 let scalar_or_array = type_byte & 0x18;
481 let is_array = scalar_or_array != 0;
482 if is_array && scalar_or_array != 0x08 {
483 let (_bound, consumed) = self.decode_size(&data[offset..])?;
485 offset += consumed;
486 }
487 let base_type = type_byte & 0xE7;
488
489 if base_type == 0x60 {
491 if is_array {
492 return Some((FieldType::StringArray, offset));
493 } else {
494 return Some((FieldType::String, offset));
495 }
496 }
497
498 if let Some(tc) = TypeCode::from_byte(base_type) {
500 if is_array {
501 return Some((FieldType::ScalarArray(tc), offset));
502 } else {
503 return Some((FieldType::Scalar(tc), offset));
504 }
505 }
506
507 debug!("Unknown type byte: 0x{:02x}", type_byte);
508 None
509 }
510
511 fn parse_structure_desc(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
513 let mut offset = 0;
514
515 let (struct_id, consumed) = self.decode_string(&data[offset..])?;
517 offset += consumed;
518
519 let struct_id = if struct_id.is_empty() {
520 None
521 } else {
522 Some(struct_id)
523 };
524
525 let (field_count, consumed) = self.decode_size(&data[offset..])?;
527 offset += consumed;
528
529 let mut fields = Vec::with_capacity(field_count);
530
531 for _ in 0..field_count {
532 if offset >= data.len() {
533 break;
534 }
535 if let Some((field, consumed)) = self.parse_field_desc(&data[offset..]) {
536 offset += consumed;
537 fields.push(field);
538 } else {
539 break;
540 }
541 }
542
543 Some((StructureDesc { struct_id, fields }, offset))
544 }
545
546 pub fn parse_introspection(&self, data: &[u8]) -> Option<StructureDesc> {
548 self.parse_introspection_with_len(data)
549 .map(|(desc, _)| desc)
550 }
551
552 pub fn parse_introspection_with_len(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
554 if data.is_empty() {
555 return None;
556 }
557
558 let type_byte = data[0];
560
561 if type_byte == 0x80 {
563 let (desc, consumed) = self.parse_structure_desc(&data[1..])?;
564 return Some((desc, 1 + consumed));
565 }
566
567 if type_byte == 0xFD {
570 if data.len() < 3 {
571 return None;
572 }
573 let key = if self.is_be {
574 u16::from_be_bytes([data[1], data[2]])
575 } else {
576 u16::from_le_bytes([data[1], data[2]])
577 };
578 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[3..]) {
579 if !desc.fields.is_empty() {
581 self.registry.borrow_mut().insert(
582 key,
583 FieldType::Structure(desc.clone()),
584 );
585 } else {
586 self.registry.borrow_mut().insert(
587 key,
588 FieldType::Structure(desc.clone()),
589 );
590 }
591 return Some((desc, 3 + consumed));
592 }
593 return None;
594 }
595
596 if type_byte == 0xFE {
599 if data.len() < 3 {
600 return None;
601 }
602 let key = if self.is_be {
603 u16::from_be_bytes([data[1], data[2]])
604 } else {
605 u16::from_le_bytes([data[1], data[2]])
606 };
607 if let Some(ft) = self.registry.borrow().get(&key) {
608 if let FieldType::Structure(desc) = ft {
609 return Some((desc.clone(), 3));
610 }
611 }
612 debug!("Introspection ONLY_ID (0xFE) key={} not found in registry", key);
613 return None;
614 }
615
616 debug!("Unexpected introspection type byte: 0x{:02x}", type_byte);
617 None
618 }
619
620 fn decode_scalar(&self, data: &[u8], tc: TypeCode) -> Option<(DecodedValue, usize)> {
622 let size = tc.size()?;
623 if data.len() < size {
624 return None;
625 }
626
627 let value = match tc {
628 TypeCode::Boolean => DecodedValue::Boolean(data[0] != 0),
629 TypeCode::Int8 => DecodedValue::Int8(data[0] as i8),
630 TypeCode::UInt8 => DecodedValue::UInt8(data[0]),
631 TypeCode::Int16 => {
632 let v = if self.is_be {
633 i16::from_be_bytes([data[0], data[1]])
634 } else {
635 i16::from_le_bytes([data[0], data[1]])
636 };
637 DecodedValue::Int16(v)
638 }
639 TypeCode::UInt16 => {
640 let v = if self.is_be {
641 u16::from_be_bytes([data[0], data[1]])
642 } else {
643 u16::from_le_bytes([data[0], data[1]])
644 };
645 DecodedValue::UInt16(v)
646 }
647 TypeCode::Int32 => {
648 let v = if self.is_be {
649 i32::from_be_bytes(data[0..4].try_into().unwrap())
650 } else {
651 i32::from_le_bytes(data[0..4].try_into().unwrap())
652 };
653 DecodedValue::Int32(v)
654 }
655 TypeCode::UInt32 => {
656 let v = if self.is_be {
657 u32::from_be_bytes(data[0..4].try_into().unwrap())
658 } else {
659 u32::from_le_bytes(data[0..4].try_into().unwrap())
660 };
661 DecodedValue::UInt32(v)
662 }
663 TypeCode::Int64 => {
664 let v = if self.is_be {
665 i64::from_be_bytes(data[0..8].try_into().unwrap())
666 } else {
667 i64::from_le_bytes(data[0..8].try_into().unwrap())
668 };
669 DecodedValue::Int64(v)
670 }
671 TypeCode::UInt64 => {
672 let v = if self.is_be {
673 u64::from_be_bytes(data[0..8].try_into().unwrap())
674 } else {
675 u64::from_le_bytes(data[0..8].try_into().unwrap())
676 };
677 DecodedValue::UInt64(v)
678 }
679 TypeCode::Float32 => {
680 let v = if self.is_be {
681 f32::from_be_bytes(data[0..4].try_into().unwrap())
682 } else {
683 f32::from_le_bytes(data[0..4].try_into().unwrap())
684 };
685 DecodedValue::Float32(v)
686 }
687 TypeCode::Float64 => {
688 let v = if self.is_be {
689 f64::from_be_bytes(data[0..8].try_into().unwrap())
690 } else {
691 f64::from_le_bytes(data[0..8].try_into().unwrap())
692 };
693 DecodedValue::Float64(v)
694 }
695 _ => return None,
696 };
697
698 Some((value, size))
699 }
700
701 pub fn decode_value(
703 &self,
704 data: &[u8],
705 field_type: &FieldType,
706 ) -> Option<(DecodedValue, usize)> {
707 match field_type {
708 FieldType::Scalar(tc) => self.decode_scalar(data, *tc),
709 FieldType::String | FieldType::BoundedString(_) => {
710 let (s, consumed) = self.decode_string(data)?;
711 Some((DecodedValue::String(s), consumed))
712 }
713 FieldType::ScalarArray(tc) => {
714 let (count, size_consumed) = self.decode_size(data)?;
715 let mut offset = size_consumed;
716 let limit = count.min(4_000_000);
717 let mut values = Vec::with_capacity(limit);
718 let elem_size = tc.size().unwrap_or(1);
719 for _ in 0..limit {
720 if let Some((val, consumed)) = self.decode_scalar(&data[offset..], *tc) {
721 values.push(val);
722 offset += consumed;
723 } else {
724 break;
725 }
726 }
727 let remaining = count.saturating_sub(limit);
730 offset += remaining * elem_size;
731 Some((DecodedValue::Array(values), offset))
732 }
733 FieldType::StringArray => {
734 let (count, size_consumed) = self.decode_size(data)?;
735 let mut offset = size_consumed;
736 let max_items = count.min(4096);
737 let mut values = Vec::with_capacity(max_items);
738 for _ in 0..max_items {
739 if let Some((s, consumed)) = self.decode_string(&data[offset..]) {
740 values.push(DecodedValue::String(s));
741 offset += consumed;
742 } else {
743 break;
744 }
745 }
746 Some((DecodedValue::Array(values), offset))
747 }
748 FieldType::Structure(desc) => self.decode_structure(data, desc),
749 FieldType::StructureArray(desc) => {
750 let (count, size_consumed) = self.decode_size(data)?;
751 let mut offset = size_consumed;
752 let mut values = Vec::with_capacity(count.min(256));
753 for _ in 0..count.min(256) {
754 if offset >= data.len() {
756 return None;
757 }
758 let null_indicator = data[offset];
759 offset += 1;
760 if null_indicator == 0 {
761 values.push(DecodedValue::Structure(Vec::new()));
763 continue;
764 }
765 let (item, consumed) = self.decode_structure(&data[offset..], desc)?;
766 values.push(item);
767 offset += consumed;
768 }
769 Some((DecodedValue::Array(values), offset))
770 }
771 FieldType::Union(fields) => {
772 let (selector, consumed) = self.decode_size(data)?;
773 let field = fields.get(selector)?;
774 let (value, val_consumed) =
775 self.decode_value(&data[consumed..], &field.field_type)?;
776 Some((
777 DecodedValue::Structure(vec![(field.name.clone(), value)]),
778 consumed + val_consumed,
779 ))
780 }
781 FieldType::UnionArray(fields) => {
782 let (count, size_consumed) = self.decode_size(data)?;
783 let mut offset = size_consumed;
784 let mut values = Vec::with_capacity(count.min(128));
785 for _ in 0..count.min(128) {
786 let (selector, consumed) = self.decode_size(&data[offset..])?;
787 offset += consumed;
788 let field = fields.get(selector)?;
789 let (value, val_consumed) =
790 self.decode_value(&data[offset..], &field.field_type)?;
791 offset += val_consumed;
792 values.push(DecodedValue::Structure(vec![(field.name.clone(), value)]));
793 }
794 Some((DecodedValue::Array(values), offset))
795 }
796 FieldType::Variant => {
797 if data.is_empty() {
798 return None;
799 }
800 if data[0] == 0xFF {
801 return Some((DecodedValue::Null, 1));
802 }
803 let (variant_type, type_consumed) = self.parse_type_desc(data)?;
804 let (variant_value, value_consumed) =
805 self.decode_value(&data[type_consumed..], &variant_type)?;
806 Some((variant_value, type_consumed + value_consumed))
807 }
808 FieldType::VariantArray => {
809 let (count, size_consumed) = self.decode_size(data)?;
810 let mut offset = size_consumed;
811 let mut values = Vec::with_capacity(count.min(128));
812 for _ in 0..count.min(128) {
813 let (v, consumed) = self.decode_value(&data[offset..], &FieldType::Variant)?;
814 values.push(v);
815 offset += consumed;
816 }
817 Some((DecodedValue::Array(values), offset))
818 }
819 }
820 }
821
822 pub fn decode_structure(
824 &self,
825 data: &[u8],
826 desc: &StructureDesc,
827 ) -> Option<(DecodedValue, usize)> {
828 let mut offset = 0;
829 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
830
831 for field in &desc.fields {
832 if offset >= data.len() {
833 break;
834 }
835 if let Some((value, consumed)) = self.decode_value(&data[offset..], &field.field_type) {
836 fields.push((field.name.clone(), value));
837 offset += consumed;
838 } else {
839 break;
841 }
842 }
843
844 Some((DecodedValue::Structure(fields), offset))
845 }
846
847 pub fn decode_structure_with_bitset(
850 &self,
851 data: &[u8],
852 desc: &StructureDesc,
853 ) -> Option<(DecodedValue, usize)> {
854 if data.is_empty() {
855 return None;
856 }
857
858 let mut offset = 0;
859
860 let (bitset_size, size_consumed) = self.decode_size(data)?;
862 offset += size_consumed;
863
864 if bitset_size == 0 || offset + bitset_size > data.len() {
865 return Some((DecodedValue::Structure(vec![]), offset));
866 }
867
868 let bitset = &data[offset..offset + bitset_size];
869 offset += bitset_size;
870
871 let (value, consumed) =
872 self.decode_structure_with_bitset_body(&data[offset..], desc, bitset)?;
873 Some((value, offset + consumed))
874 }
875
876 pub fn decode_structure_with_bitset_and_overrun(
878 &self,
879 data: &[u8],
880 desc: &StructureDesc,
881 ) -> Option<(DecodedValue, usize)> {
882 if data.is_empty() {
883 return None;
884 }
885 let mut offset = 0usize;
886 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
887 offset += consumed1;
888 if offset + changed_size > data.len() {
889 return None;
890 }
891 let changed = &data[offset..offset + changed_size];
892 offset += changed_size;
893
894 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
895 offset += consumed2;
896 if offset + overrun_size > data.len() {
897 return None;
898 }
899 offset += overrun_size;
900
901 let (value, consumed) =
902 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
903 Some((value, offset + consumed))
904 }
905
906 pub fn decode_structure_with_bitset_then_overrun(
908 &self,
909 data: &[u8],
910 desc: &StructureDesc,
911 ) -> Option<(DecodedValue, usize)> {
912 if data.is_empty() {
913 return None;
914 }
915 let mut offset = 0usize;
916 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
917 offset += consumed1;
918 if offset + changed_size > data.len() {
919 return None;
920 }
921 let changed = &data[offset..offset + changed_size];
922 offset += changed_size;
923
924 let (value, consumed) =
925 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
926 offset += consumed;
927
928 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
929 offset += consumed2;
930 if offset + overrun_size > data.len() {
931 return None;
932 }
933 offset += overrun_size;
934
935 Some((value, offset))
936 }
937
938 fn decode_structure_with_bitset_body(
939 &self,
940 data: &[u8],
941 desc: &StructureDesc,
942 bitset: &[u8],
943 ) -> Option<(DecodedValue, usize)> {
944 debug!(
946 "Bitset: {:02x?} (size={}), total_fields={}",
947 bitset,
948 bitset.len(),
949 count_structure_fields(desc)
950 );
951 debug!(
952 "Structure fields: {:?}",
953 desc.fields.iter().map(|f| &f.name).collect::<Vec<_>>()
954 );
955
956 let mut has_field_bits = false;
958 if !bitset.is_empty() {
959 for (i, b) in bitset.iter().enumerate() {
960 let mask = if i == 0 { *b & !0x01 } else { *b };
961 if mask != 0 {
962 has_field_bits = true;
963 break;
964 }
965 }
966 }
967 if !has_field_bits && !bitset.is_empty() && (bitset[0] & 0x01) != 0 {
968 if let Some((value, consumed)) = self.decode_structure(data, desc) {
969 return Some((value, consumed));
970 }
971 }
972
973 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
974 let mut offset = 0usize;
975
976 fn decode_with_bitset_recursive(
977 decoder: &PvdDecoder,
978 data: &[u8],
979 offset: &mut usize,
980 desc: &StructureDesc,
981 bitset: &[u8],
982 bit_offset: &mut usize,
983 fields: &mut Vec<(String, DecodedValue)>,
984 ) -> bool {
985 for field in &desc.fields {
986 let byte_idx = *bit_offset / 8;
987 let bit_idx = *bit_offset % 8;
988 let current_bit = *bit_offset;
989 *bit_offset += 1;
990
991 let field_present = if byte_idx < bitset.len() {
992 (bitset[byte_idx] & (1 << bit_idx)) != 0
993 } else {
994 false
995 };
996
997 debug!(
998 "Field '{}' at bit {}: present={}",
999 field.name, current_bit, field_present
1000 );
1001
1002 if let FieldType::Structure(nested_desc) = &field.field_type {
1003 let child_start_bit = *bit_offset;
1004 let child_field_count = count_structure_fields(nested_desc);
1005
1006 let mut any_child_bits_set = false;
1007 for i in 0..child_field_count {
1008 let check_byte = (child_start_bit + i) / 8;
1009 let check_bit = (child_start_bit + i) % 8;
1010 if check_byte < bitset.len() && (bitset[check_byte] & (1 << check_bit)) != 0
1011 {
1012 any_child_bits_set = true;
1013 break;
1014 }
1015 }
1016
1017 debug!(
1018 "Nested structure '{}': parent_present={}, child_start_bit={}, child_count={}, any_child_bits_set={}",
1019 field.name,
1020 field_present,
1021 child_start_bit,
1022 child_field_count,
1023 any_child_bits_set
1024 );
1025
1026 if field_present && !any_child_bits_set {
1027 *bit_offset += child_field_count;
1028 if *offset < data.len() {
1029 if let Some((value, consumed)) =
1030 decoder.decode_structure(&data[*offset..], nested_desc)
1031 {
1032 debug!(
1033 "Decoded full nested structure '{}', consumed {} bytes",
1034 field.name, consumed
1035 );
1036 fields.push((field.name.clone(), value));
1037 *offset += consumed;
1038 } else {
1039 debug!("Failed to decode full nested structure '{}'", field.name);
1040 return false;
1041 }
1042 }
1043 } else if any_child_bits_set {
1044 let mut nested_fields: Vec<(String, DecodedValue)> = Vec::new();
1045 if !decode_with_bitset_recursive(
1046 decoder,
1047 data,
1048 offset,
1049 nested_desc,
1050 bitset,
1051 bit_offset,
1052 &mut nested_fields,
1053 ) {
1054 return false;
1055 }
1056 debug!(
1057 "Nested structure '{}' decoded {} fields",
1058 field.name,
1059 nested_fields.len()
1060 );
1061 if !nested_fields.is_empty() {
1062 fields
1063 .push((field.name.clone(), DecodedValue::Structure(nested_fields)));
1064 }
1065 } else {
1066 *bit_offset += child_field_count;
1067 }
1068 } else if field_present {
1069 if *offset >= data.len() {
1070 debug!(
1071 "Data exhausted at offset {} for field '{}'",
1072 *offset, field.name
1073 );
1074 return false;
1075 }
1076 if let Some((value, consumed)) =
1077 decoder.decode_value(&data[*offset..], &field.field_type)
1078 {
1079 fields.push((field.name.clone(), value));
1080 *offset += consumed;
1081 } else {
1082 return false;
1083 }
1084 }
1085 }
1086 true
1087 }
1088
1089 let mut bit_offset = 1;
1090 decode_with_bitset_recursive(
1091 self,
1092 data,
1093 &mut offset,
1094 desc,
1095 bitset,
1096 &mut bit_offset,
1097 &mut fields,
1098 );
1099 Some((DecodedValue::Structure(fields), offset))
1100 }
1101}
1102
1103fn count_structure_fields(desc: &StructureDesc) -> usize {
1105 let mut count = 0;
1106 for field in &desc.fields {
1107 count += 1;
1108 if let FieldType::Structure(nested) = &field.field_type {
1109 count += count_structure_fields(nested);
1110 }
1111 }
1112 count
1113}
1114
1115pub fn extract_subfield_desc(desc: &StructureDesc, path: &str) -> Option<StructureDesc> {
1120 if path.is_empty() {
1121 return Some(desc.clone());
1122 }
1123 let mut parts = path.splitn(2, '.');
1124 let head = parts.next()?;
1125 let tail = parts.next().unwrap_or("");
1126 for field in &desc.fields {
1127 if field.name == head {
1128 match &field.field_type {
1129 FieldType::Structure(nested) | FieldType::StructureArray(nested) => {
1130 return extract_subfield_desc(nested, tail);
1131 }
1132 _ => {
1133 if tail.is_empty() {
1134 return Some(StructureDesc {
1135 struct_id: None,
1136 fields: vec![field.clone()],
1137 });
1138 }
1139 return None;
1140 }
1141 }
1142 }
1143 }
1144 None
1145}
1146
1147pub fn format_structure_desc(desc: &StructureDesc) -> String {
1149 let mut parts = Vec::new();
1150 if let Some(ref id) = desc.struct_id {
1151 parts.push(id.clone());
1152 }
1153 for field in &desc.fields {
1154 parts.push(format!("{}:{}", field.name, field.field_type.type_name()));
1155 }
1156 parts.join(", ")
1157}
1158
1159pub fn format_structure_tree(desc: &StructureDesc) -> String {
1160 fn push_fields(out: &mut Vec<String>, fields: &[FieldDesc], indent: usize) {
1161 let prefix = " ".repeat(indent);
1162 for field in fields {
1163 match &field.field_type {
1164 FieldType::Structure(nested) => {
1165 out.push(format!("{}{}: structure", prefix, field.name));
1166 push_fields(out, &nested.fields, indent + 1);
1167 }
1168 FieldType::StructureArray(nested) => {
1169 out.push(format!("{}{}: structure[]", prefix, field.name));
1170 push_fields(out, &nested.fields, indent + 1);
1171 }
1172 FieldType::Union(variants) => {
1173 out.push(format!("{}{}: union", prefix, field.name));
1174 push_fields(out, variants, indent + 1);
1175 }
1176 FieldType::UnionArray(variants) => {
1177 out.push(format!("{}{}: union[]", prefix, field.name));
1178 push_fields(out, variants, indent + 1);
1179 }
1180 FieldType::BoundedString(bound) => {
1181 out.push(format!("{}{}: string<={}", prefix, field.name, bound));
1182 }
1183 _ => {
1184 out.push(format!(
1185 "{}{}: {}",
1186 prefix,
1187 field.name,
1188 field.field_type.type_name()
1189 ));
1190 }
1191 }
1192 }
1193 }
1194
1195 let mut lines = Vec::new();
1196 if let Some(id) = &desc.struct_id {
1197 lines.push(format!("struct {}", id));
1198 } else {
1199 lines.push("struct <anonymous>".to_string());
1200 }
1201 push_fields(&mut lines, &desc.fields, 0);
1202 lines.join("\n")
1203}
1204
1205pub fn extract_nt_scalar_value(decoded: &DecodedValue) -> Option<&DecodedValue> {
1207 if let DecodedValue::Structure(fields) = decoded {
1208 for (name, value) in fields {
1209 if name == "value" {
1210 return Some(value);
1211 }
1212 }
1213 }
1214 None
1215}
1216
1217pub fn format_compact_value(decoded: &DecodedValue) -> String {
1219 match decoded {
1220 DecodedValue::Structure(fields) => {
1221 if fields.is_empty() {
1222 return "{}".to_string();
1223 }
1224
1225 let mut parts = Vec::new();
1226
1227 for (name, val) in fields {
1228 let formatted = format_field_value_compact(name, val);
1229 if !formatted.is_empty() {
1230 parts.push(formatted);
1231 }
1232 }
1233
1234 parts.join(", ")
1235 }
1236 _ => format!("{}", decoded),
1237 }
1238}
1239
1240fn format_field_value_compact(name: &str, val: &DecodedValue) -> String {
1242 match val {
1243 DecodedValue::Structure(fields) => {
1244 match name {
1246 "alarm" => {
1247 let severity = fields.iter().find(|(n, _)| n == "severity");
1249 let message = fields.iter().find(|(n, _)| n == "message");
1250 let mut parts = Vec::new();
1251 if let Some((_, DecodedValue::Int32(s))) = severity {
1252 if *s != 0 {
1253 parts.push(format!("sev={}", s));
1254 }
1255 }
1256 if let Some((_, DecodedValue::String(m))) = message {
1257 if !m.is_empty() {
1258 parts.push(format!("\"{}\"", m));
1259 }
1260 }
1261 if parts.is_empty() {
1262 String::new() } else {
1264 format!("alarm={{{}}}", parts.join(", "))
1265 }
1266 }
1267 "timeStamp" => {
1268 let secs = fields.iter().find(|(n, _)| n == "secondsPastEpoch");
1270 if let Some((_, DecodedValue::Int64(s))) = secs {
1271 format!("ts={}", s)
1272 } else {
1273 String::new()
1274 }
1275 }
1276 "display" | "control" | "valueAlarm" => {
1277 String::new()
1279 }
1280 _ => {
1281 let nested: Vec<String> = fields
1283 .iter()
1284 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1285 .collect();
1286
1287 if nested.is_empty() {
1288 String::new()
1289 } else {
1290 format!("{}={{{}}}", name, nested.join(", "))
1291 }
1292 }
1293 }
1294 }
1295 _ => {
1296 format!("{}={}", name, format_scalar_value(val))
1297 }
1298 }
1299}
1300
1301fn format_scalar_value(val: &DecodedValue) -> String {
1303 match val {
1304 DecodedValue::Null => "null".to_string(),
1305 DecodedValue::Boolean(v) => format!("{}", v),
1306 DecodedValue::Int8(v) => format!("{}", v),
1307 DecodedValue::Int16(v) => format!("{}", v),
1308 DecodedValue::Int32(v) => format!("{}", v),
1309 DecodedValue::Int64(v) => format!("{}", v),
1310 DecodedValue::UInt8(v) => format!("{}", v),
1311 DecodedValue::UInt16(v) => format!("{}", v),
1312 DecodedValue::UInt32(v) => format!("{}", v),
1313 DecodedValue::UInt64(v) => format!("{}", v),
1314 DecodedValue::Float32(v) => format!("{:.4}", v),
1315 DecodedValue::Float64(v) => format!("{:.6}", v),
1316 DecodedValue::String(v) => format!("\"{}\"", v),
1317 DecodedValue::Array(arr) => {
1318 if arr.is_empty() {
1319 "[]".to_string()
1320 } else {
1321 let items: Vec<String> = arr.iter().map(|v| format_scalar_value(v)).collect();
1322 format!("[{}]", items.join(", "))
1323 }
1324 }
1325 DecodedValue::Structure(fields) => {
1326 let nested: Vec<String> = fields
1327 .iter()
1328 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1329 .collect();
1330 format!("{{{}}}", nested.join(", "))
1331 }
1332 DecodedValue::Raw(data) => {
1333 if data.len() <= 4 {
1334 format!("<{}>", hex::encode(data))
1335 } else {
1336 format!("<{}B>", data.len())
1337 }
1338 }
1339 }
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344 use super::*;
1345
1346 #[test]
1347 fn test_decode_size() {
1348 let decoder = PvdDecoder::new(false);
1349
1350 assert_eq!(decoder.decode_size(&[5]), Some((5, 1)));
1352 assert_eq!(decoder.decode_size(&[253]), Some((253, 1)));
1353
1354 assert_eq!(
1356 decoder.decode_size(&[254, 0x00, 0x01, 0x00, 0x00]),
1357 Some((256, 5))
1358 );
1359 }
1360
1361 #[test]
1362 fn test_parse_introspection_full_with_id() {
1363 let decoder = PvdDecoder::new(false);
1364 let data = vec![
1365 0xFD, 0x06, 0x00, 0x80, 0x00, 0x01, 0x05, b'v', b'a', b'l', b'u', b'e', 0x43, ];
1373 let desc = decoder
1374 .parse_introspection(&data)
1375 .expect("parsed introspection");
1376 assert_eq!(desc.fields.len(), 1);
1377 assert_eq!(desc.fields[0].name, "value");
1378 match desc.fields[0].field_type {
1379 FieldType::Scalar(TypeCode::Float64) => {}
1380 _ => panic!("expected float64 value field"),
1381 }
1382 }
1383
1384 #[test]
1385 fn test_decode_string() {
1386 let decoder = PvdDecoder::new(false);
1387
1388 assert_eq!(decoder.decode_string(&[0]), Some((String::new(), 1)));
1390
1391 let data = [5, b'h', b'e', b'l', b'l', b'o'];
1393 assert_eq!(decoder.decode_string(&data), Some(("hello".to_string(), 6)));
1394 }
1395
1396 #[test]
1397 fn decode_variant_accepts_full_with_id_type_tag() {
1398 let decoder = PvdDecoder::new(false);
1399 let data = [0xFD, 0x02, 0x00, 0x60, 0x02, b'o', b'k'];
1401 let (value, consumed) = decoder
1402 .decode_value(&data, &FieldType::Variant)
1403 .expect("decode variant");
1404 assert_eq!(consumed, data.len());
1405 assert!(matches!(value, DecodedValue::String(ref s) if s == "ok"));
1406 }
1407
1408 #[test]
1409 fn test_decode_bitset_whole_structure() {
1410 let decoder = PvdDecoder::new(false);
1411 let desc = StructureDesc {
1412 struct_id: None,
1413 fields: vec![FieldDesc {
1414 name: "value".to_string(),
1415 field_type: FieldType::Scalar(TypeCode::Float64),
1416 }],
1417 };
1418 let mut data = Vec::new();
1420 data.push(0x01);
1421 data.push(0x01);
1422 data.extend_from_slice(&1.25f64.to_le_bytes());
1423
1424 let (decoded, _consumed) = decoder
1425 .decode_structure_with_bitset(&data, &desc)
1426 .expect("decoded");
1427 if let DecodedValue::Structure(fields) = decoded {
1428 assert_eq!(fields.len(), 1);
1429 assert_eq!(fields[0].0, "value");
1430 } else {
1431 panic!("expected structure");
1432 }
1433 }
1434
1435 #[test]
1436 fn format_structure_tree_includes_nested_fields() {
1437 let desc = StructureDesc {
1438 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1439 fields: vec![
1440 FieldDesc {
1441 name: "value".to_string(),
1442 field_type: FieldType::Scalar(TypeCode::Float64),
1443 },
1444 FieldDesc {
1445 name: "alarm".to_string(),
1446 field_type: FieldType::Structure(StructureDesc {
1447 struct_id: None,
1448 fields: vec![
1449 FieldDesc {
1450 name: "severity".to_string(),
1451 field_type: FieldType::Scalar(TypeCode::Int32),
1452 },
1453 FieldDesc {
1454 name: "message".to_string(),
1455 field_type: FieldType::String,
1456 },
1457 ],
1458 }),
1459 },
1460 ],
1461 };
1462
1463 let rendered = format_structure_tree(&desc);
1464 assert!(rendered.contains("struct epics:nt/NTScalar:1.0"));
1465 assert!(rendered.contains("value: double"));
1466 assert!(rendered.contains("alarm: structure"));
1467 assert!(rendered.contains("severity: int"));
1468 assert!(rendered.contains("message: string"));
1469 }
1470
1471 #[test]
1472 fn decode_string_array_not_capped_at_100_items() {
1473 fn encode_size(size: usize) -> Vec<u8> {
1474 if size == 0 {
1475 return vec![0x00];
1476 }
1477 if size < 254 {
1478 return vec![size as u8];
1479 }
1480 let mut out = vec![0xFE];
1481 out.extend_from_slice(&(size as u32).to_le_bytes());
1482 out
1483 }
1484
1485 let item_count = 150usize;
1486 let mut raw = encode_size(item_count);
1487 for idx in 0..item_count {
1488 let s = format!("PV:{}", idx);
1489 raw.extend_from_slice(&encode_size(s.len()));
1490 raw.extend_from_slice(s.as_bytes());
1491 }
1492
1493 let decoder = PvdDecoder::new(false);
1494 let (decoded, _consumed) = decoder
1495 .decode_value(&raw, &FieldType::StringArray)
1496 .expect("decoded");
1497
1498 let DecodedValue::Array(items) = decoded else {
1499 panic!("expected decoded array");
1500 };
1501 assert_eq!(items.len(), item_count);
1502 }
1503}