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}
297
298impl PvdDecoder {
299 pub fn new(is_be: bool) -> Self {
300 Self { is_be }
301 }
302
303 pub fn decode_size(&self, data: &[u8]) -> Option<(usize, usize)> {
305 if data.is_empty() {
306 return None;
307 }
308 let first = data[0];
309 if first == 0xFF {
310 return Some((0, 1)); }
313 if first < 254 {
314 return Some((first as usize, 1));
315 }
316 if first == 254 {
317 if data.len() < 5 {
319 return None;
320 }
321 let size = if self.is_be {
322 u32::from_be_bytes([data[1], data[2], data[3], data[4]]) as usize
323 } else {
324 u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize
325 };
326 return Some((size, 5));
327 }
328 None
330 }
331
332 pub fn decode_string(&self, data: &[u8]) -> Option<(String, usize)> {
334 let (size, size_bytes) = self.decode_size(data)?;
335 if size == 0 {
336 return Some((String::new(), size_bytes));
337 }
338 if data.len() < size_bytes + size {
339 return None;
340 }
341 let s = std::str::from_utf8(&data[size_bytes..size_bytes + size]).ok()?;
342 Some((s.to_string(), size_bytes + size))
343 }
344
345 pub fn parse_field_desc(&self, data: &[u8]) -> Option<(FieldDesc, usize)> {
347 if data.is_empty() {
348 return None;
349 }
350
351 let mut offset = 0;
352
353 let (name, consumed) = self.decode_string(&data[offset..])?;
355 offset += consumed;
356
357 if offset >= data.len() {
358 return None;
359 }
360
361 let (field_type, type_consumed) = self.parse_type_desc(&data[offset..])?;
363 offset += type_consumed;
364
365 Some((FieldDesc { name, field_type }, offset))
366 }
367
368 fn parse_type_desc(&self, data: &[u8]) -> Option<(FieldType, usize)> {
370 if data.is_empty() {
371 return None;
372 }
373
374 let type_byte = data[0];
375 let mut offset = 1;
376
377 if type_byte == 0xFF {
379 return Some((FieldType::Variant, 1));
380 }
381
382 if type_byte == 0xFD {
385 if data.len() < 4 {
386 return None;
387 }
388 if let Some((field_type, consumed)) = self.parse_type_desc(&data[3..]) {
390 return Some((field_type, 3 + consumed));
391 }
392 if let Some((field_type, consumed)) = self.parse_type_desc(&data[1..]) {
394 return Some((field_type, 1 + consumed));
395 }
396 return None;
397 }
398
399 if type_byte == 0xFE {
402 debug!("Type descriptor uses ONLY_ID (0xFE) without registry context");
403 return None;
404 }
405
406 if type_byte == 0x80 || type_byte == 0x88 {
408 let is_array = (type_byte & 0x08) != 0;
409 if is_array {
410 if offset >= data.len() || data[offset] != 0x80 {
412 return None;
413 }
414 offset += 1;
415 }
416 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
417 offset += consumed;
418 if is_array {
419 return Some((FieldType::StructureArray(struct_desc), offset));
420 } else {
421 return Some((FieldType::Structure(struct_desc), offset));
422 }
423 }
424
425 if type_byte == 0x81 || type_byte == 0x89 {
427 let is_array = (type_byte & 0x08) != 0;
428 if is_array {
429 if offset >= data.len() || data[offset] != 0x81 {
431 return None;
432 }
433 offset += 1;
434 }
435 let (struct_desc, consumed) = self.parse_structure_desc(&data[offset..])?;
437 offset += consumed;
438 if is_array {
439 return Some((FieldType::UnionArray(struct_desc.fields), offset));
440 } else {
441 return Some((FieldType::Union(struct_desc.fields), offset));
442 }
443 }
444
445 if type_byte == 0x82 {
447 return Some((FieldType::Variant, 1));
448 }
449 if type_byte == 0x8A {
450 return Some((FieldType::VariantArray, 1));
451 }
452
453 if type_byte == 0x83 || type_byte == 0x86 {
455 let (bound, consumed) = self.decode_size(&data[offset..])?;
456 offset += consumed;
457 return Some((FieldType::BoundedString(bound as u32), offset));
458 }
459
460 let scalar_or_array = type_byte & 0x18;
463 let is_array = scalar_or_array != 0;
464 if is_array && scalar_or_array != 0x08 {
465 let (_bound, consumed) = self.decode_size(&data[offset..])?;
467 offset += consumed;
468 }
469 let base_type = type_byte & 0xE7;
470
471 if base_type == 0x60 {
473 if is_array {
474 return Some((FieldType::StringArray, offset));
475 } else {
476 return Some((FieldType::String, offset));
477 }
478 }
479
480 if let Some(tc) = TypeCode::from_byte(base_type) {
482 if is_array {
483 return Some((FieldType::ScalarArray(tc), offset));
484 } else {
485 return Some((FieldType::Scalar(tc), offset));
486 }
487 }
488
489 debug!("Unknown type byte: 0x{:02x}", type_byte);
490 None
491 }
492
493 fn parse_structure_desc(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
495 let mut offset = 0;
496
497 let (struct_id, consumed) = self.decode_string(&data[offset..])?;
499 offset += consumed;
500
501 let struct_id = if struct_id.is_empty() {
502 None
503 } else {
504 Some(struct_id)
505 };
506
507 let (field_count, consumed) = self.decode_size(&data[offset..])?;
509 offset += consumed;
510
511 let mut fields = Vec::with_capacity(field_count);
512
513 for _ in 0..field_count {
514 if offset >= data.len() {
515 break;
516 }
517 if let Some((field, consumed)) = self.parse_field_desc(&data[offset..]) {
518 offset += consumed;
519 fields.push(field);
520 } else {
521 break;
522 }
523 }
524
525 Some((StructureDesc { struct_id, fields }, offset))
526 }
527
528 pub fn parse_introspection(&self, data: &[u8]) -> Option<StructureDesc> {
530 self.parse_introspection_with_len(data)
531 .map(|(desc, _)| desc)
532 }
533
534 pub fn parse_introspection_with_len(&self, data: &[u8]) -> Option<(StructureDesc, usize)> {
536 if data.is_empty() {
537 return None;
538 }
539
540 let type_byte = data[0];
542
543 if type_byte == 0x80 {
545 let (desc, consumed) = self.parse_structure_desc(&data[1..])?;
546 return Some((desc, 1 + consumed));
547 }
548
549 if type_byte == 0xFD {
552 if data.len() < 4 {
553 return None;
554 }
555 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[3..]) {
557 return Some((desc, 3 + consumed));
558 }
559 if let Some((desc, consumed)) = self.parse_introspection_with_len(&data[1..]) {
561 return Some((desc, 1 + consumed));
562 }
563 return None;
564 }
565
566 if type_byte == 0xFE {
569 debug!(
570 "Introspection uses ONLY_ID (0xFE), but no registry is available in this decoder"
571 );
572 return None;
573 }
574
575 debug!("Unexpected introspection type byte: 0x{:02x}", type_byte);
576 None
577 }
578
579 fn decode_scalar(&self, data: &[u8], tc: TypeCode) -> Option<(DecodedValue, usize)> {
581 let size = tc.size()?;
582 if data.len() < size {
583 return None;
584 }
585
586 let value = match tc {
587 TypeCode::Boolean => DecodedValue::Boolean(data[0] != 0),
588 TypeCode::Int8 => DecodedValue::Int8(data[0] as i8),
589 TypeCode::UInt8 => DecodedValue::UInt8(data[0]),
590 TypeCode::Int16 => {
591 let v = if self.is_be {
592 i16::from_be_bytes([data[0], data[1]])
593 } else {
594 i16::from_le_bytes([data[0], data[1]])
595 };
596 DecodedValue::Int16(v)
597 }
598 TypeCode::UInt16 => {
599 let v = if self.is_be {
600 u16::from_be_bytes([data[0], data[1]])
601 } else {
602 u16::from_le_bytes([data[0], data[1]])
603 };
604 DecodedValue::UInt16(v)
605 }
606 TypeCode::Int32 => {
607 let v = if self.is_be {
608 i32::from_be_bytes(data[0..4].try_into().unwrap())
609 } else {
610 i32::from_le_bytes(data[0..4].try_into().unwrap())
611 };
612 DecodedValue::Int32(v)
613 }
614 TypeCode::UInt32 => {
615 let v = if self.is_be {
616 u32::from_be_bytes(data[0..4].try_into().unwrap())
617 } else {
618 u32::from_le_bytes(data[0..4].try_into().unwrap())
619 };
620 DecodedValue::UInt32(v)
621 }
622 TypeCode::Int64 => {
623 let v = if self.is_be {
624 i64::from_be_bytes(data[0..8].try_into().unwrap())
625 } else {
626 i64::from_le_bytes(data[0..8].try_into().unwrap())
627 };
628 DecodedValue::Int64(v)
629 }
630 TypeCode::UInt64 => {
631 let v = if self.is_be {
632 u64::from_be_bytes(data[0..8].try_into().unwrap())
633 } else {
634 u64::from_le_bytes(data[0..8].try_into().unwrap())
635 };
636 DecodedValue::UInt64(v)
637 }
638 TypeCode::Float32 => {
639 let v = if self.is_be {
640 f32::from_be_bytes(data[0..4].try_into().unwrap())
641 } else {
642 f32::from_le_bytes(data[0..4].try_into().unwrap())
643 };
644 DecodedValue::Float32(v)
645 }
646 TypeCode::Float64 => {
647 let v = if self.is_be {
648 f64::from_be_bytes(data[0..8].try_into().unwrap())
649 } else {
650 f64::from_le_bytes(data[0..8].try_into().unwrap())
651 };
652 DecodedValue::Float64(v)
653 }
654 _ => return None,
655 };
656
657 Some((value, size))
658 }
659
660 pub fn decode_value(
662 &self,
663 data: &[u8],
664 field_type: &FieldType,
665 ) -> Option<(DecodedValue, usize)> {
666 match field_type {
667 FieldType::Scalar(tc) => self.decode_scalar(data, *tc),
668 FieldType::String | FieldType::BoundedString(_) => {
669 let (s, consumed) = self.decode_string(data)?;
670 Some((DecodedValue::String(s), consumed))
671 }
672 FieldType::ScalarArray(tc) => {
673 let (count, size_consumed) = self.decode_size(data)?;
674 let mut offset = size_consumed;
675 let limit = count.min(4_000_000);
676 let mut values = Vec::with_capacity(limit);
677 let elem_size = tc.size().unwrap_or(1);
678 for _ in 0..limit {
679 if let Some((val, consumed)) = self.decode_scalar(&data[offset..], *tc) {
680 values.push(val);
681 offset += consumed;
682 } else {
683 break;
684 }
685 }
686 let remaining = count.saturating_sub(limit);
689 offset += remaining * elem_size;
690 Some((DecodedValue::Array(values), offset))
691 }
692 FieldType::StringArray => {
693 let (count, size_consumed) = self.decode_size(data)?;
694 let mut offset = size_consumed;
695 let max_items = count.min(4096);
696 let mut values = Vec::with_capacity(max_items);
697 for _ in 0..max_items {
698 if let Some((s, consumed)) = self.decode_string(&data[offset..]) {
699 values.push(DecodedValue::String(s));
700 offset += consumed;
701 } else {
702 break;
703 }
704 }
705 Some((DecodedValue::Array(values), offset))
706 }
707 FieldType::Structure(desc) => self.decode_structure(data, desc),
708 FieldType::StructureArray(desc) => {
709 let (count, size_consumed) = self.decode_size(data)?;
710 let mut offset = size_consumed;
711 let mut values = Vec::with_capacity(count.min(256));
712 for _ in 0..count.min(256) {
713 if offset >= data.len() {
715 return None;
716 }
717 let null_indicator = data[offset];
718 offset += 1;
719 if null_indicator == 0 {
720 values.push(DecodedValue::Structure(Vec::new()));
722 continue;
723 }
724 let (item, consumed) = self.decode_structure(&data[offset..], desc)?;
725 values.push(item);
726 offset += consumed;
727 }
728 Some((DecodedValue::Array(values), offset))
729 }
730 FieldType::Union(fields) => {
731 let (selector, consumed) = self.decode_size(data)?;
732 let field = fields.get(selector)?;
733 let (value, val_consumed) =
734 self.decode_value(&data[consumed..], &field.field_type)?;
735 Some((
736 DecodedValue::Structure(vec![(field.name.clone(), value)]),
737 consumed + val_consumed,
738 ))
739 }
740 FieldType::UnionArray(fields) => {
741 let (count, size_consumed) = self.decode_size(data)?;
742 let mut offset = size_consumed;
743 let mut values = Vec::with_capacity(count.min(128));
744 for _ in 0..count.min(128) {
745 let (selector, consumed) = self.decode_size(&data[offset..])?;
746 offset += consumed;
747 let field = fields.get(selector)?;
748 let (value, val_consumed) =
749 self.decode_value(&data[offset..], &field.field_type)?;
750 offset += val_consumed;
751 values.push(DecodedValue::Structure(vec![(field.name.clone(), value)]));
752 }
753 Some((DecodedValue::Array(values), offset))
754 }
755 FieldType::Variant => {
756 if data.is_empty() {
757 return None;
758 }
759 if data[0] == 0xFF {
760 return Some((DecodedValue::Null, 1));
761 }
762 let (variant_type, type_consumed) = self.parse_type_desc(data)?;
763 let (variant_value, value_consumed) =
764 self.decode_value(&data[type_consumed..], &variant_type)?;
765 Some((variant_value, type_consumed + value_consumed))
766 }
767 FieldType::VariantArray => {
768 let (count, size_consumed) = self.decode_size(data)?;
769 let mut offset = size_consumed;
770 let mut values = Vec::with_capacity(count.min(128));
771 for _ in 0..count.min(128) {
772 let (v, consumed) = self.decode_value(&data[offset..], &FieldType::Variant)?;
773 values.push(v);
774 offset += consumed;
775 }
776 Some((DecodedValue::Array(values), offset))
777 }
778 }
779 }
780
781 pub fn decode_structure(
783 &self,
784 data: &[u8],
785 desc: &StructureDesc,
786 ) -> Option<(DecodedValue, usize)> {
787 let mut offset = 0;
788 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
789
790 for field in &desc.fields {
791 if offset >= data.len() {
792 break;
793 }
794 if let Some((value, consumed)) = self.decode_value(&data[offset..], &field.field_type) {
795 fields.push((field.name.clone(), value));
796 offset += consumed;
797 } else {
798 break;
800 }
801 }
802
803 Some((DecodedValue::Structure(fields), offset))
804 }
805
806 pub fn decode_structure_with_bitset(
809 &self,
810 data: &[u8],
811 desc: &StructureDesc,
812 ) -> Option<(DecodedValue, usize)> {
813 if data.is_empty() {
814 return None;
815 }
816
817 let mut offset = 0;
818
819 let (bitset_size, size_consumed) = self.decode_size(data)?;
821 offset += size_consumed;
822
823 if bitset_size == 0 || offset + bitset_size > data.len() {
824 return Some((DecodedValue::Structure(vec![]), offset));
825 }
826
827 let bitset = &data[offset..offset + bitset_size];
828 offset += bitset_size;
829
830 let (value, consumed) =
831 self.decode_structure_with_bitset_body(&data[offset..], desc, bitset)?;
832 Some((value, offset + consumed))
833 }
834
835 pub fn decode_structure_with_bitset_and_overrun(
837 &self,
838 data: &[u8],
839 desc: &StructureDesc,
840 ) -> Option<(DecodedValue, usize)> {
841 if data.is_empty() {
842 return None;
843 }
844 let mut offset = 0usize;
845 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
846 offset += consumed1;
847 if offset + changed_size > data.len() {
848 return None;
849 }
850 let changed = &data[offset..offset + changed_size];
851 offset += changed_size;
852
853 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
854 offset += consumed2;
855 if offset + overrun_size > data.len() {
856 return None;
857 }
858 offset += overrun_size;
859
860 let (value, consumed) =
861 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
862 Some((value, offset + consumed))
863 }
864
865 pub fn decode_structure_with_bitset_then_overrun(
867 &self,
868 data: &[u8],
869 desc: &StructureDesc,
870 ) -> Option<(DecodedValue, usize)> {
871 if data.is_empty() {
872 return None;
873 }
874 let mut offset = 0usize;
875 let (changed_size, consumed1) = self.decode_size(&data[offset..])?;
876 offset += consumed1;
877 if offset + changed_size > data.len() {
878 return None;
879 }
880 let changed = &data[offset..offset + changed_size];
881 offset += changed_size;
882
883 let (value, consumed) =
884 self.decode_structure_with_bitset_body(&data[offset..], desc, changed)?;
885 offset += consumed;
886
887 let (overrun_size, consumed2) = self.decode_size(&data[offset..])?;
888 offset += consumed2;
889 if offset + overrun_size > data.len() {
890 return None;
891 }
892 offset += overrun_size;
893
894 Some((value, offset))
895 }
896
897 fn decode_structure_with_bitset_body(
898 &self,
899 data: &[u8],
900 desc: &StructureDesc,
901 bitset: &[u8],
902 ) -> Option<(DecodedValue, usize)> {
903 debug!(
905 "Bitset: {:02x?} (size={}), total_fields={}",
906 bitset,
907 bitset.len(),
908 count_structure_fields(desc)
909 );
910 debug!(
911 "Structure fields: {:?}",
912 desc.fields.iter().map(|f| &f.name).collect::<Vec<_>>()
913 );
914
915 let mut has_field_bits = false;
917 if !bitset.is_empty() {
918 for (i, b) in bitset.iter().enumerate() {
919 let mask = if i == 0 { *b & !0x01 } else { *b };
920 if mask != 0 {
921 has_field_bits = true;
922 break;
923 }
924 }
925 }
926 if !has_field_bits && !bitset.is_empty() && (bitset[0] & 0x01) != 0 {
927 if let Some((value, consumed)) = self.decode_structure(data, desc) {
928 return Some((value, consumed));
929 }
930 }
931
932 let mut fields: Vec<(String, DecodedValue)> = Vec::new();
933 let mut offset = 0usize;
934
935 fn decode_with_bitset_recursive(
936 decoder: &PvdDecoder,
937 data: &[u8],
938 offset: &mut usize,
939 desc: &StructureDesc,
940 bitset: &[u8],
941 bit_offset: &mut usize,
942 fields: &mut Vec<(String, DecodedValue)>,
943 ) -> bool {
944 for field in &desc.fields {
945 let byte_idx = *bit_offset / 8;
946 let bit_idx = *bit_offset % 8;
947 let current_bit = *bit_offset;
948 *bit_offset += 1;
949
950 let field_present = if byte_idx < bitset.len() {
951 (bitset[byte_idx] & (1 << bit_idx)) != 0
952 } else {
953 false
954 };
955
956 debug!(
957 "Field '{}' at bit {}: present={}",
958 field.name, current_bit, field_present
959 );
960
961 if let FieldType::Structure(nested_desc) = &field.field_type {
962 let child_start_bit = *bit_offset;
963 let child_field_count = count_structure_fields(nested_desc);
964
965 let mut any_child_bits_set = false;
966 for i in 0..child_field_count {
967 let check_byte = (child_start_bit + i) / 8;
968 let check_bit = (child_start_bit + i) % 8;
969 if check_byte < bitset.len() && (bitset[check_byte] & (1 << check_bit)) != 0
970 {
971 any_child_bits_set = true;
972 break;
973 }
974 }
975
976 debug!(
977 "Nested structure '{}': parent_present={}, child_start_bit={}, child_count={}, any_child_bits_set={}",
978 field.name,
979 field_present,
980 child_start_bit,
981 child_field_count,
982 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 {
1280 let items: Vec<String> = arr.iter().map(|v| format_scalar_value(v)).collect();
1281 format!("[{}]", items.join(", "))
1282 }
1283 }
1284 DecodedValue::Structure(fields) => {
1285 let nested: Vec<String> = fields
1286 .iter()
1287 .map(|(n, v)| format!("{}={}", n, format_scalar_value(v)))
1288 .collect();
1289 format!("{{{}}}", nested.join(", "))
1290 }
1291 DecodedValue::Raw(data) => {
1292 if data.len() <= 4 {
1293 format!("<{}>", hex::encode(data))
1294 } else {
1295 format!("<{}B>", data.len())
1296 }
1297 }
1298 }
1299}
1300
1301#[cfg(test)]
1302mod tests {
1303 use super::*;
1304
1305 #[test]
1306 fn test_decode_size() {
1307 let decoder = PvdDecoder::new(false);
1308
1309 assert_eq!(decoder.decode_size(&[5]), Some((5, 1)));
1311 assert_eq!(decoder.decode_size(&[253]), Some((253, 1)));
1312
1313 assert_eq!(
1315 decoder.decode_size(&[254, 0x00, 0x01, 0x00, 0x00]),
1316 Some((256, 5))
1317 );
1318 }
1319
1320 #[test]
1321 fn test_parse_introspection_full_with_id() {
1322 let decoder = PvdDecoder::new(false);
1323 let data = vec![
1324 0xFD, 0x06, 0x00, 0x80, 0x00, 0x01, 0x05, b'v', b'a', b'l', b'u', b'e', 0x43, ];
1332 let desc = decoder
1333 .parse_introspection(&data)
1334 .expect("parsed introspection");
1335 assert_eq!(desc.fields.len(), 1);
1336 assert_eq!(desc.fields[0].name, "value");
1337 match desc.fields[0].field_type {
1338 FieldType::Scalar(TypeCode::Float64) => {}
1339 _ => panic!("expected float64 value field"),
1340 }
1341 }
1342
1343 #[test]
1344 fn test_decode_string() {
1345 let decoder = PvdDecoder::new(false);
1346
1347 assert_eq!(decoder.decode_string(&[0]), Some((String::new(), 1)));
1349
1350 let data = [5, b'h', b'e', b'l', b'l', b'o'];
1352 assert_eq!(decoder.decode_string(&data), Some(("hello".to_string(), 6)));
1353 }
1354
1355 #[test]
1356 fn decode_variant_accepts_full_with_id_type_tag() {
1357 let decoder = PvdDecoder::new(false);
1358 let data = [0xFD, 0x02, 0x00, 0x60, 0x02, b'o', b'k'];
1360 let (value, consumed) = decoder
1361 .decode_value(&data, &FieldType::Variant)
1362 .expect("decode variant");
1363 assert_eq!(consumed, data.len());
1364 assert!(matches!(value, DecodedValue::String(ref s) if s == "ok"));
1365 }
1366
1367 #[test]
1368 fn test_decode_bitset_whole_structure() {
1369 let decoder = PvdDecoder::new(false);
1370 let desc = StructureDesc {
1371 struct_id: None,
1372 fields: vec![FieldDesc {
1373 name: "value".to_string(),
1374 field_type: FieldType::Scalar(TypeCode::Float64),
1375 }],
1376 };
1377 let mut data = Vec::new();
1379 data.push(0x01);
1380 data.push(0x01);
1381 data.extend_from_slice(&1.25f64.to_le_bytes());
1382
1383 let (decoded, _consumed) = decoder
1384 .decode_structure_with_bitset(&data, &desc)
1385 .expect("decoded");
1386 if let DecodedValue::Structure(fields) = decoded {
1387 assert_eq!(fields.len(), 1);
1388 assert_eq!(fields[0].0, "value");
1389 } else {
1390 panic!("expected structure");
1391 }
1392 }
1393
1394 #[test]
1395 fn format_structure_tree_includes_nested_fields() {
1396 let desc = StructureDesc {
1397 struct_id: Some("epics:nt/NTScalar:1.0".to_string()),
1398 fields: vec![
1399 FieldDesc {
1400 name: "value".to_string(),
1401 field_type: FieldType::Scalar(TypeCode::Float64),
1402 },
1403 FieldDesc {
1404 name: "alarm".to_string(),
1405 field_type: FieldType::Structure(StructureDesc {
1406 struct_id: None,
1407 fields: vec![
1408 FieldDesc {
1409 name: "severity".to_string(),
1410 field_type: FieldType::Scalar(TypeCode::Int32),
1411 },
1412 FieldDesc {
1413 name: "message".to_string(),
1414 field_type: FieldType::String,
1415 },
1416 ],
1417 }),
1418 },
1419 ],
1420 };
1421
1422 let rendered = format_structure_tree(&desc);
1423 assert!(rendered.contains("struct epics:nt/NTScalar:1.0"));
1424 assert!(rendered.contains("value: double"));
1425 assert!(rendered.contains("alarm: structure"));
1426 assert!(rendered.contains("severity: int"));
1427 assert!(rendered.contains("message: string"));
1428 }
1429
1430 #[test]
1431 fn decode_string_array_not_capped_at_100_items() {
1432 fn encode_size(size: usize) -> Vec<u8> {
1433 if size == 0 {
1434 return vec![0x00];
1435 }
1436 if size < 254 {
1437 return vec![size as u8];
1438 }
1439 let mut out = vec![0xFE];
1440 out.extend_from_slice(&(size as u32).to_le_bytes());
1441 out
1442 }
1443
1444 let item_count = 150usize;
1445 let mut raw = encode_size(item_count);
1446 for idx in 0..item_count {
1447 let s = format!("PV:{}", idx);
1448 raw.extend_from_slice(&encode_size(s.len()));
1449 raw.extend_from_slice(s.as_bytes());
1450 }
1451
1452 let decoder = PvdDecoder::new(false);
1453 let (decoded, _consumed) = decoder
1454 .decode_value(&raw, &FieldType::StringArray)
1455 .expect("decoded");
1456
1457 let DecodedValue::Array(items) = decoded else {
1458 panic!("expected decoded array");
1459 };
1460 assert_eq!(items.len(), item_count);
1461 }
1462}