1use alloc::boxed::Box;
35use alloc::string::{String, ToString};
36use alloc::vec::Vec;
37
38use zerodds_cdr::buffer::{BufferReader, BufferWriter};
39use zerodds_cdr::endianness::Endianness;
40use zerodds_cdr::error::{DecodeError, EncodeError};
41use zerodds_cdr::struct_enc::{
42 decode_appendable, encode_appendable, encode_mutable_member, read_all_mutable_members,
43};
44use zerodds_cdr::xcdr1::{encode_pl_cdr1_member, read_all_pl_cdr1_members, write_pl_cdr1_sentinel};
45use zerodds_cdr::{CdrDecode, CdrEncode, WString};
46
47use crate::dynamic::data::{DynamicData, DynamicValue};
48use crate::dynamic::descriptor::{ExtensibilityKind, TypeKind};
49use crate::dynamic::type_::DynamicType;
50
51#[derive(Debug)]
53pub enum CodecError {
54 Wire(String),
56 Dynamic(String),
58 NotSupported(String),
60}
61
62impl core::fmt::Display for CodecError {
63 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
64 match self {
65 Self::Wire(m) => write!(f, "wire: {m}"),
66 Self::Dynamic(m) => write!(f, "dynamic: {m}"),
67 Self::NotSupported(m) => write!(f, "not supported: {m}"),
68 }
69 }
70}
71
72type R<T> = Result<T, CodecError>;
73
74const fn endian(big: bool) -> Endianness {
75 if big {
76 Endianness::Big
77 } else {
78 Endianness::Little
79 }
80}
81
82fn dec_sentinel() -> DecodeError {
85 DecodeError::UnexpectedEof {
86 needed: 0,
87 offset: 0,
88 }
89}
90fn enc_sentinel() -> EncodeError {
91 EncodeError::ValueOutOfRange {
92 message: "dynamic codec inner error",
93 }
94}
95
96const fn is_primitive_kind(kind: TypeKind) -> bool {
99 matches!(
100 kind,
101 TypeKind::Boolean
102 | TypeKind::Byte
103 | TypeKind::Int8
104 | TypeKind::UInt8
105 | TypeKind::Int16
106 | TypeKind::UInt16
107 | TypeKind::Int32
108 | TypeKind::UInt32
109 | TypeKind::Int64
110 | TypeKind::UInt64
111 | TypeKind::Float32
112 | TypeKind::Float64
113 | TypeKind::Char8
114 | TypeKind::Char16
115 | TypeKind::Enumeration
116 )
117}
118
119fn scalar_dynamic_type(desc: &crate::dynamic::descriptor::TypeDescriptor) -> Option<DynamicType> {
123 use crate::dynamic::builder::DynamicTypeBuilderFactory as F;
124 match desc.kind {
125 TypeKind::String8 => Some(F::create_string_type(
126 desc.bound.first().copied().unwrap_or(0),
127 )),
128 TypeKind::String16 => Some(F::create_wstring_type(
129 desc.bound.first().copied().unwrap_or(0),
130 )),
131 k if is_primitive_kind(k) => Some(DynamicType::new_primitive(k)),
132 _ => None,
133 }
134}
135
136const DISCRIMINATOR_MEMBER_ID: u32 = 0;
139
140fn read_discriminator(r: &mut BufferReader<'_>, kind: TypeKind) -> R<i64> {
144 let w = |e: DecodeError| CodecError::Wire(e.to_string());
145 Ok(match kind {
146 TypeKind::Boolean | TypeKind::Byte | TypeKind::UInt8 | TypeKind::Char8 => {
147 i64::from(r.read_u8().map_err(w)?)
148 }
149 TypeKind::Int8 => i64::from(r.read_u8().map_err(w)? as i8),
150 TypeKind::UInt16 | TypeKind::Char16 => i64::from(r.read_u16().map_err(w)?),
151 TypeKind::Int16 => i64::from(r.read_u16().map_err(w)? as i16),
152 TypeKind::UInt32 | TypeKind::Enumeration => i64::from(r.read_u32().map_err(w)?),
153 TypeKind::Int32 => i64::from(r.read_u32().map_err(w)? as i32),
154 TypeKind::UInt64 => r.read_u64().map_err(w)? as i64,
155 TypeKind::Int64 => r.read_u64().map_err(w)? as i64,
156 other => {
157 return Err(CodecError::NotSupported(alloc::format!(
158 "union discriminator kind {other:?}"
159 )));
160 }
161 })
162}
163
164fn write_discriminator(w: &mut BufferWriter, kind: TypeKind, val: i64) -> R<()> {
166 let e = |x: EncodeError| CodecError::Wire(x.to_string());
167 match kind {
168 TypeKind::Boolean | TypeKind::Byte | TypeKind::UInt8 | TypeKind::Char8 | TypeKind::Int8 => {
169 w.write_u8(val as u8).map_err(e)
170 }
171 TypeKind::UInt16 | TypeKind::Char16 | TypeKind::Int16 => w.write_u16(val as u16).map_err(e),
172 TypeKind::UInt32 | TypeKind::Enumeration | TypeKind::Int32 => {
173 w.write_u32(val as u32).map_err(e)
174 }
175 TypeKind::UInt64 | TypeKind::Int64 => w.write_u64(val as u64).map_err(e),
176 other => Err(CodecError::NotSupported(alloc::format!(
177 "union discriminator kind {other:?}"
178 ))),
179 }
180}
181
182fn union_discriminator_kind(ty: &DynamicType) -> TypeKind {
185 ty.descriptor()
186 .discriminator_type
187 .as_ref()
188 .map_or(TypeKind::Int32, |d| d.kind)
189}
190
191pub fn decode_dynamic(
203 ty: &DynamicType,
204 bytes: &[u8],
205 xcdr2: bool,
206 big_endian: bool,
207) -> R<DynamicData> {
208 let mut r = BufferReader::new(bytes, endian(big_endian));
209 if xcdr2 {
210 r = r.xcdr2();
211 }
212 decode_aggregate(ty, &mut r, xcdr2)
213}
214
215fn decode_aggregate(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
216 match ty.kind() {
217 TypeKind::Structure => decode_struct(ty, r, xcdr2),
218 TypeKind::Union => decode_union(ty, r, xcdr2),
219 other => Err(CodecError::NotSupported(alloc::format!(
220 "top-level type kind {other:?} is not an aggregate"
221 ))),
222 }
223}
224
225fn decode_struct(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
226 match ty.descriptor().extensibility_kind {
227 ExtensibilityKind::Final => {
228 let mut data = DynamicData::new(ty.clone());
230 read_members_in_order(ty, r, xcdr2, &mut data)?;
231 Ok(data)
232 }
233 ExtensibilityKind::Appendable => {
234 if !xcdr2 {
236 let mut data = DynamicData::new(ty.clone());
237 read_members_in_order(ty, r, xcdr2, &mut data)?;
238 return Ok(data);
239 }
240 let mut inner: Option<CodecError> = None;
241 let res = decode_appendable(r, |sub| {
242 let mut data = DynamicData::new(ty.clone());
243 match read_members_in_order(ty, sub, xcdr2, &mut data) {
244 Ok(()) => Ok(data),
245 Err(e) => {
246 inner = Some(e);
247 Err(dec_sentinel())
248 }
249 }
250 });
251 match res {
252 Ok(d) => Ok(d),
253 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
254 }
255 }
256 ExtensibilityKind::Mutable => {
257 if xcdr2 {
258 decode_struct_mutable(ty, r, xcdr2)
259 } else {
260 decode_struct_pl_cdr1(ty, r)
261 }
262 }
263 }
264}
265
266fn read_members_in_order(
267 ty: &DynamicType,
268 r: &mut BufferReader<'_>,
269 xcdr2: bool,
270 data: &mut DynamicData,
271) -> R<()> {
272 for i in 0..ty.member_count() {
273 let m = ty
274 .member_by_index(i)
275 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
276 let mt = m.dynamic_type().clone();
277 let id = m.id();
278 let val = read_value(&mt, r, xcdr2)?;
279 data.set_value_raw(id, val);
280 }
281 Ok(())
282}
283
284fn decode_struct_mutable(
285 ty: &DynamicType,
286 r: &mut BufferReader<'_>,
287 xcdr2: bool,
288) -> R<DynamicData> {
289 let mut err: Option<CodecError> = None;
295 let res = decode_appendable(r, |sub| {
296 let members = read_all_mutable_members(sub)?;
297 let mut data = DynamicData::new(ty.clone());
298 for mm in members {
299 let Some(m) = ty.member_by_id(mm.member_id) else {
300 continue; };
302 let mt = m.dynamic_type().clone();
303 let mut bsub = BufferReader::new(mm.body, sub.endianness());
304 if xcdr2 {
305 bsub = bsub.xcdr2();
306 }
307 match read_value(&mt, &mut bsub, xcdr2) {
308 Ok(val) => data.set_value_raw(m.id(), val),
309 Err(e) => {
310 err = Some(e);
311 return Err(dec_sentinel());
312 }
313 }
314 }
315 Ok(data)
316 });
317 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
318}
319
320fn decode_struct_pl_cdr1(ty: &DynamicType, r: &mut BufferReader<'_>) -> R<DynamicData> {
324 let members = read_all_pl_cdr1_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
325 let mut data = DynamicData::new(ty.clone());
326 for mm in members {
327 let Some(m) = ty.member_by_id(mm.member_id) else {
328 continue; };
330 let mt = m.dynamic_type().clone();
331 let mut bsub = BufferReader::new(&mm.body, r.endianness());
332 let val = read_value(&mt, &mut bsub, false)?;
333 data.set_value_raw(m.id(), val);
334 }
335 Ok(data)
336}
337
338fn decode_union_pl_cdr1(ty: &DynamicType, r: &mut BufferReader<'_>) -> R<DynamicData> {
341 let members = read_all_pl_cdr1_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
342 let mut data = DynamicData::new(ty.clone());
343 let disc_idx = members
344 .iter()
345 .position(|m| m.member_id == DISCRIMINATOR_MEMBER_ID)
346 .ok_or_else(|| {
347 CodecError::Wire("mutable union (PL_CDR1): no discriminator member id 0".to_string())
348 })?;
349 let mut dsub = BufferReader::new(&members[disc_idx].body, r.endianness());
350 let disc = read_discriminator(&mut dsub, union_discriminator_kind(ty))?;
351
352 let mut chosen = None;
353 let mut default = None;
354 for m in ty.members() {
355 let d = m.descriptor();
356 if d.is_default_label {
357 default = Some(m);
358 }
359 if d.label.contains(&disc) {
360 chosen = Some(m);
361 break;
362 }
363 }
364 if let Some(m) = chosen.or(default) {
365 if let Some(bw) = members
366 .iter()
367 .enumerate()
368 .find(|(i, _)| *i != disc_idx)
369 .map(|(_, w)| w)
370 {
371 let mt = m.dynamic_type().clone();
372 let mut bsub = BufferReader::new(&bw.body, r.endianness());
373 let val = read_value(&mt, &mut bsub, false)?;
374 data.set_value_raw(m.id(), val);
375 }
376 }
377 Ok(data)
378}
379
380fn decode_union(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
381 match ty.descriptor().extensibility_kind {
382 ExtensibilityKind::Final => decode_union_body(ty, r, xcdr2),
383 ExtensibilityKind::Appendable => {
384 if !xcdr2 {
386 return decode_union_body(ty, r, xcdr2);
387 }
388 let mut inner: Option<CodecError> = None;
389 let res = decode_appendable(r, |sub| match decode_union_body(ty, sub, xcdr2) {
390 Ok(d) => Ok(d),
391 Err(e) => {
392 inner = Some(e);
393 Err(dec_sentinel())
394 }
395 });
396 match res {
397 Ok(d) => Ok(d),
398 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
399 }
400 }
401 ExtensibilityKind::Mutable => {
402 if xcdr2 {
403 decode_union_mutable(ty, r, xcdr2)
404 } else {
405 decode_union_pl_cdr1(ty, r)
406 }
407 }
408 }
409}
410
411fn decode_union_mutable(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
419 let mut err: Option<CodecError> = None;
423 let res = decode_appendable(r, |sub| match decode_union_mutable_body(ty, sub, xcdr2) {
424 Ok(d) => Ok(d),
425 Err(e) => {
426 err = Some(e);
427 Err(dec_sentinel())
428 }
429 });
430 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
431}
432
433fn decode_union_mutable_body(
435 ty: &DynamicType,
436 r: &mut BufferReader<'_>,
437 xcdr2: bool,
438) -> R<DynamicData> {
439 let members = read_all_mutable_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
440 let mut data = DynamicData::new(ty.clone());
441
442 let disc_idx = members
446 .iter()
447 .position(|m| m.member_id == DISCRIMINATOR_MEMBER_ID)
448 .ok_or_else(|| {
449 CodecError::Wire(
450 "mutable union: no discriminator member (id 0) on the wire".to_string(),
451 )
452 })?;
453 let mut dsub = BufferReader::new(members[disc_idx].body, r.endianness());
454 if xcdr2 {
455 dsub = dsub.xcdr2();
456 }
457 let disc = read_discriminator(&mut dsub, union_discriminator_kind(ty))?;
458
459 let mut chosen = None;
460 let mut default = None;
461 for m in ty.members() {
462 let d = m.descriptor();
463 if d.is_default_label {
464 default = Some(m);
465 }
466 if d.label.contains(&disc) {
467 chosen = Some(m);
468 break;
469 }
470 }
471 if let Some(m) = chosen.or(default) {
472 if let Some(bw) = members
480 .iter()
481 .enumerate()
482 .find(|(i, _)| *i != disc_idx)
483 .map(|(_, w)| w)
484 {
485 let mt = m.dynamic_type().clone();
486 let mut bsub = BufferReader::new(bw.body, r.endianness());
487 if xcdr2 {
488 bsub = bsub.xcdr2();
489 }
490 let val = read_value(&mt, &mut bsub, xcdr2)?;
491 data.set_value_raw(m.id(), val);
492 }
493 }
494 Ok(data)
495}
496
497fn decode_union_body(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
498 let disc = read_discriminator(r, union_discriminator_kind(ty))?;
499 let mut data = DynamicData::new(ty.clone());
500 let mut chosen = None;
501 let mut default = None;
502 for m in ty.members() {
503 let d = m.descriptor();
504 if d.is_default_label {
505 default = Some(m);
506 }
507 if d.label.contains(&disc) {
508 chosen = Some(m);
509 break;
510 }
511 }
512 if let Some(m) = chosen.or(default) {
513 let mt = m.dynamic_type().clone();
514 let val = read_value(&mt, r, xcdr2)?;
515 data.set_value_raw(m.id(), val);
516 }
517 Ok(data)
518}
519
520fn read_value(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicValue> {
522 let w = |e: DecodeError| CodecError::Wire(e.to_string());
523 Ok(match ty.kind() {
524 TypeKind::Boolean => DynamicValue::Bool(r.read_u8().map_err(w)? != 0),
525 TypeKind::Byte => DynamicValue::Byte(r.read_u8().map_err(w)?),
526 TypeKind::UInt8 => DynamicValue::UInt8(r.read_u8().map_err(w)?),
527 TypeKind::Int8 => DynamicValue::Int8(r.read_u8().map_err(w)? as i8),
528 TypeKind::Int16 => DynamicValue::Int16(r.read_u16().map_err(w)? as i16),
529 TypeKind::UInt16 => DynamicValue::UInt16(r.read_u16().map_err(w)?),
530 TypeKind::Int32 => DynamicValue::Int32(r.read_u32().map_err(w)? as i32),
531 TypeKind::UInt32 => DynamicValue::UInt32(r.read_u32().map_err(w)?),
532 TypeKind::Enumeration => DynamicValue::Int32(r.read_u32().map_err(w)? as i32),
533 TypeKind::Int64 => DynamicValue::Int64(r.read_u64().map_err(w)? as i64),
534 TypeKind::UInt64 => DynamicValue::UInt64(r.read_u64().map_err(w)?),
535 TypeKind::Float32 => DynamicValue::Float32(f32::from_bits(r.read_u32().map_err(w)?)),
536 TypeKind::Float64 => DynamicValue::Float64(f64::from_bits(r.read_u64().map_err(w)?)),
537 TypeKind::Char8 => DynamicValue::Char8(r.read_u8().map_err(w)?),
538 TypeKind::Char16 => DynamicValue::Char16(r.read_u16().map_err(w)?),
539 TypeKind::String8 => DynamicValue::String(r.read_string().map_err(w)?),
540 TypeKind::String16 => {
541 let ws = WString::decode(r).map_err(w)?;
543 DynamicValue::WString(ws.0.encode_utf16().collect())
544 }
545 TypeKind::Structure | TypeKind::Union => {
546 DynamicValue::Complex(Box::new(decode_aggregate(ty, r, xcdr2)?))
547 }
548 TypeKind::Sequence => read_collection(ty, r, xcdr2, None)?,
549 TypeKind::Array => {
550 let n: usize = ty.descriptor().bound.iter().copied().product::<u32>() as usize;
551 read_collection(ty, r, xcdr2, Some(n))?
552 }
553 TypeKind::Alias => {
554 let target = ty
557 .descriptor()
558 .base_type
559 .as_ref()
560 .and_then(|b| scalar_dynamic_type(b))
561 .ok_or_else(|| CodecError::NotSupported("alias-to-composite target".to_string()))?;
562 read_value(&target, r, xcdr2)?
563 }
564 other => {
565 return Err(CodecError::NotSupported(alloc::format!(
566 "member kind {other:?}"
567 )));
568 }
569 })
570}
571
572fn read_collection(
583 ty: &DynamicType,
584 r: &mut BufferReader<'_>,
585 xcdr2: bool,
586 fixed_len: Option<usize>,
587) -> R<DynamicValue> {
588 let desc = ty.descriptor();
589 let elem_desc = desc
590 .element_type
591 .as_ref()
592 .ok_or_else(|| CodecError::Dynamic("collection has no element_type".to_string()))?;
593 let elem_kind = elem_desc.kind;
594 let elem_ty = element_type(ty, elem_desc)?;
595 let w = |e: DecodeError| CodecError::Wire(e.to_string());
596 if xcdr2 && !is_primitive_kind(elem_kind) {
597 let _dheader = r.read_u32().map_err(w)?; }
599 let count = match fixed_len {
600 Some(n) => n,
601 None => r.read_u32().map_err(w)? as usize,
602 };
603 let mut out = Vec::with_capacity(count.min(4096));
604 for _ in 0..count {
605 let v = read_value(&elem_ty, r, xcdr2)?;
606 let elem_data = match v {
607 DynamicValue::Complex(d) => *d,
609 scalar => {
611 let mut d = DynamicData::new(elem_ty.clone());
612 d.set_value_raw(0, scalar);
613 d
614 }
615 };
616 out.push(elem_data);
617 }
618 Ok(DynamicValue::Sequence(out))
619}
620
621fn element_type(
625 ty: &DynamicType,
626 elem_desc: &crate::dynamic::descriptor::TypeDescriptor,
627) -> R<DynamicType> {
628 crate::dynamic::collection::resolved_element(ty)
629 .cloned()
630 .or_else(|| scalar_dynamic_type(elem_desc))
631 .ok_or_else(|| {
632 CodecError::NotSupported(alloc::format!(
633 "collection of {:?} (no resolved element type)",
634 elem_desc.kind
635 ))
636 })
637}
638
639fn write_collection(
644 items: &[DynamicData],
645 ty: &DynamicType,
646 w: &mut BufferWriter,
647 xcdr2: bool,
648) -> R<()> {
649 let desc = ty.descriptor();
650 let elem_desc = desc
651 .element_type
652 .as_ref()
653 .ok_or_else(|| CodecError::Dynamic("collection has no element_type".to_string()))?;
654 let elem_kind = elem_desc.kind;
655 let elem_ty = element_type(ty, elem_desc)?;
656 let composite = matches!(elem_ty.kind(), TypeKind::Structure | TypeKind::Union);
657 let is_array = ty.kind() == TypeKind::Array;
658 let e = |x: EncodeError| CodecError::Wire(x.to_string());
659
660 let write_body = |w: &mut BufferWriter| -> R<()> {
661 if !is_array {
662 w.write_u32(items.len() as u32).map_err(e)?;
663 }
664 for d in items {
665 if composite {
666 write_aggregate(d, w, xcdr2)?;
667 } else {
668 let v = d
669 .get_value(0)
670 .ok_or_else(|| CodecError::Dynamic("scalar element unset".to_string()))?;
671 write_value(&v.clone(), &elem_ty, w, xcdr2)?;
672 }
673 }
674 Ok(())
675 };
676
677 if xcdr2 && !is_primitive_kind(elem_kind) {
678 let mut sub = BufferWriter::new(w.endianness());
680 if xcdr2 {
681 sub = sub.xcdr2();
682 }
683 write_body(&mut sub)?;
684 let body = sub.into_bytes();
685 w.write_u32(body.len() as u32).map_err(e)?;
686 w.write_bytes(&body).map_err(e)?;
687 Ok(())
688 } else {
689 write_body(w)
690 }
691}
692
693pub fn encode_dynamic(data: &DynamicData, xcdr2: bool, big_endian: bool) -> R<Vec<u8>> {
703 let mut w = BufferWriter::new(endian(big_endian));
704 if xcdr2 {
705 w = w.xcdr2();
706 }
707 write_aggregate(data, &mut w, xcdr2)?;
708 Ok(w.into_bytes())
709}
710
711fn write_aggregate(data: &DynamicData, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
712 let ty = data.dynamic_type().clone();
713 match ty.kind() {
714 TypeKind::Structure => write_struct(data, &ty, w, xcdr2),
715 TypeKind::Union => write_union(data, &ty, w, xcdr2),
716 other => Err(CodecError::NotSupported(alloc::format!(
717 "top-level type kind {other:?} is not an aggregate"
718 ))),
719 }
720}
721
722fn write_struct(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
723 match ty.descriptor().extensibility_kind {
724 ExtensibilityKind::Final => write_members_in_order(data, ty, w, xcdr2),
725 ExtensibilityKind::Appendable => {
726 if !xcdr2 {
729 return write_members_in_order(data, ty, w, xcdr2);
730 }
731 let mut inner: Option<CodecError> = None;
732 let res = encode_appendable(w, |sub| {
733 match write_members_in_order(data, ty, sub, xcdr2) {
734 Ok(()) => Ok(()),
735 Err(e) => {
736 inner = Some(e);
737 Err(enc_sentinel())
738 }
739 }
740 });
741 match res {
742 Ok(()) => Ok(()),
743 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
744 }
745 }
746 ExtensibilityKind::Mutable => {
747 if !xcdr2 {
750 return write_struct_pl_cdr1(data, ty, w);
751 }
752 let mut outer: Option<CodecError> = None;
756 let res = encode_appendable(w, |fw| {
757 for i in 0..ty.member_count() {
758 let m = match ty.member_by_index(i).ok_or_else(|| {
759 CodecError::Dynamic(alloc::format!("missing member index {i}"))
760 }) {
761 Ok(m) => m,
762 Err(e) => {
763 outer = Some(e);
764 return Err(enc_sentinel());
765 }
766 };
767 let id = m.id();
768 let Some(val) = data.get_value(id).cloned() else {
769 continue;
770 };
771 let mt = m.dynamic_type().clone();
772 let mu = m.descriptor().is_must_understand;
773 let res = encode_mutable_member(fw, id, mu, |sub| {
774 match write_value(&val, &mt, sub, xcdr2) {
775 Ok(()) => Ok(()),
776 Err(e) => {
777 outer = Some(e);
778 Err(enc_sentinel())
779 }
780 }
781 });
782 if let Err(e) = res {
783 if outer.is_none() {
784 outer = Some(CodecError::Wire(e.to_string()));
785 }
786 return Err(enc_sentinel());
787 }
788 }
789 Ok(())
790 });
791 res.map_err(|e| outer.unwrap_or_else(|| CodecError::Wire(e.to_string())))
792 }
793 }
794}
795
796fn write_members_in_order(
797 data: &DynamicData,
798 ty: &DynamicType,
799 w: &mut BufferWriter,
800 xcdr2: bool,
801) -> R<()> {
802 for i in 0..ty.member_count() {
803 let m = ty
804 .member_by_index(i)
805 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
806 let id = m.id();
807 let val = data
808 .get_value(id)
809 .ok_or_else(|| CodecError::Dynamic(alloc::format!("member {id} unset on encode")))?
810 .clone();
811 let mt = m.dynamic_type().clone();
812 write_value(&val, &mt, w, xcdr2)?;
813 }
814 Ok(())
815}
816
817fn write_struct_pl_cdr1(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter) -> R<()> {
821 for i in 0..ty.member_count() {
822 let m = ty
823 .member_by_index(i)
824 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
825 let id = m.id();
826 let Some(val) = data.get_value(id).cloned() else {
827 continue;
828 };
829 let mt = m.dynamic_type().clone();
830 let mut err: Option<CodecError> = None;
831 let res = encode_pl_cdr1_member(w, id, |sub| match write_value(&val, &mt, sub, false) {
832 Ok(()) => Ok(()),
833 Err(e) => {
834 err = Some(e);
835 Err(enc_sentinel())
836 }
837 });
838 if let Err(e) = res {
839 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
840 }
841 }
842 write_pl_cdr1_sentinel(w).map_err(|e| CodecError::Wire(e.to_string()))
843}
844
845fn write_union_pl_cdr1(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter) -> R<()> {
848 let disc_kind = union_discriminator_kind(ty);
849 let m = ty
850 .members()
851 .find(|m| data.get_value(m.id()).is_some())
852 .ok_or_else(|| CodecError::Dynamic("union has no branch set".to_string()))?;
853 let disc = m.descriptor().label.first().copied().unwrap_or(0);
854 let val = data
855 .get_value(m.id())
856 .cloned()
857 .ok_or_else(|| CodecError::Dynamic("union branch value vanished".to_string()))?;
858 let mt = m.dynamic_type().clone();
859
860 let mut err: Option<CodecError> = None;
861 let r1 = encode_pl_cdr1_member(w, DISCRIMINATOR_MEMBER_ID, |sub| match write_discriminator(
862 sub, disc_kind, disc,
863 ) {
864 Ok(()) => Ok(()),
865 Err(e) => {
866 err = Some(e);
867 Err(enc_sentinel())
868 }
869 });
870 if let Err(e) = r1 {
871 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
872 }
873 let r2 = encode_pl_cdr1_member(w, m.id(), |sub| match write_value(&val, &mt, sub, false) {
874 Ok(()) => Ok(()),
875 Err(e) => {
876 err = Some(e);
877 Err(enc_sentinel())
878 }
879 });
880 if let Err(e) = r2 {
881 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
882 }
883 write_pl_cdr1_sentinel(w).map_err(|e| CodecError::Wire(e.to_string()))
884}
885
886fn write_union(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
887 match ty.descriptor().extensibility_kind {
888 ExtensibilityKind::Final => write_union_body(data, ty, w, xcdr2),
889 ExtensibilityKind::Appendable => {
890 if !xcdr2 {
892 return write_union_body(data, ty, w, xcdr2);
893 }
894 let mut inner: Option<CodecError> = None;
895 let res = encode_appendable(w, |sub| match write_union_body(data, ty, sub, xcdr2) {
896 Ok(()) => Ok(()),
897 Err(e) => {
898 inner = Some(e);
899 Err(enc_sentinel())
900 }
901 });
902 match res {
903 Ok(()) => Ok(()),
904 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
905 }
906 }
907 ExtensibilityKind::Mutable => {
908 if xcdr2 {
909 write_union_mutable(data, ty, w, xcdr2)
910 } else {
911 write_union_pl_cdr1(data, ty, w)
912 }
913 }
914 }
915}
916
917fn write_union_body(
918 data: &DynamicData,
919 ty: &DynamicType,
920 w: &mut BufferWriter,
921 xcdr2: bool,
922) -> R<()> {
923 let disc_kind = union_discriminator_kind(ty);
924 for m in ty.members() {
925 let id = m.id();
926 if let Some(val) = data.get_value(id).cloned() {
927 let disc = m.descriptor().label.first().copied().unwrap_or(0);
928 write_discriminator(w, disc_kind, disc)?;
929 let mt = m.dynamic_type().clone();
930 return write_value(&val, &mt, w, xcdr2);
931 }
932 }
933 Err(CodecError::Dynamic("union has no branch set".to_string()))
934}
935
936fn write_union_mutable(
942 data: &DynamicData,
943 ty: &DynamicType,
944 w: &mut BufferWriter,
945 xcdr2: bool,
946) -> R<()> {
947 let disc_kind = union_discriminator_kind(ty);
948 let m = ty
949 .members()
950 .find(|m| data.get_value(m.id()).is_some())
951 .ok_or_else(|| CodecError::Dynamic("union has no branch set".to_string()))?;
952 let disc = m.descriptor().label.first().copied().unwrap_or(0);
953 let val = data
954 .get_value(m.id())
955 .cloned()
956 .ok_or_else(|| CodecError::Dynamic("union branch value vanished".to_string()))?;
957 let mt = m.dynamic_type().clone();
958
959 let mu = m.descriptor().is_must_understand;
960 let mut err: Option<CodecError> = None;
964 let res =
965 encode_appendable(w, |fw| {
966 let r1 = encode_mutable_member(fw, DISCRIMINATOR_MEMBER_ID, true, |sub| {
967 match write_discriminator(sub, disc_kind, disc) {
968 Ok(()) => Ok(()),
969 Err(e) => {
970 err = Some(e);
971 Err(enc_sentinel())
972 }
973 }
974 });
975 if let Err(e) = r1 {
976 if err.is_none() {
977 err = Some(CodecError::Wire(e.to_string()));
978 }
979 return Err(enc_sentinel());
980 }
981 let r2 = encode_mutable_member(fw, m.id(), mu, |sub| {
982 match write_value(&val, &mt, sub, xcdr2) {
983 Ok(()) => Ok(()),
984 Err(e) => {
985 err = Some(e);
986 Err(enc_sentinel())
987 }
988 }
989 });
990 if let Err(e) = r2 {
991 if err.is_none() {
992 err = Some(CodecError::Wire(e.to_string()));
993 }
994 return Err(enc_sentinel());
995 }
996 Ok(())
997 });
998 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
999}
1000
1001fn write_value(v: &DynamicValue, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
1003 let e = |x: EncodeError| CodecError::Wire(x.to_string());
1004 match v {
1005 DynamicValue::Bool(b) => w.write_u8(u8::from(*b)).map_err(e),
1006 DynamicValue::Byte(x) | DynamicValue::UInt8(x) | DynamicValue::Char8(x) => {
1007 w.write_u8(*x).map_err(e)
1008 }
1009 DynamicValue::Int8(x) => w.write_u8(*x as u8).map_err(e),
1010 DynamicValue::Int16(x) => w.write_u16(*x as u16).map_err(e),
1011 DynamicValue::UInt16(x) | DynamicValue::Char16(x) => w.write_u16(*x).map_err(e),
1012 DynamicValue::Int32(x) => w.write_u32(*x as u32).map_err(e),
1013 DynamicValue::UInt32(x) => w.write_u32(*x).map_err(e),
1014 DynamicValue::Int64(x) => w.write_u64(*x as u64).map_err(e),
1015 DynamicValue::UInt64(x) => w.write_u64(*x).map_err(e),
1016 DynamicValue::Float32(x) => w.write_u32(x.to_bits()).map_err(e),
1017 DynamicValue::Float64(x) => w.write_u64(x.to_bits()).map_err(e),
1018 DynamicValue::String(s) => w.write_string(s).map_err(e),
1019 DynamicValue::WString(units) => {
1020 WString(String::from_utf16_lossy(units))
1023 .encode(w)
1024 .map_err(e)
1025 }
1026 DynamicValue::Complex(d) => write_aggregate(d, w, xcdr2),
1027 DynamicValue::Sequence(items) => write_collection(items, ty, w, xcdr2),
1028 DynamicValue::None => Ok(()),
1029 }
1030}
1031
1032#[cfg(test)]
1033#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
1034mod tests {
1035 use super::*;
1036 use crate::dynamic::builder::DynamicTypeBuilderFactory;
1037 use crate::dynamic::descriptor::{MemberDescriptor, TypeDescriptor};
1038
1039 fn sample_type(ext: ExtensibilityKind) -> DynamicType {
1042 let mut desc = TypeDescriptor::structure("Sample");
1043 desc.extensibility_kind = ext;
1044 let mut b = DynamicTypeBuilderFactory::create_type(desc).expect("builder");
1045 b.add_struct_member("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
1046 .unwrap();
1047 b.add_struct_member(
1048 "b",
1049 1,
1050 TypeDescriptor::primitive(TypeKind::UInt64, "uint64"),
1051 )
1052 .unwrap();
1053 let string_desc = DynamicTypeBuilderFactory::create_string_type(255)
1054 .descriptor()
1055 .clone();
1056 b.add_struct_member("s", 2, string_desc).unwrap();
1057 b.add_struct_member(
1058 "f",
1059 3,
1060 TypeDescriptor::primitive(TypeKind::Float64, "float64"),
1061 )
1062 .unwrap();
1063 b.build().expect("build")
1064 }
1065
1066 fn populate(ty: &DynamicType) -> DynamicData {
1067 let mut d = DynamicData::new(ty.clone());
1068 d.set_int32_value(0, -7).unwrap();
1069 d.set_uint64_value(1, 0xDEAD_BEEF_0000_0001).unwrap();
1070 d.set_string_value(2, "hello-dynamic").unwrap();
1071 d.set_float64_value(3, 3.5).unwrap();
1072 d
1073 }
1074
1075 fn roundtrip(ext: ExtensibilityKind, xcdr2: bool) {
1076 let ty = sample_type(ext);
1077 let src = populate(&ty);
1078 let bytes = encode_dynamic(&src, xcdr2, false).expect("encode");
1079 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1080 assert_eq!(back.get_int32_value(0).unwrap(), -7);
1082 assert_eq!(back.get_uint64_value(1).unwrap(), 0xDEAD_BEEF_0000_0001);
1083 assert_eq!(back.get_string_value(2).unwrap(), "hello-dynamic");
1084 assert!((back.get_float64_value(3).unwrap() - 3.5).abs() < f64::EPSILON);
1085 let bytes2 = encode_dynamic(&back, xcdr2, false).expect("re-encode");
1087 assert_eq!(
1088 bytes, bytes2,
1089 "byte-exact round-trip failed for {ext:?} xcdr2={xcdr2}"
1090 );
1091 }
1092
1093 #[test]
1094 fn roundtrip_final_xcdr2() {
1095 roundtrip(ExtensibilityKind::Final, true);
1096 }
1097 #[test]
1098 fn roundtrip_final_xcdr1() {
1099 roundtrip(ExtensibilityKind::Final, false);
1100 }
1101 #[test]
1102 fn roundtrip_appendable_xcdr2() {
1103 roundtrip(ExtensibilityKind::Appendable, true);
1104 }
1105 #[test]
1106 fn roundtrip_mutable_xcdr2() {
1107 roundtrip(ExtensibilityKind::Mutable, true);
1108 }
1109
1110 #[test]
1113 fn roundtrip_union() {
1114 let disc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
1115 let mut b = DynamicTypeBuilderFactory::create_union("U", disc).expect("union builder");
1116 let mut m1 =
1118 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1119 m1.index = 0;
1120 m1.label = alloc::vec![1];
1121 b.add_member_resolved(m1, DynamicType::new_primitive(TypeKind::Int32))
1122 .unwrap();
1123 let mut m2 = MemberDescriptor::new(
1125 "u",
1126 1,
1127 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1128 );
1129 m2.index = 1;
1130 m2.label = alloc::vec![2];
1131 b.add_member_resolved(m2, DynamicType::new_primitive(TypeKind::UInt32))
1132 .unwrap();
1133 let u = b.build().expect("build union");
1134
1135 let mut d = DynamicData::new(u.clone());
1137 d.set_uint32_value(1, 0xABCD).unwrap();
1138 let bytes = encode_dynamic(&d, true, false).expect("encode");
1139 let back = decode_dynamic(&u, &bytes, true, false).expect("decode");
1140 assert_eq!(back.get_uint32_value(1).unwrap(), 0xABCD);
1141 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1142 }
1143
1144 #[test]
1149 fn roundtrip_union_mutable() {
1150 let build = || {
1151 let mut ud =
1152 TypeDescriptor::union("UM", TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1153 ud.extensibility_kind = ExtensibilityKind::Mutable;
1154 let mut b = DynamicTypeBuilderFactory::create_type(ud).expect("union");
1155 let mut m1 =
1156 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1157 m1.index = 0;
1158 m1.label = alloc::vec![1];
1159 b.add_member_resolved(m1, DynamicType::new_primitive(TypeKind::Int32))
1160 .unwrap();
1161 let mut m2 = MemberDescriptor::new(
1162 "u",
1163 1,
1164 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1165 );
1166 m2.index = 1;
1167 m2.label = alloc::vec![2];
1168 b.add_member_resolved(m2, DynamicType::new_primitive(TypeKind::UInt32))
1169 .unwrap();
1170 b.build().expect("build union")
1171 };
1172
1173 let u = build();
1175 let mut d = DynamicData::new(u.clone());
1176 d.set_int32_value(0, -42).unwrap();
1177 let bytes = encode_dynamic(&d, true, false).expect("encode a");
1178 let back = decode_dynamic(&u, &bytes, true, false).expect("decode a");
1179 assert_eq!(back.get_int32_value(0).unwrap(), -42);
1180 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1181
1182 let mut d2 = DynamicData::new(u.clone());
1184 d2.set_uint32_value(1, 0xDEAD_BEEF).unwrap();
1185 let bytes2 = encode_dynamic(&d2, true, false).expect("encode u");
1186 let back2 = decode_dynamic(&u, &bytes2, true, false).expect("decode u");
1187 assert_eq!(back2.get_uint32_value(1).unwrap(), 0xDEAD_BEEF);
1188 assert_eq!(encode_dynamic(&back2, true, false).unwrap(), bytes2);
1189 }
1190
1191 #[test]
1195 fn union_int16_discriminator_is_two_bytes() {
1196 let mut ud =
1197 TypeDescriptor::union("U16", TypeDescriptor::primitive(TypeKind::Int16, "int16"));
1198 ud.extensibility_kind = ExtensibilityKind::Final;
1199 let mut b = DynamicTypeBuilderFactory::create_type(ud).expect("union");
1200 let mut m =
1201 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int8, "int8"));
1202 m.index = 0;
1203 m.label = alloc::vec![10];
1204 b.add_member_resolved(m, DynamicType::new_primitive(TypeKind::Int8))
1205 .unwrap();
1206 let u = b.build().expect("build");
1207
1208 let mut d = DynamicData::new(u.clone());
1209 d.set_int8_value(0, 7).unwrap();
1210 let bytes = encode_dynamic(&d, true, false).expect("encode");
1211 assert_eq!(
1213 bytes,
1214 alloc::vec![0x0a, 0x00, 0x07],
1215 "discriminator must be 2 bytes"
1216 );
1217 let back = decode_dynamic(&u, &bytes, true, false).expect("decode");
1218 assert_eq!(back.get_int8_value(0).unwrap(), 7);
1219 }
1220
1221 fn scalar_i32(v: i32) -> DynamicData {
1224 let mut d = DynamicData::new(DynamicType::new_primitive(TypeKind::Int32));
1225 d.set_value_raw(0, DynamicValue::Int32(v));
1226 d
1227 }
1228
1229 #[test]
1231 fn roundtrip_seq_int32() {
1232 let mut desc = TypeDescriptor::structure("S");
1233 desc.extensibility_kind = ExtensibilityKind::Final;
1234 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1235 let seq = TypeDescriptor::sequence(
1236 "seq",
1237 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1238 0,
1239 );
1240 b.add_struct_member("xs", 0, seq).unwrap();
1241 let ty = b.build().unwrap();
1242
1243 let mut d = DynamicData::new(ty.clone());
1244 let items = alloc::vec![scalar_i32(11), scalar_i32(-22), scalar_i32(33),];
1245 d.set_sequence_value(0, items).unwrap();
1246 let bytes = encode_dynamic(&d, true, false).expect("encode");
1247 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1248 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1249 panic!("seq")
1250 };
1251 let got: alloc::vec::Vec<i32> = v.iter().map(|e| e.get_int32_value(0).unwrap()).collect();
1252 assert_eq!(got, alloc::vec![11, -22, 33]);
1253 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1254 }
1255
1256 #[test]
1258 fn roundtrip_seq_string() {
1259 let mut desc = TypeDescriptor::structure("S");
1260 desc.extensibility_kind = ExtensibilityKind::Final;
1261 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1262 let strd = DynamicTypeBuilderFactory::create_string_type(255)
1263 .descriptor()
1264 .clone();
1265 let seq = TypeDescriptor::sequence("seq", strd, 0);
1266 b.add_struct_member("ss", 0, seq).unwrap();
1267 let ty = b.build().unwrap();
1268
1269 let mut d = DynamicData::new(ty.clone());
1270 let mk = |s: &str| {
1271 let mut e = DynamicData::new(DynamicTypeBuilderFactory::create_string_type(255));
1272 e.set_value_raw(0, DynamicValue::String(s.into()));
1273 e
1274 };
1275 d.set_sequence_value(0, alloc::vec![mk("a"), mk("bb"), mk("ccc")])
1276 .unwrap();
1277 let bytes = encode_dynamic(&d, true, false).expect("encode");
1278 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1279 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1280 panic!("seq")
1281 };
1282 let got: alloc::vec::Vec<alloc::string::String> =
1283 v.iter().map(|e| e.get_string_value(0).unwrap()).collect();
1284 assert_eq!(
1285 got,
1286 alloc::vec!["a".to_string(), "bb".to_string(), "ccc".to_string()]
1287 );
1288 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1289 }
1290
1291 #[test]
1293 fn roundtrip_array_int32() {
1294 let mut desc = TypeDescriptor::structure("S");
1295 desc.extensibility_kind = ExtensibilityKind::Final;
1296 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1297 let arr = TypeDescriptor::array(
1298 "arr",
1299 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1300 alloc::vec![3],
1301 );
1302 b.add_struct_member("a", 0, arr).unwrap();
1303 let ty = b.build().unwrap();
1304
1305 let mut d = DynamicData::new(ty.clone());
1306 let items = alloc::vec![scalar_i32(7), scalar_i32(8), scalar_i32(9),];
1307 d.set_sequence_value(0, items).unwrap();
1308 let bytes = encode_dynamic(&d, true, false).expect("encode");
1309 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1310 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1311 panic!("arr")
1312 };
1313 let got: alloc::vec::Vec<i32> = v.iter().map(|e| e.get_int32_value(0).unwrap()).collect();
1314 assert_eq!(got, alloc::vec![7, 8, 9]);
1315 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1316 }
1317
1318 #[test]
1322 fn roundtrip_seq_of_struct() {
1323 use crate::dynamic::collection;
1324 let inner = sample_type(ExtensibilityKind::Final);
1325 let seq_ty = collection::sequence_of(inner.clone(), 0);
1326 let mut desc = TypeDescriptor::structure("S");
1327 desc.extensibility_kind = ExtensibilityKind::Final;
1328 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1329 let mut md = MemberDescriptor::new("xs", 0, seq_ty.descriptor().clone());
1330 md.index = 0;
1331 b.add_member_resolved(md, seq_ty).unwrap();
1332 let ty = b.build().unwrap();
1333
1334 let mut d = DynamicData::new(ty.clone());
1335 let e0 = populate(&inner);
1336 let mut e1 = DynamicData::new(inner.clone());
1337 e1.set_int32_value(0, 123).unwrap();
1338 e1.set_uint64_value(1, 7).unwrap();
1339 e1.set_string_value(2, "second").unwrap();
1340 e1.set_float64_value(3, -1.25).unwrap();
1341 d.set_sequence_value(0, alloc::vec![e0, e1]).unwrap();
1342
1343 for xcdr2 in [true, false] {
1344 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1345 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1346 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1347 panic!("seq")
1348 };
1349 assert_eq!(v.len(), 2);
1350 assert_eq!(v[0].dynamic_type().kind(), TypeKind::Structure);
1352 assert_eq!(v[0].get_string_value(2).unwrap(), "hello-dynamic");
1353 assert_eq!(v[1].get_int32_value(0).unwrap(), 123);
1354 assert_eq!(v[1].get_string_value(2).unwrap(), "second");
1355 assert_eq!(
1356 encode_dynamic(&back, xcdr2, false).unwrap(),
1357 bytes,
1358 "byte-exact seq<struct> xcdr2={xcdr2}"
1359 );
1360 }
1361 }
1362
1363 #[test]
1365 fn roundtrip_array_of_struct() {
1366 use crate::dynamic::collection;
1367 let inner = sample_type(ExtensibilityKind::Final);
1368 let arr_ty = collection::array_of(inner.clone(), alloc::vec![2], "arr");
1369 let mut desc = TypeDescriptor::structure("S");
1370 desc.extensibility_kind = ExtensibilityKind::Final;
1371 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1372 let mut md = MemberDescriptor::new("a", 0, arr_ty.descriptor().clone());
1373 md.index = 0;
1374 b.add_member_resolved(md, arr_ty).unwrap();
1375 let ty = b.build().unwrap();
1376
1377 let mut d = DynamicData::new(ty.clone());
1378 let mut e0 = DynamicData::new(inner.clone());
1379 e0.set_int32_value(0, 1).unwrap();
1380 e0.set_uint64_value(1, 1).unwrap();
1381 e0.set_string_value(2, "x").unwrap();
1382 e0.set_float64_value(3, 1.0).unwrap();
1383 let mut e1 = DynamicData::new(inner.clone());
1384 e1.set_int32_value(0, 2).unwrap();
1385 e1.set_uint64_value(1, 2).unwrap();
1386 e1.set_string_value(2, "y").unwrap();
1387 e1.set_float64_value(3, 2.0).unwrap();
1388 d.set_sequence_value(0, alloc::vec![e0, e1]).unwrap();
1389
1390 let bytes = encode_dynamic(&d, true, false).expect("encode");
1391 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1392 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1393 panic!("arr")
1394 };
1395 assert_eq!(v.len(), 2);
1396 assert_eq!(v[0].get_string_value(2).unwrap(), "x");
1397 assert_eq!(v[1].get_int32_value(0).unwrap(), 2);
1398 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1399 }
1400
1401 #[test]
1403 fn roundtrip_wstring() {
1404 let mut desc = TypeDescriptor::structure("W");
1405 desc.extensibility_kind = ExtensibilityKind::Final;
1406 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1407 let wdesc = DynamicTypeBuilderFactory::create_wstring_type(0)
1408 .descriptor()
1409 .clone();
1410 let mut md = MemberDescriptor::new("w", 0, wdesc);
1411 md.index = 0;
1412 b.add_member_resolved(md, DynamicTypeBuilderFactory::create_wstring_type(0))
1413 .unwrap();
1414 let ty = b.build().unwrap();
1415
1416 let mut d = DynamicData::new(ty.clone());
1417 let units: alloc::vec::Vec<u16> = "wïde→".encode_utf16().collect();
1418 d.set_value_raw(0, DynamicValue::WString(units.clone()));
1419 for xcdr2 in [true, false] {
1420 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1421 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1422 let DynamicValue::WString(got) = back.get_value(0).unwrap() else {
1423 panic!("wstring")
1424 };
1425 assert_eq!(*got, units);
1426 assert_eq!(encode_dynamic(&back, xcdr2, false).unwrap(), bytes);
1427 }
1428 }
1429
1430 #[test]
1432 fn roundtrip_nested_struct() {
1433 let inner = sample_type(ExtensibilityKind::Final);
1434 let mut desc = TypeDescriptor::structure("Outer");
1435 desc.extensibility_kind = ExtensibilityKind::Final;
1436 let mut b = DynamicTypeBuilderFactory::create_type(desc).expect("builder");
1437 b.add_struct_member(
1438 "id",
1439 0,
1440 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1441 )
1442 .unwrap();
1443 let mut md = MemberDescriptor::new("inner", 1, inner.descriptor().clone());
1444 md.index = 1;
1445 b.add_member_resolved(md, inner.clone()).unwrap();
1446 let outer = b.build().expect("build outer");
1447
1448 let mut d = DynamicData::new(outer.clone());
1449 d.set_uint32_value(0, 99).unwrap();
1450 d.set_complex_value(1, populate(&inner)).unwrap();
1451
1452 let bytes = encode_dynamic(&d, true, false).expect("encode");
1453 let back = decode_dynamic(&outer, &bytes, true, false).expect("decode");
1454 assert_eq!(back.get_uint32_value(0).unwrap(), 99);
1455 let inner_back = back.get_complex_value(1).unwrap();
1456 assert_eq!(inner_back.get_string_value(2).unwrap(), "hello-dynamic");
1457 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1458 }
1459}