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