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 alias_target(ty: &DynamicType) -> R<DynamicType> {
127 let desc = ty.descriptor();
128 desc.base_type
129 .as_ref()
130 .or(desc.element_type.as_ref())
131 .and_then(|b| scalar_dynamic_type(b))
132 .ok_or_else(|| {
133 CodecError::NotSupported(
134 "alias target: composite typedefs are resolved transparently by the bridge; \
135 a bare composite alias node reaching the codec is unsupported"
136 .to_string(),
137 )
138 })
139}
140
141fn scalar_dynamic_type(desc: &crate::dynamic::descriptor::TypeDescriptor) -> Option<DynamicType> {
142 use crate::dynamic::builder::DynamicTypeBuilderFactory as F;
143 match desc.kind {
144 TypeKind::String8 => Some(F::create_string_type(
145 desc.bound.first().copied().unwrap_or(0),
146 )),
147 TypeKind::String16 => Some(F::create_wstring_type(
148 desc.bound.first().copied().unwrap_or(0),
149 )),
150 k if is_primitive_kind(k) => Some(DynamicType::new_primitive(k)),
151 _ => None,
152 }
153}
154
155const DISCRIMINATOR_MEMBER_ID: u32 = 0;
158
159fn read_discriminator(r: &mut BufferReader<'_>, kind: TypeKind) -> R<i64> {
163 let w = |e: DecodeError| CodecError::Wire(e.to_string());
164 Ok(match kind {
165 TypeKind::Boolean | TypeKind::Byte | TypeKind::UInt8 | TypeKind::Char8 => {
166 i64::from(r.read_u8().map_err(w)?)
167 }
168 TypeKind::Int8 => i64::from(r.read_u8().map_err(w)? as i8),
169 TypeKind::UInt16 | TypeKind::Char16 => i64::from(r.read_u16().map_err(w)?),
170 TypeKind::Int16 => i64::from(r.read_u16().map_err(w)? as i16),
171 TypeKind::UInt32 | TypeKind::Enumeration => i64::from(r.read_u32().map_err(w)?),
172 TypeKind::Int32 => i64::from(r.read_u32().map_err(w)? as i32),
173 TypeKind::UInt64 => r.read_u64().map_err(w)? as i64,
174 TypeKind::Int64 => r.read_u64().map_err(w)? as i64,
175 other => {
176 return Err(CodecError::NotSupported(alloc::format!(
177 "union discriminator kind {other:?}"
178 )));
179 }
180 })
181}
182
183fn write_discriminator(w: &mut BufferWriter, kind: TypeKind, val: i64) -> R<()> {
185 let e = |x: EncodeError| CodecError::Wire(x.to_string());
186 match kind {
187 TypeKind::Boolean | TypeKind::Byte | TypeKind::UInt8 | TypeKind::Char8 | TypeKind::Int8 => {
188 w.write_u8(val as u8).map_err(e)
189 }
190 TypeKind::UInt16 | TypeKind::Char16 | TypeKind::Int16 => w.write_u16(val as u16).map_err(e),
191 TypeKind::UInt32 | TypeKind::Enumeration | TypeKind::Int32 => {
192 w.write_u32(val as u32).map_err(e)
193 }
194 TypeKind::UInt64 | TypeKind::Int64 => w.write_u64(val as u64).map_err(e),
195 other => Err(CodecError::NotSupported(alloc::format!(
196 "union discriminator kind {other:?}"
197 ))),
198 }
199}
200
201fn union_discriminator_kind(ty: &DynamicType) -> TypeKind {
204 ty.descriptor()
205 .discriminator_type
206 .as_ref()
207 .map_or(TypeKind::Int32, |d| d.kind)
208}
209
210pub fn decode_dynamic(
222 ty: &DynamicType,
223 bytes: &[u8],
224 xcdr2: bool,
225 big_endian: bool,
226) -> R<DynamicData> {
227 let mut r = BufferReader::new(bytes, endian(big_endian));
228 if xcdr2 {
229 r = r.xcdr2();
230 }
231 decode_aggregate(ty, &mut r, xcdr2)
232}
233
234fn decode_aggregate(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
235 match ty.kind() {
236 TypeKind::Structure => decode_struct(ty, r, xcdr2),
237 TypeKind::Union => decode_union(ty, r, xcdr2),
238 other => Err(CodecError::NotSupported(alloc::format!(
239 "top-level type kind {other:?} is not an aggregate"
240 ))),
241 }
242}
243
244fn decode_struct(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
245 match ty.descriptor().extensibility_kind {
246 ExtensibilityKind::Final => {
247 let mut data = DynamicData::new(ty.clone());
249 read_members_in_order(ty, r, xcdr2, &mut data)?;
250 Ok(data)
251 }
252 ExtensibilityKind::Appendable => {
253 if !xcdr2 {
255 let mut data = DynamicData::new(ty.clone());
256 read_members_in_order(ty, r, xcdr2, &mut data)?;
257 return Ok(data);
258 }
259 let mut inner: Option<CodecError> = None;
260 let res = decode_appendable(r, |sub| {
261 let mut data = DynamicData::new(ty.clone());
262 match read_members_in_order(ty, sub, xcdr2, &mut data) {
263 Ok(()) => Ok(data),
264 Err(e) => {
265 inner = Some(e);
266 Err(dec_sentinel())
267 }
268 }
269 });
270 match res {
271 Ok(d) => Ok(d),
272 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
273 }
274 }
275 ExtensibilityKind::Mutable => {
276 if xcdr2 {
277 decode_struct_mutable(ty, r, xcdr2)
278 } else {
279 decode_struct_pl_cdr1(ty, r)
280 }
281 }
282 }
283}
284
285fn read_members_in_order(
286 ty: &DynamicType,
287 r: &mut BufferReader<'_>,
288 xcdr2: bool,
289 data: &mut DynamicData,
290) -> R<()> {
291 for i in 0..ty.member_count() {
292 let m = ty
293 .member_by_index(i)
294 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
295 let mt = m.dynamic_type().clone();
296 let id = m.id();
297 let val = read_value(&mt, r, xcdr2)?;
298 data.set_value_raw(id, val);
299 }
300 Ok(())
301}
302
303fn decode_struct_mutable(
304 ty: &DynamicType,
305 r: &mut BufferReader<'_>,
306 xcdr2: bool,
307) -> R<DynamicData> {
308 let mut err: Option<CodecError> = None;
314 let res = decode_appendable(r, |sub| {
315 let members = read_all_mutable_members(sub)?;
316 let mut data = DynamicData::new(ty.clone());
317 for mm in members {
318 let Some(m) = ty.member_by_id(mm.member_id) else {
319 continue; };
321 let mt = m.dynamic_type().clone();
322 let mut bsub = BufferReader::new(mm.body, sub.endianness());
323 if xcdr2 {
324 bsub = bsub.xcdr2();
325 }
326 match read_value(&mt, &mut bsub, xcdr2) {
327 Ok(val) => data.set_value_raw(m.id(), val),
328 Err(e) => {
329 err = Some(e);
330 return Err(dec_sentinel());
331 }
332 }
333 }
334 Ok(data)
335 });
336 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
337}
338
339fn decode_struct_pl_cdr1(ty: &DynamicType, r: &mut BufferReader<'_>) -> R<DynamicData> {
343 let members = read_all_pl_cdr1_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
344 let mut data = DynamicData::new(ty.clone());
345 for mm in members {
346 let Some(m) = ty.member_by_id(mm.member_id) else {
347 continue; };
349 let mt = m.dynamic_type().clone();
350 let mut bsub = BufferReader::new(&mm.body, r.endianness());
351 let val = read_value(&mt, &mut bsub, false)?;
352 data.set_value_raw(m.id(), val);
353 }
354 Ok(data)
355}
356
357fn decode_union_pl_cdr1(ty: &DynamicType, r: &mut BufferReader<'_>) -> R<DynamicData> {
360 let members = read_all_pl_cdr1_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
361 let mut data = DynamicData::new(ty.clone());
362 let disc_idx = members
363 .iter()
364 .position(|m| m.member_id == DISCRIMINATOR_MEMBER_ID)
365 .ok_or_else(|| {
366 CodecError::Wire("mutable union (PL_CDR1): no discriminator member id 0".to_string())
367 })?;
368 let mut dsub = BufferReader::new(&members[disc_idx].body, r.endianness());
369 let disc = read_discriminator(&mut dsub, union_discriminator_kind(ty))?;
370
371 let mut chosen = None;
372 let mut default = None;
373 for m in ty.members() {
374 let d = m.descriptor();
375 if d.is_default_label {
376 default = Some(m);
377 }
378 if d.label.contains(&disc) {
379 chosen = Some(m);
380 break;
381 }
382 }
383 if let Some(m) = chosen.or(default) {
384 if let Some(bw) = members
385 .iter()
386 .enumerate()
387 .find(|(i, _)| *i != disc_idx)
388 .map(|(_, w)| w)
389 {
390 let mt = m.dynamic_type().clone();
391 let mut bsub = BufferReader::new(&bw.body, r.endianness());
392 let val = read_value(&mt, &mut bsub, false)?;
393 data.set_value_raw(m.id(), val);
394 }
395 }
396 Ok(data)
397}
398
399fn decode_union(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
400 match ty.descriptor().extensibility_kind {
401 ExtensibilityKind::Final => decode_union_body(ty, r, xcdr2),
402 ExtensibilityKind::Appendable => {
403 if !xcdr2 {
405 return decode_union_body(ty, r, xcdr2);
406 }
407 let mut inner: Option<CodecError> = None;
408 let res = decode_appendable(r, |sub| match decode_union_body(ty, sub, xcdr2) {
409 Ok(d) => Ok(d),
410 Err(e) => {
411 inner = Some(e);
412 Err(dec_sentinel())
413 }
414 });
415 match res {
416 Ok(d) => Ok(d),
417 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
418 }
419 }
420 ExtensibilityKind::Mutable => {
421 if xcdr2 {
422 decode_union_mutable(ty, r, xcdr2)
423 } else {
424 decode_union_pl_cdr1(ty, r)
425 }
426 }
427 }
428}
429
430fn decode_union_mutable(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
438 let mut err: Option<CodecError> = None;
442 let res = decode_appendable(r, |sub| match decode_union_mutable_body(ty, sub, xcdr2) {
443 Ok(d) => Ok(d),
444 Err(e) => {
445 err = Some(e);
446 Err(dec_sentinel())
447 }
448 });
449 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
450}
451
452fn decode_union_mutable_body(
454 ty: &DynamicType,
455 r: &mut BufferReader<'_>,
456 xcdr2: bool,
457) -> R<DynamicData> {
458 let members = read_all_mutable_members(r).map_err(|e| CodecError::Wire(e.to_string()))?;
459 let mut data = DynamicData::new(ty.clone());
460
461 let disc_idx = members
465 .iter()
466 .position(|m| m.member_id == DISCRIMINATOR_MEMBER_ID)
467 .ok_or_else(|| {
468 CodecError::Wire(
469 "mutable union: no discriminator member (id 0) on the wire".to_string(),
470 )
471 })?;
472 let mut dsub = BufferReader::new(members[disc_idx].body, r.endianness());
473 if xcdr2 {
474 dsub = dsub.xcdr2();
475 }
476 let disc = read_discriminator(&mut dsub, union_discriminator_kind(ty))?;
477
478 let mut chosen = None;
479 let mut default = None;
480 for m in ty.members() {
481 let d = m.descriptor();
482 if d.is_default_label {
483 default = Some(m);
484 }
485 if d.label.contains(&disc) {
486 chosen = Some(m);
487 break;
488 }
489 }
490 if let Some(m) = chosen.or(default) {
491 if let Some(bw) = members
499 .iter()
500 .enumerate()
501 .find(|(i, _)| *i != disc_idx)
502 .map(|(_, w)| w)
503 {
504 let mt = m.dynamic_type().clone();
505 let mut bsub = BufferReader::new(bw.body, r.endianness());
506 if xcdr2 {
507 bsub = bsub.xcdr2();
508 }
509 let val = read_value(&mt, &mut bsub, xcdr2)?;
510 data.set_value_raw(m.id(), val);
511 }
512 }
513 Ok(data)
514}
515
516fn decode_union_body(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
517 let disc = read_discriminator(r, union_discriminator_kind(ty))?;
518 let mut data = DynamicData::new(ty.clone());
519 let mut chosen = None;
520 let mut default = None;
521 for m in ty.members() {
522 let d = m.descriptor();
523 if d.is_default_label {
524 default = Some(m);
525 }
526 if d.label.contains(&disc) {
527 chosen = Some(m);
528 break;
529 }
530 }
531 if let Some(m) = chosen.or(default) {
532 let mt = m.dynamic_type().clone();
533 let val = read_value(&mt, r, xcdr2)?;
534 data.set_value_raw(m.id(), val);
535 }
536 Ok(data)
537}
538
539fn read_value(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicValue> {
541 let w = |e: DecodeError| CodecError::Wire(e.to_string());
542 Ok(match ty.kind() {
543 TypeKind::Boolean => DynamicValue::Bool(r.read_u8().map_err(w)? != 0),
544 TypeKind::Byte => DynamicValue::Byte(r.read_u8().map_err(w)?),
545 TypeKind::UInt8 => DynamicValue::UInt8(r.read_u8().map_err(w)?),
546 TypeKind::Int8 => DynamicValue::Int8(r.read_u8().map_err(w)? as i8),
547 TypeKind::Int16 => DynamicValue::Int16(r.read_u16().map_err(w)? as i16),
548 TypeKind::UInt16 => DynamicValue::UInt16(r.read_u16().map_err(w)?),
549 TypeKind::Int32 => DynamicValue::Int32(r.read_u32().map_err(w)? as i32),
550 TypeKind::UInt32 => DynamicValue::UInt32(r.read_u32().map_err(w)?),
551 TypeKind::Enumeration => DynamicValue::Int32(r.read_u32().map_err(w)? as i32),
552 TypeKind::Int64 => DynamicValue::Int64(r.read_u64().map_err(w)? as i64),
553 TypeKind::UInt64 => DynamicValue::UInt64(r.read_u64().map_err(w)?),
554 TypeKind::Float32 => DynamicValue::Float32(f32::from_bits(r.read_u32().map_err(w)?)),
555 TypeKind::Float64 => DynamicValue::Float64(f64::from_bits(r.read_u64().map_err(w)?)),
556 TypeKind::Char8 => DynamicValue::Char8(r.read_u8().map_err(w)?),
557 TypeKind::Char16 => DynamicValue::Char16(r.read_u16().map_err(w)?),
558 TypeKind::String8 => DynamicValue::String(r.read_string().map_err(w)?),
559 TypeKind::String16 => {
560 let ws = WString::decode(r).map_err(w)?;
562 DynamicValue::WString(ws.0.encode_utf16().collect())
563 }
564 TypeKind::Structure | TypeKind::Union => {
565 DynamicValue::Complex(Box::new(decode_aggregate(ty, r, xcdr2)?))
566 }
567 TypeKind::Sequence => read_collection(ty, r, xcdr2, None)?,
568 TypeKind::Array => {
569 let n: usize = ty.descriptor().bound.iter().copied().product::<u32>() as usize;
570 read_collection(ty, r, xcdr2, Some(n))?
571 }
572 TypeKind::Map => read_map(ty, r, xcdr2)?,
573 TypeKind::Alias => read_value(&alias_target(ty)?, r, xcdr2)?,
574 other => {
575 return Err(CodecError::NotSupported(alloc::format!(
576 "member kind {other:?}"
577 )));
578 }
579 })
580}
581
582fn read_collection(
593 ty: &DynamicType,
594 r: &mut BufferReader<'_>,
595 xcdr2: bool,
596 fixed_len: Option<usize>,
597) -> R<DynamicValue> {
598 let desc = ty.descriptor();
599 let elem_desc = desc
600 .element_type
601 .as_ref()
602 .ok_or_else(|| CodecError::Dynamic("collection has no element_type".to_string()))?;
603 let elem_kind = elem_desc.kind;
604 let elem_ty = element_type(ty, elem_desc)?;
605 let w = |e: DecodeError| CodecError::Wire(e.to_string());
606 if xcdr2 && !is_primitive_kind(elem_kind) {
607 let _dheader = r.read_u32().map_err(w)?; }
609 let count = match fixed_len {
610 Some(n) => n,
611 None => r.read_u32().map_err(w)? as usize,
612 };
613 let mut out = Vec::with_capacity(count.min(4096));
614 for _ in 0..count {
615 out.push(read_element(&elem_ty, r, xcdr2)?);
616 }
617 Ok(DynamicValue::Sequence(out))
618}
619
620fn read_element(elem_ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicData> {
624 Ok(match read_value(elem_ty, r, xcdr2)? {
625 DynamicValue::Complex(d) => *d,
626 scalar => {
627 let mut d = DynamicData::new(elem_ty.clone());
628 d.set_value_raw(0, scalar);
629 d
630 }
631 })
632}
633
634fn read_map(ty: &DynamicType, r: &mut BufferReader<'_>, xcdr2: bool) -> R<DynamicValue> {
642 let (key_ty, val_ty, pair_primitive) = map_kv_types(ty)?;
643 let w = |e: DecodeError| CodecError::Wire(e.to_string());
644 if xcdr2 && !pair_primitive {
645 let _dheader = r.read_u32().map_err(w)?;
646 }
647 let count = r.read_u32().map_err(w)? as usize;
648 let mut out = Vec::with_capacity(count.min(4096));
649 for _ in 0..count {
650 let k = read_element(&key_ty, r, xcdr2)?;
651 let v = read_element(&val_ty, r, xcdr2)?;
652 out.push((k, v));
653 }
654 Ok(DynamicValue::Map(out))
655}
656
657fn map_kv_types(ty: &DynamicType) -> R<(DynamicType, DynamicType, bool)> {
661 let desc = ty.descriptor();
662 let key_desc = desc
663 .key_element_type
664 .as_ref()
665 .ok_or_else(|| CodecError::Dynamic("map has no key type".to_string()))?;
666 let val_desc = desc
667 .element_type
668 .as_ref()
669 .ok_or_else(|| CodecError::Dynamic("map has no value type".to_string()))?;
670 let key_ty = crate::dynamic::collection::resolved_map_key(ty)
671 .cloned()
672 .or_else(|| scalar_dynamic_type(key_desc))
673 .ok_or_else(|| CodecError::NotSupported("map key (no resolved type)".to_string()))?;
674 let val_ty = crate::dynamic::collection::resolved_map_value(ty)
675 .cloned()
676 .or_else(|| scalar_dynamic_type(val_desc))
677 .ok_or_else(|| CodecError::NotSupported("map value (no resolved type)".to_string()))?;
678 let pair_primitive = is_primitive_kind(key_desc.kind) && is_primitive_kind(val_desc.kind);
679 Ok((key_ty, val_ty, pair_primitive))
680}
681
682fn element_type(
686 ty: &DynamicType,
687 elem_desc: &crate::dynamic::descriptor::TypeDescriptor,
688) -> R<DynamicType> {
689 crate::dynamic::collection::resolved_element(ty)
690 .cloned()
691 .or_else(|| scalar_dynamic_type(elem_desc))
692 .ok_or_else(|| {
693 CodecError::NotSupported(alloc::format!(
694 "collection of {:?} (no resolved element type)",
695 elem_desc.kind
696 ))
697 })
698}
699
700fn write_collection(
705 items: &[DynamicData],
706 ty: &DynamicType,
707 w: &mut BufferWriter,
708 xcdr2: bool,
709) -> R<()> {
710 let desc = ty.descriptor();
711 let elem_desc = desc
712 .element_type
713 .as_ref()
714 .ok_or_else(|| CodecError::Dynamic("collection has no element_type".to_string()))?;
715 let elem_kind = elem_desc.kind;
716 let elem_ty = element_type(ty, elem_desc)?;
717 let is_array = ty.kind() == TypeKind::Array;
718 let e = |x: EncodeError| CodecError::Wire(x.to_string());
719
720 let write_body = |w: &mut BufferWriter| -> R<()> {
721 if !is_array {
722 w.write_u32(items.len() as u32).map_err(e)?;
723 }
724 for d in items {
725 write_element(d, &elem_ty, w, xcdr2)?;
726 }
727 Ok(())
728 };
729
730 if xcdr2 && !is_primitive_kind(elem_kind) {
731 let mut sub = BufferWriter::new(w.endianness());
733 if xcdr2 {
734 sub = sub.xcdr2();
735 }
736 write_body(&mut sub)?;
737 let body = sub.into_bytes();
738 w.write_u32(body.len() as u32).map_err(e)?;
739 w.write_bytes(&body).map_err(e)?;
740 Ok(())
741 } else {
742 write_body(w)
743 }
744}
745
746fn write_element(
750 d: &DynamicData,
751 elem_ty: &DynamicType,
752 w: &mut BufferWriter,
753 xcdr2: bool,
754) -> R<()> {
755 if matches!(elem_ty.kind(), TypeKind::Structure | TypeKind::Union) {
756 write_aggregate(d, w, xcdr2)
757 } else {
758 let v = d
759 .get_value(0)
760 .ok_or_else(|| CodecError::Dynamic("scalar element unset".to_string()))?;
761 write_value(&v.clone(), elem_ty, w, xcdr2)
762 }
763}
764
765fn write_map(
770 entries: &[(DynamicData, DynamicData)],
771 ty: &DynamicType,
772 w: &mut BufferWriter,
773 xcdr2: bool,
774) -> R<()> {
775 let (key_ty, val_ty, pair_primitive) = map_kv_types(ty)?;
776 let e = |x: EncodeError| CodecError::Wire(x.to_string());
777 let write_body = |w: &mut BufferWriter| -> R<()> {
778 w.write_u32(entries.len() as u32).map_err(e)?;
779 for (k, v) in entries {
780 write_element(k, &key_ty, w, xcdr2)?;
781 write_element(v, &val_ty, w, xcdr2)?;
782 }
783 Ok(())
784 };
785 if xcdr2 && !pair_primitive {
786 let mut sub = BufferWriter::new(w.endianness());
787 sub = sub.xcdr2();
788 write_body(&mut sub)?;
789 let body = sub.into_bytes();
790 w.write_u32(body.len() as u32).map_err(e)?;
791 w.write_bytes(&body).map_err(e)?;
792 Ok(())
793 } else {
794 write_body(w)
795 }
796}
797
798pub fn encode_dynamic(data: &DynamicData, xcdr2: bool, big_endian: bool) -> R<Vec<u8>> {
808 let mut w = BufferWriter::new(endian(big_endian));
809 if xcdr2 {
810 w = w.xcdr2();
811 }
812 write_aggregate(data, &mut w, xcdr2)?;
813 Ok(w.into_bytes())
814}
815
816fn write_aggregate(data: &DynamicData, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
817 let ty = data.dynamic_type().clone();
818 match ty.kind() {
819 TypeKind::Structure => write_struct(data, &ty, w, xcdr2),
820 TypeKind::Union => write_union(data, &ty, w, xcdr2),
821 other => Err(CodecError::NotSupported(alloc::format!(
822 "top-level type kind {other:?} is not an aggregate"
823 ))),
824 }
825}
826
827fn write_struct(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
828 match ty.descriptor().extensibility_kind {
829 ExtensibilityKind::Final => write_members_in_order(data, ty, w, xcdr2),
830 ExtensibilityKind::Appendable => {
831 if !xcdr2 {
834 return write_members_in_order(data, ty, w, xcdr2);
835 }
836 let mut inner: Option<CodecError> = None;
837 let res = encode_appendable(w, |sub| {
838 match write_members_in_order(data, ty, sub, xcdr2) {
839 Ok(()) => Ok(()),
840 Err(e) => {
841 inner = Some(e);
842 Err(enc_sentinel())
843 }
844 }
845 });
846 match res {
847 Ok(()) => Ok(()),
848 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
849 }
850 }
851 ExtensibilityKind::Mutable => {
852 if !xcdr2 {
855 return write_struct_pl_cdr1(data, ty, w);
856 }
857 let mut outer: Option<CodecError> = None;
861 let res = encode_appendable(w, |fw| {
862 for i in 0..ty.member_count() {
863 let m = match ty.member_by_index(i).ok_or_else(|| {
864 CodecError::Dynamic(alloc::format!("missing member index {i}"))
865 }) {
866 Ok(m) => m,
867 Err(e) => {
868 outer = Some(e);
869 return Err(enc_sentinel());
870 }
871 };
872 let id = m.id();
873 let Some(val) = data.get_value(id).cloned() else {
874 continue;
875 };
876 let mt = m.dynamic_type().clone();
877 let mu = m.descriptor().is_must_understand;
878 let res = encode_mutable_member(fw, id, mu, |sub| {
879 match write_value(&val, &mt, sub, xcdr2) {
880 Ok(()) => Ok(()),
881 Err(e) => {
882 outer = Some(e);
883 Err(enc_sentinel())
884 }
885 }
886 });
887 if let Err(e) = res {
888 if outer.is_none() {
889 outer = Some(CodecError::Wire(e.to_string()));
890 }
891 return Err(enc_sentinel());
892 }
893 }
894 Ok(())
895 });
896 res.map_err(|e| outer.unwrap_or_else(|| CodecError::Wire(e.to_string())))
897 }
898 }
899}
900
901fn write_members_in_order(
902 data: &DynamicData,
903 ty: &DynamicType,
904 w: &mut BufferWriter,
905 xcdr2: bool,
906) -> R<()> {
907 for i in 0..ty.member_count() {
908 let m = ty
909 .member_by_index(i)
910 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
911 let id = m.id();
912 let val = data
913 .get_value(id)
914 .ok_or_else(|| CodecError::Dynamic(alloc::format!("member {id} unset on encode")))?
915 .clone();
916 let mt = m.dynamic_type().clone();
917 write_value(&val, &mt, w, xcdr2)?;
918 }
919 Ok(())
920}
921
922fn write_struct_pl_cdr1(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter) -> R<()> {
926 for i in 0..ty.member_count() {
927 let m = ty
928 .member_by_index(i)
929 .ok_or_else(|| CodecError::Dynamic(alloc::format!("missing member index {i}")))?;
930 let id = m.id();
931 let Some(val) = data.get_value(id).cloned() else {
932 continue;
933 };
934 let mt = m.dynamic_type().clone();
935 let mut err: Option<CodecError> = None;
936 let res = encode_pl_cdr1_member(w, id, |sub| match write_value(&val, &mt, sub, false) {
937 Ok(()) => Ok(()),
938 Err(e) => {
939 err = Some(e);
940 Err(enc_sentinel())
941 }
942 });
943 if let Err(e) = res {
944 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
945 }
946 }
947 write_pl_cdr1_sentinel(w).map_err(|e| CodecError::Wire(e.to_string()))
948}
949
950fn write_union_pl_cdr1(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter) -> R<()> {
953 let disc_kind = union_discriminator_kind(ty);
954 let m = ty
955 .members()
956 .find(|m| data.get_value(m.id()).is_some())
957 .ok_or_else(|| CodecError::Dynamic("union has no branch set".to_string()))?;
958 let disc = m.descriptor().label.first().copied().unwrap_or(0);
959 let val = data
960 .get_value(m.id())
961 .cloned()
962 .ok_or_else(|| CodecError::Dynamic("union branch value vanished".to_string()))?;
963 let mt = m.dynamic_type().clone();
964
965 let mut err: Option<CodecError> = None;
966 let r1 = encode_pl_cdr1_member(w, DISCRIMINATOR_MEMBER_ID, |sub| match write_discriminator(
967 sub, disc_kind, disc,
968 ) {
969 Ok(()) => Ok(()),
970 Err(e) => {
971 err = Some(e);
972 Err(enc_sentinel())
973 }
974 });
975 if let Err(e) = r1 {
976 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
977 }
978 let r2 = encode_pl_cdr1_member(w, m.id(), |sub| match write_value(&val, &mt, sub, false) {
979 Ok(()) => Ok(()),
980 Err(e) => {
981 err = Some(e);
982 Err(enc_sentinel())
983 }
984 });
985 if let Err(e) = r2 {
986 return Err(err.unwrap_or_else(|| CodecError::Wire(e.to_string())));
987 }
988 write_pl_cdr1_sentinel(w).map_err(|e| CodecError::Wire(e.to_string()))
989}
990
991fn write_union(data: &DynamicData, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
992 match ty.descriptor().extensibility_kind {
993 ExtensibilityKind::Final => write_union_body(data, ty, w, xcdr2),
994 ExtensibilityKind::Appendable => {
995 if !xcdr2 {
997 return write_union_body(data, ty, w, xcdr2);
998 }
999 let mut inner: Option<CodecError> = None;
1000 let res = encode_appendable(w, |sub| match write_union_body(data, ty, sub, xcdr2) {
1001 Ok(()) => Ok(()),
1002 Err(e) => {
1003 inner = Some(e);
1004 Err(enc_sentinel())
1005 }
1006 });
1007 match res {
1008 Ok(()) => Ok(()),
1009 Err(e) => Err(inner.unwrap_or_else(|| CodecError::Wire(e.to_string()))),
1010 }
1011 }
1012 ExtensibilityKind::Mutable => {
1013 if xcdr2 {
1014 write_union_mutable(data, ty, w, xcdr2)
1015 } else {
1016 write_union_pl_cdr1(data, ty, w)
1017 }
1018 }
1019 }
1020}
1021
1022fn write_union_body(
1023 data: &DynamicData,
1024 ty: &DynamicType,
1025 w: &mut BufferWriter,
1026 xcdr2: bool,
1027) -> R<()> {
1028 let disc_kind = union_discriminator_kind(ty);
1029 for m in ty.members() {
1030 let id = m.id();
1031 if let Some(val) = data.get_value(id).cloned() {
1032 let disc = m.descriptor().label.first().copied().unwrap_or(0);
1033 write_discriminator(w, disc_kind, disc)?;
1034 let mt = m.dynamic_type().clone();
1035 return write_value(&val, &mt, w, xcdr2);
1036 }
1037 }
1038 Err(CodecError::Dynamic("union has no branch set".to_string()))
1039}
1040
1041fn write_union_mutable(
1047 data: &DynamicData,
1048 ty: &DynamicType,
1049 w: &mut BufferWriter,
1050 xcdr2: bool,
1051) -> R<()> {
1052 let disc_kind = union_discriminator_kind(ty);
1053 let m = ty
1054 .members()
1055 .find(|m| data.get_value(m.id()).is_some())
1056 .ok_or_else(|| CodecError::Dynamic("union has no branch set".to_string()))?;
1057 let disc = m.descriptor().label.first().copied().unwrap_or(0);
1058 let val = data
1059 .get_value(m.id())
1060 .cloned()
1061 .ok_or_else(|| CodecError::Dynamic("union branch value vanished".to_string()))?;
1062 let mt = m.dynamic_type().clone();
1063
1064 let mu = m.descriptor().is_must_understand;
1065 let mut err: Option<CodecError> = None;
1069 let res =
1070 encode_appendable(w, |fw| {
1071 let r1 = encode_mutable_member(fw, DISCRIMINATOR_MEMBER_ID, true, |sub| {
1072 match write_discriminator(sub, disc_kind, disc) {
1073 Ok(()) => Ok(()),
1074 Err(e) => {
1075 err = Some(e);
1076 Err(enc_sentinel())
1077 }
1078 }
1079 });
1080 if let Err(e) = r1 {
1081 if err.is_none() {
1082 err = Some(CodecError::Wire(e.to_string()));
1083 }
1084 return Err(enc_sentinel());
1085 }
1086 let r2 = encode_mutable_member(fw, m.id(), mu, |sub| {
1087 match write_value(&val, &mt, sub, xcdr2) {
1088 Ok(()) => Ok(()),
1089 Err(e) => {
1090 err = Some(e);
1091 Err(enc_sentinel())
1092 }
1093 }
1094 });
1095 if let Err(e) = r2 {
1096 if err.is_none() {
1097 err = Some(CodecError::Wire(e.to_string()));
1098 }
1099 return Err(enc_sentinel());
1100 }
1101 Ok(())
1102 });
1103 res.map_err(|e| err.unwrap_or_else(|| CodecError::Wire(e.to_string())))
1104}
1105
1106fn write_value(v: &DynamicValue, ty: &DynamicType, w: &mut BufferWriter, xcdr2: bool) -> R<()> {
1108 let e = |x: EncodeError| CodecError::Wire(x.to_string());
1109 match v {
1110 DynamicValue::Bool(b) => w.write_u8(u8::from(*b)).map_err(e),
1111 DynamicValue::Byte(x) | DynamicValue::UInt8(x) | DynamicValue::Char8(x) => {
1112 w.write_u8(*x).map_err(e)
1113 }
1114 DynamicValue::Int8(x) => w.write_u8(*x as u8).map_err(e),
1115 DynamicValue::Int16(x) => w.write_u16(*x as u16).map_err(e),
1116 DynamicValue::UInt16(x) | DynamicValue::Char16(x) => w.write_u16(*x).map_err(e),
1117 DynamicValue::Int32(x) => w.write_u32(*x as u32).map_err(e),
1118 DynamicValue::UInt32(x) => w.write_u32(*x).map_err(e),
1119 DynamicValue::Int64(x) => w.write_u64(*x as u64).map_err(e),
1120 DynamicValue::UInt64(x) => w.write_u64(*x).map_err(e),
1121 DynamicValue::Float32(x) => w.write_u32(x.to_bits()).map_err(e),
1122 DynamicValue::Float64(x) => w.write_u64(x.to_bits()).map_err(e),
1123 DynamicValue::String(s) => w.write_string(s).map_err(e),
1124 DynamicValue::WString(units) => {
1125 WString(String::from_utf16_lossy(units))
1128 .encode(w)
1129 .map_err(e)
1130 }
1131 DynamicValue::Complex(d) => write_aggregate(d, w, xcdr2),
1132 DynamicValue::Sequence(items) => write_collection(items, ty, w, xcdr2),
1133 DynamicValue::Map(entries) => write_map(entries, ty, w, xcdr2),
1134 DynamicValue::None => Ok(()),
1135 }
1136}
1137
1138#[cfg(test)]
1139#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
1140mod tests {
1141 use super::*;
1142 use crate::dynamic::builder::DynamicTypeBuilderFactory;
1143 use crate::dynamic::descriptor::{MemberDescriptor, TypeDescriptor};
1144
1145 fn sample_type(ext: ExtensibilityKind) -> DynamicType {
1148 let mut desc = TypeDescriptor::structure("Sample");
1149 desc.extensibility_kind = ext;
1150 let mut b = DynamicTypeBuilderFactory::create_type(desc).expect("builder");
1151 b.add_struct_member("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
1152 .unwrap();
1153 b.add_struct_member(
1154 "b",
1155 1,
1156 TypeDescriptor::primitive(TypeKind::UInt64, "uint64"),
1157 )
1158 .unwrap();
1159 let string_desc = DynamicTypeBuilderFactory::create_string_type(255)
1160 .descriptor()
1161 .clone();
1162 b.add_struct_member("s", 2, string_desc).unwrap();
1163 b.add_struct_member(
1164 "f",
1165 3,
1166 TypeDescriptor::primitive(TypeKind::Float64, "float64"),
1167 )
1168 .unwrap();
1169 b.build().expect("build")
1170 }
1171
1172 fn populate(ty: &DynamicType) -> DynamicData {
1173 let mut d = DynamicData::new(ty.clone());
1174 d.set_int32_value(0, -7).unwrap();
1175 d.set_uint64_value(1, 0xDEAD_BEEF_0000_0001).unwrap();
1176 d.set_string_value(2, "hello-dynamic").unwrap();
1177 d.set_float64_value(3, 3.5).unwrap();
1178 d
1179 }
1180
1181 fn roundtrip(ext: ExtensibilityKind, xcdr2: bool) {
1182 let ty = sample_type(ext);
1183 let src = populate(&ty);
1184 let bytes = encode_dynamic(&src, xcdr2, false).expect("encode");
1185 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1186 assert_eq!(back.get_int32_value(0).unwrap(), -7);
1188 assert_eq!(back.get_uint64_value(1).unwrap(), 0xDEAD_BEEF_0000_0001);
1189 assert_eq!(back.get_string_value(2).unwrap(), "hello-dynamic");
1190 assert!((back.get_float64_value(3).unwrap() - 3.5).abs() < f64::EPSILON);
1191 let bytes2 = encode_dynamic(&back, xcdr2, false).expect("re-encode");
1193 assert_eq!(
1194 bytes, bytes2,
1195 "byte-exact round-trip failed for {ext:?} xcdr2={xcdr2}"
1196 );
1197 }
1198
1199 #[test]
1200 fn roundtrip_final_xcdr2() {
1201 roundtrip(ExtensibilityKind::Final, true);
1202 }
1203 #[test]
1204 fn roundtrip_final_xcdr1() {
1205 roundtrip(ExtensibilityKind::Final, false);
1206 }
1207 #[test]
1208 fn roundtrip_appendable_xcdr2() {
1209 roundtrip(ExtensibilityKind::Appendable, true);
1210 }
1211 #[test]
1212 fn roundtrip_mutable_xcdr2() {
1213 roundtrip(ExtensibilityKind::Mutable, true);
1214 }
1215
1216 #[test]
1219 fn roundtrip_union() {
1220 let disc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
1221 let mut b = DynamicTypeBuilderFactory::create_union("U", disc).expect("union builder");
1222 let mut m1 =
1224 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1225 m1.index = 0;
1226 m1.label = alloc::vec![1];
1227 b.add_member_resolved(m1, DynamicType::new_primitive(TypeKind::Int32))
1228 .unwrap();
1229 let mut m2 = MemberDescriptor::new(
1231 "u",
1232 1,
1233 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1234 );
1235 m2.index = 1;
1236 m2.label = alloc::vec![2];
1237 b.add_member_resolved(m2, DynamicType::new_primitive(TypeKind::UInt32))
1238 .unwrap();
1239 let u = b.build().expect("build union");
1240
1241 let mut d = DynamicData::new(u.clone());
1243 d.set_uint32_value(1, 0xABCD).unwrap();
1244 let bytes = encode_dynamic(&d, true, false).expect("encode");
1245 let back = decode_dynamic(&u, &bytes, true, false).expect("decode");
1246 assert_eq!(back.get_uint32_value(1).unwrap(), 0xABCD);
1247 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1248 }
1249
1250 #[test]
1255 fn roundtrip_union_mutable() {
1256 let build = || {
1257 let mut ud =
1258 TypeDescriptor::union("UM", TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1259 ud.extensibility_kind = ExtensibilityKind::Mutable;
1260 let mut b = DynamicTypeBuilderFactory::create_type(ud).expect("union");
1261 let mut m1 =
1262 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
1263 m1.index = 0;
1264 m1.label = alloc::vec![1];
1265 b.add_member_resolved(m1, DynamicType::new_primitive(TypeKind::Int32))
1266 .unwrap();
1267 let mut m2 = MemberDescriptor::new(
1268 "u",
1269 1,
1270 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1271 );
1272 m2.index = 1;
1273 m2.label = alloc::vec![2];
1274 b.add_member_resolved(m2, DynamicType::new_primitive(TypeKind::UInt32))
1275 .unwrap();
1276 b.build().expect("build union")
1277 };
1278
1279 let u = build();
1281 let mut d = DynamicData::new(u.clone());
1282 d.set_int32_value(0, -42).unwrap();
1283 let bytes = encode_dynamic(&d, true, false).expect("encode a");
1284 let back = decode_dynamic(&u, &bytes, true, false).expect("decode a");
1285 assert_eq!(back.get_int32_value(0).unwrap(), -42);
1286 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1287
1288 let mut d2 = DynamicData::new(u.clone());
1290 d2.set_uint32_value(1, 0xDEAD_BEEF).unwrap();
1291 let bytes2 = encode_dynamic(&d2, true, false).expect("encode u");
1292 let back2 = decode_dynamic(&u, &bytes2, true, false).expect("decode u");
1293 assert_eq!(back2.get_uint32_value(1).unwrap(), 0xDEAD_BEEF);
1294 assert_eq!(encode_dynamic(&back2, true, false).unwrap(), bytes2);
1295 }
1296
1297 #[test]
1301 fn union_int16_discriminator_is_two_bytes() {
1302 let mut ud =
1303 TypeDescriptor::union("U16", TypeDescriptor::primitive(TypeKind::Int16, "int16"));
1304 ud.extensibility_kind = ExtensibilityKind::Final;
1305 let mut b = DynamicTypeBuilderFactory::create_type(ud).expect("union");
1306 let mut m =
1307 MemberDescriptor::new("a", 0, TypeDescriptor::primitive(TypeKind::Int8, "int8"));
1308 m.index = 0;
1309 m.label = alloc::vec![10];
1310 b.add_member_resolved(m, DynamicType::new_primitive(TypeKind::Int8))
1311 .unwrap();
1312 let u = b.build().expect("build");
1313
1314 let mut d = DynamicData::new(u.clone());
1315 d.set_int8_value(0, 7).unwrap();
1316 let bytes = encode_dynamic(&d, true, false).expect("encode");
1317 assert_eq!(
1319 bytes,
1320 alloc::vec![0x0a, 0x00, 0x07],
1321 "discriminator must be 2 bytes"
1322 );
1323 let back = decode_dynamic(&u, &bytes, true, false).expect("decode");
1324 assert_eq!(back.get_int8_value(0).unwrap(), 7);
1325 }
1326
1327 fn scalar_i32(v: i32) -> DynamicData {
1330 let mut d = DynamicData::new(DynamicType::new_primitive(TypeKind::Int32));
1331 d.set_value_raw(0, DynamicValue::Int32(v));
1332 d
1333 }
1334
1335 #[test]
1337 fn roundtrip_seq_int32() {
1338 let mut desc = TypeDescriptor::structure("S");
1339 desc.extensibility_kind = ExtensibilityKind::Final;
1340 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1341 let seq = TypeDescriptor::sequence(
1342 "seq",
1343 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1344 0,
1345 );
1346 b.add_struct_member("xs", 0, seq).unwrap();
1347 let ty = b.build().unwrap();
1348
1349 let mut d = DynamicData::new(ty.clone());
1350 let items = alloc::vec![scalar_i32(11), scalar_i32(-22), scalar_i32(33),];
1351 d.set_sequence_value(0, items).unwrap();
1352 let bytes = encode_dynamic(&d, true, false).expect("encode");
1353 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1354 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1355 panic!("seq")
1356 };
1357 let got: alloc::vec::Vec<i32> = v.iter().map(|e| e.get_int32_value(0).unwrap()).collect();
1358 assert_eq!(got, alloc::vec![11, -22, 33]);
1359 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1360 }
1361
1362 #[test]
1364 fn roundtrip_seq_string() {
1365 let mut desc = TypeDescriptor::structure("S");
1366 desc.extensibility_kind = ExtensibilityKind::Final;
1367 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1368 let strd = DynamicTypeBuilderFactory::create_string_type(255)
1369 .descriptor()
1370 .clone();
1371 let seq = TypeDescriptor::sequence("seq", strd, 0);
1372 b.add_struct_member("ss", 0, seq).unwrap();
1373 let ty = b.build().unwrap();
1374
1375 let mut d = DynamicData::new(ty.clone());
1376 let mk = |s: &str| {
1377 let mut e = DynamicData::new(DynamicTypeBuilderFactory::create_string_type(255));
1378 e.set_value_raw(0, DynamicValue::String(s.into()));
1379 e
1380 };
1381 d.set_sequence_value(0, alloc::vec![mk("a"), mk("bb"), mk("ccc")])
1382 .unwrap();
1383 let bytes = encode_dynamic(&d, true, false).expect("encode");
1384 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1385 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1386 panic!("seq")
1387 };
1388 let got: alloc::vec::Vec<alloc::string::String> =
1389 v.iter().map(|e| e.get_string_value(0).unwrap()).collect();
1390 assert_eq!(
1391 got,
1392 alloc::vec!["a".to_string(), "bb".to_string(), "ccc".to_string()]
1393 );
1394 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1395 }
1396
1397 #[test]
1399 fn roundtrip_array_int32() {
1400 let mut desc = TypeDescriptor::structure("S");
1401 desc.extensibility_kind = ExtensibilityKind::Final;
1402 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1403 let arr = TypeDescriptor::array(
1404 "arr",
1405 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1406 alloc::vec![3],
1407 );
1408 b.add_struct_member("a", 0, arr).unwrap();
1409 let ty = b.build().unwrap();
1410
1411 let mut d = DynamicData::new(ty.clone());
1412 let items = alloc::vec![scalar_i32(7), scalar_i32(8), scalar_i32(9),];
1413 d.set_sequence_value(0, items).unwrap();
1414 let bytes = encode_dynamic(&d, true, false).expect("encode");
1415 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1416 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1417 panic!("arr")
1418 };
1419 let got: alloc::vec::Vec<i32> = v.iter().map(|e| e.get_int32_value(0).unwrap()).collect();
1420 assert_eq!(got, alloc::vec![7, 8, 9]);
1421 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1422 }
1423
1424 #[test]
1428 fn roundtrip_seq_of_struct() {
1429 use crate::dynamic::collection;
1430 let inner = sample_type(ExtensibilityKind::Final);
1431 let seq_ty = collection::sequence_of(inner.clone(), 0);
1432 let mut desc = TypeDescriptor::structure("S");
1433 desc.extensibility_kind = ExtensibilityKind::Final;
1434 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1435 let mut md = MemberDescriptor::new("xs", 0, seq_ty.descriptor().clone());
1436 md.index = 0;
1437 b.add_member_resolved(md, seq_ty).unwrap();
1438 let ty = b.build().unwrap();
1439
1440 let mut d = DynamicData::new(ty.clone());
1441 let e0 = populate(&inner);
1442 let mut e1 = DynamicData::new(inner.clone());
1443 e1.set_int32_value(0, 123).unwrap();
1444 e1.set_uint64_value(1, 7).unwrap();
1445 e1.set_string_value(2, "second").unwrap();
1446 e1.set_float64_value(3, -1.25).unwrap();
1447 d.set_sequence_value(0, alloc::vec![e0, e1]).unwrap();
1448
1449 for xcdr2 in [true, false] {
1450 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1451 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1452 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1453 panic!("seq")
1454 };
1455 assert_eq!(v.len(), 2);
1456 assert_eq!(v[0].dynamic_type().kind(), TypeKind::Structure);
1458 assert_eq!(v[0].get_string_value(2).unwrap(), "hello-dynamic");
1459 assert_eq!(v[1].get_int32_value(0).unwrap(), 123);
1460 assert_eq!(v[1].get_string_value(2).unwrap(), "second");
1461 assert_eq!(
1462 encode_dynamic(&back, xcdr2, false).unwrap(),
1463 bytes,
1464 "byte-exact seq<struct> xcdr2={xcdr2}"
1465 );
1466 }
1467 }
1468
1469 #[test]
1471 fn roundtrip_array_of_struct() {
1472 use crate::dynamic::collection;
1473 let inner = sample_type(ExtensibilityKind::Final);
1474 let arr_ty = collection::array_of(inner.clone(), alloc::vec![2], "arr");
1475 let mut desc = TypeDescriptor::structure("S");
1476 desc.extensibility_kind = ExtensibilityKind::Final;
1477 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1478 let mut md = MemberDescriptor::new("a", 0, arr_ty.descriptor().clone());
1479 md.index = 0;
1480 b.add_member_resolved(md, arr_ty).unwrap();
1481 let ty = b.build().unwrap();
1482
1483 let mut d = DynamicData::new(ty.clone());
1484 let mut e0 = DynamicData::new(inner.clone());
1485 e0.set_int32_value(0, 1).unwrap();
1486 e0.set_uint64_value(1, 1).unwrap();
1487 e0.set_string_value(2, "x").unwrap();
1488 e0.set_float64_value(3, 1.0).unwrap();
1489 let mut e1 = DynamicData::new(inner.clone());
1490 e1.set_int32_value(0, 2).unwrap();
1491 e1.set_uint64_value(1, 2).unwrap();
1492 e1.set_string_value(2, "y").unwrap();
1493 e1.set_float64_value(3, 2.0).unwrap();
1494 d.set_sequence_value(0, alloc::vec![e0, e1]).unwrap();
1495
1496 let bytes = encode_dynamic(&d, true, false).expect("encode");
1497 let back = decode_dynamic(&ty, &bytes, true, false).expect("decode");
1498 let DynamicValue::Sequence(v) = back.get_value(0).unwrap() else {
1499 panic!("arr")
1500 };
1501 assert_eq!(v.len(), 2);
1502 assert_eq!(v[0].get_string_value(2).unwrap(), "x");
1503 assert_eq!(v[1].get_int32_value(0).unwrap(), 2);
1504 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1505 }
1506
1507 #[test]
1509 fn roundtrip_wstring() {
1510 let mut desc = TypeDescriptor::structure("W");
1511 desc.extensibility_kind = ExtensibilityKind::Final;
1512 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1513 let wdesc = DynamicTypeBuilderFactory::create_wstring_type(0)
1514 .descriptor()
1515 .clone();
1516 let mut md = MemberDescriptor::new("w", 0, wdesc);
1517 md.index = 0;
1518 b.add_member_resolved(md, DynamicTypeBuilderFactory::create_wstring_type(0))
1519 .unwrap();
1520 let ty = b.build().unwrap();
1521
1522 let mut d = DynamicData::new(ty.clone());
1523 let units: alloc::vec::Vec<u16> = "wïde→".encode_utf16().collect();
1524 d.set_value_raw(0, DynamicValue::WString(units.clone()));
1525 for xcdr2 in [true, false] {
1526 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1527 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1528 let DynamicValue::WString(got) = back.get_value(0).unwrap() else {
1529 panic!("wstring")
1530 };
1531 assert_eq!(*got, units);
1532 assert_eq!(encode_dynamic(&back, xcdr2, false).unwrap(), bytes);
1533 }
1534 }
1535
1536 #[test]
1538 fn roundtrip_nested_struct() {
1539 let inner = sample_type(ExtensibilityKind::Final);
1540 let mut desc = TypeDescriptor::structure("Outer");
1541 desc.extensibility_kind = ExtensibilityKind::Final;
1542 let mut b = DynamicTypeBuilderFactory::create_type(desc).expect("builder");
1543 b.add_struct_member(
1544 "id",
1545 0,
1546 TypeDescriptor::primitive(TypeKind::UInt32, "uint32"),
1547 )
1548 .unwrap();
1549 let mut md = MemberDescriptor::new("inner", 1, inner.descriptor().clone());
1550 md.index = 1;
1551 b.add_member_resolved(md, inner.clone()).unwrap();
1552 let outer = b.build().expect("build outer");
1553
1554 let mut d = DynamicData::new(outer.clone());
1555 d.set_uint32_value(0, 99).unwrap();
1556 d.set_complex_value(1, populate(&inner)).unwrap();
1557
1558 let bytes = encode_dynamic(&d, true, false).expect("encode");
1559 let back = decode_dynamic(&outer, &bytes, true, false).expect("decode");
1560 assert_eq!(back.get_uint32_value(0).unwrap(), 99);
1561 let inner_back = back.get_complex_value(1).unwrap();
1562 assert_eq!(inner_back.get_string_value(2).unwrap(), "hello-dynamic");
1563 assert_eq!(encode_dynamic(&back, true, false).unwrap(), bytes);
1564 }
1565
1566 fn scalar_dd(ty: &DynamicType, v: DynamicValue) -> DynamicData {
1573 let mut d = DynamicData::new(ty.clone());
1574 d.set_value_raw(0, v);
1575 d
1576 }
1577
1578 #[test]
1579 fn map_primitive_pair_byte_identical_to_compiled() {
1580 use alloc::collections::BTreeMap;
1581 let kt = DynamicType::new_primitive(TypeKind::Int32);
1582 let vt = DynamicType::new_primitive(TypeKind::Int32);
1583 let map_ty = crate::dynamic::collection::map_of(kt.clone(), vt.clone(), 0, "m");
1584 let entries = alloc::vec![
1586 (
1587 scalar_dd(&kt, DynamicValue::Int32(1)),
1588 scalar_dd(&vt, DynamicValue::Int32(2))
1589 ),
1590 (
1591 scalar_dd(&kt, DynamicValue::Int32(7)),
1592 scalar_dd(&vt, DynamicValue::Int32(42))
1593 ),
1594 ];
1595 let mut oracle: BTreeMap<i32, i32> = BTreeMap::new();
1596 oracle.insert(7, 42);
1597 oracle.insert(1, 2);
1598
1599 for xcdr2 in [true, false] {
1600 let mut wr = BufferWriter::new(Endianness::Little);
1601 let mut wo = BufferWriter::new(Endianness::Little);
1602 if xcdr2 {
1603 wr = wr.xcdr2();
1604 wo = wo.xcdr2();
1605 }
1606 write_map(&entries, &map_ty, &mut wr, xcdr2).unwrap();
1607 oracle.encode(&mut wo).unwrap();
1608 assert_eq!(
1609 wr.into_bytes(),
1610 wo.into_bytes(),
1611 "map<i32,i32> byte-identical to compiled BTreeMap, xcdr2={xcdr2}"
1612 );
1613 }
1614 }
1615
1616 #[test]
1617 fn map_nonprimitive_value_byte_identical_to_compiled() {
1618 use alloc::collections::BTreeMap;
1619 let kt = DynamicType::new_primitive(TypeKind::Int32);
1620 let vt = DynamicTypeBuilderFactory::create_string_type(0);
1621 let map_ty = crate::dynamic::collection::map_of(kt.clone(), vt.clone(), 0, "m");
1622 let entries = alloc::vec![
1623 (
1624 scalar_dd(&kt, DynamicValue::Int32(1)),
1625 scalar_dd(&vt, DynamicValue::String("one".to_string()))
1626 ),
1627 (
1628 scalar_dd(&kt, DynamicValue::Int32(7)),
1629 scalar_dd(&vt, DynamicValue::String("seven".to_string()))
1630 ),
1631 ];
1632 let mut oracle: BTreeMap<i32, String> = BTreeMap::new();
1633 oracle.insert(7, "seven".to_string());
1634 oracle.insert(1, "one".to_string());
1635
1636 for xcdr2 in [true, false] {
1637 let mut wr = BufferWriter::new(Endianness::Little);
1638 let mut wo = BufferWriter::new(Endianness::Little);
1639 if xcdr2 {
1640 wr = wr.xcdr2();
1641 wo = wo.xcdr2();
1642 }
1643 write_map(&entries, &map_ty, &mut wr, xcdr2).unwrap();
1644 oracle.encode(&mut wo).unwrap();
1645 assert_eq!(
1647 wr.into_bytes(),
1648 wo.into_bytes(),
1649 "map<i32,string> byte-identical to compiled BTreeMap, xcdr2={xcdr2}"
1650 );
1651 }
1652 }
1653
1654 #[test]
1657 fn roundtrip_map_of_scalar() {
1658 let kt = DynamicType::new_primitive(TypeKind::Int32);
1659 let vt = DynamicType::new_primitive(TypeKind::Int32);
1660 let map_ty = crate::dynamic::collection::map_of(kt.clone(), vt.clone(), 0, "m");
1661 let mut desc = TypeDescriptor::structure("M");
1662 desc.extensibility_kind = ExtensibilityKind::Final;
1663 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1664 let mut md = MemberDescriptor::new("m", 0, map_ty.descriptor().clone());
1665 md.index = 0;
1666 b.add_member_resolved(md, map_ty).unwrap();
1667 let ty = b.build().unwrap();
1668
1669 let mut d = DynamicData::new(ty.clone());
1670 d.set_map_value(
1671 0,
1672 alloc::vec![
1673 (
1674 scalar_dd(&kt, DynamicValue::Int32(1)),
1675 scalar_dd(&vt, DynamicValue::Int32(2))
1676 ),
1677 (
1678 scalar_dd(&kt, DynamicValue::Int32(7)),
1679 scalar_dd(&vt, DynamicValue::Int32(42))
1680 ),
1681 ],
1682 )
1683 .unwrap();
1684
1685 for xcdr2 in [true, false] {
1686 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1687 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1688 let DynamicValue::Map(got) = back.get_value(0).unwrap() else {
1689 panic!("m is a map")
1690 };
1691 assert_eq!(got.len(), 2);
1692 assert_eq!(got[0].0.get_int32_value(0).unwrap(), 1);
1693 assert_eq!(got[1].1.get_int32_value(0).unwrap(), 42);
1694 assert_eq!(
1695 encode_dynamic(&back, xcdr2, false).unwrap(),
1696 bytes,
1697 "byte-exact map<i32,i32> round-trip xcdr2={xcdr2}"
1698 );
1699 }
1700 }
1701
1702 #[test]
1705 fn roundtrip_map_of_struct() {
1706 let kt = DynamicType::new_primitive(TypeKind::Int32);
1707 let vt = sample_type(ExtensibilityKind::Final);
1708 let map_ty = crate::dynamic::collection::map_of(kt.clone(), vt.clone(), 0, "m");
1709 let mut desc = TypeDescriptor::structure("M");
1710 desc.extensibility_kind = ExtensibilityKind::Final;
1711 let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1712 let mut md = MemberDescriptor::new("m", 0, map_ty.descriptor().clone());
1713 md.index = 0;
1714 b.add_member_resolved(md, map_ty).unwrap();
1715 let ty = b.build().unwrap();
1716
1717 let entry_val = populate(&vt);
1718 let mut d = DynamicData::new(ty.clone());
1719 d.set_map_value(
1720 0,
1721 alloc::vec![(scalar_dd(&kt, DynamicValue::Int32(5)), entry_val)],
1722 )
1723 .unwrap();
1724
1725 for xcdr2 in [true, false] {
1726 let bytes = encode_dynamic(&d, xcdr2, false).expect("encode");
1727 let back = decode_dynamic(&ty, &bytes, xcdr2, false).expect("decode");
1728 let DynamicValue::Map(got) = back.get_value(0).unwrap() else {
1729 panic!("map")
1730 };
1731 assert_eq!(got.len(), 1);
1732 assert_eq!(got[0].0.get_int32_value(0).unwrap(), 5);
1733 assert_eq!(got[0].1.dynamic_type().kind(), TypeKind::Structure);
1735 assert_eq!(got[0].1.get_string_value(2).unwrap(), "hello-dynamic");
1736 assert_eq!(
1737 encode_dynamic(&back, xcdr2, false).unwrap(),
1738 bytes,
1739 "byte-exact map<i32,struct> round-trip xcdr2={xcdr2}"
1740 );
1741 }
1742 }
1743}