zerodds_dcps/interop.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Interop-Test-Typen fuer Cross-Vendor-Nachweis.
4//!
5//! Enthaelt Application-Typen, die in der DDS-Welt als de-facto-
6//! Interop-Benchmark gelten — allen voran `ShapeType` aus dem
7//! RTI/Cyclone/Fast-DDS-ShapesDemo. Diese Typen sind nicht fuer
8//! Produktions-Nutzung gedacht, sondern dafuer, gegenueber anderen
9//! DDS-Stacks zu beweisen, dass unsere Wire- und Typen-Semantik
10//! byte-kompatibel ist.
11//!
12//! # `ShapeType`
13//!
14//! Spec-Grundlage (IDL, wie von RTI/Cyclone/Fast-DDS verwendet):
15//!
16//! ```idl
17//! struct ShapeType {
18//! @key string<128> color;
19//! int32 x;
20//! int32 y;
21//! int32 shapesize;
22//! };
23//! ```
24//!
25//! Encoding: XCDR2 Little-Endian (das ist die Standard-Einstellung
26//! saemtlicher ShapesDemo-Implementierungen, und entspricht unserem
27//! User-Payload-Encapsulation-Header `0x00 0x07 0x00 0x00`).
28//!
29//! CDR-Layout:
30//! ```text
31//! offset 0 : uint32 color.length (inkl. null-terminator)
32//! offset 4 : bytes color.utf8_bytes
33//! offset 4+n: uint8 0x00 (null-terminator)
34//! padding (auf naechste 4-Byte-Grenze)
35//! offset * : int32 x
36//! offset *+4: int32 y
37//! offset *+8: int32 shapesize
38//! ```
39//!
40//! Der `@key` auf `color` wird auf Wire-Level nicht gesondert behandelt
41//! — er steuert Instance-Keying, nicht die Serialisierung. Fuer unser
42//! Sample-Matching in v1.2 (noch keine Instance-Map im Reader) ist
43//! jede (color, x, y, shapesize)-Kombination effektiv ein eigenes
44//! Sample.
45
46extern crate alloc;
47
48use alloc::string::String;
49use alloc::vec::Vec;
50
51use zerodds_cdr::buffer::{BufferReader, BufferWriter};
52use zerodds_cdr::endianness::Endianness;
53
54use crate::dds_type::{DdsType, DecodeError, EncodeError};
55
56/// RTI / Cyclone / Fast-DDS ShapesDemo-kompatibler Application-Type.
57///
58/// Siehe Modul-Doku fuer Spec und Layout. Farbe ist Instance-Key,
59/// x/y/shapesize sind die typischen Formen-Koordinaten.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct ShapeType {
62 /// Farbe / Instance-Key. In ShapesDemo-Implementierungen typisch
63 /// `"BLUE"`, `"RED"`, `"GREEN"`, `"YELLOW"`, `"MAGENTA"`, `"CYAN"`,
64 /// `"ORANGE"`, `"PURPLE"`. Keine inhaltliche Validierung hier —
65 /// beliebige UTF-8-Strings sind zulaessig.
66 pub color: String,
67 /// X-Koordinate in Pixeln (ShapesDemo-Canvas ist ~240×270).
68 pub x: i32,
69 /// Y-Koordinate.
70 pub y: i32,
71 /// Formen-Groesse in Pixeln. Typisch 30.
72 pub shapesize: i32,
73}
74
75impl ShapeType {
76 /// Konstruktor.
77 #[must_use]
78 pub fn new(color: impl Into<String>, x: i32, y: i32, shapesize: i32) -> Self {
79 Self {
80 color: color.into(),
81 x,
82 y,
83 shapesize,
84 }
85 }
86}
87
88impl DdsType for ShapeType {
89 /// Type-Name **exakt** wie RTI/Cyclone/Fast-DDS ShapesDemo. Aendern
90 /// wuerde Matching mit jedem anderen ShapesDemo-Client brechen.
91 const TYPE_NAME: &'static str = "ShapeType";
92 /// ShapesDemo-IDL: `@key string color`. ShapeType ist keyed —
93 /// Per-Instance-QoS (TimeBasedFilter, Ownership, Lifecycle)
94 /// haengt davon ab.
95 const HAS_KEY: bool = true;
96
97 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
98 holder.write_string(&self.color);
99 }
100
101 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
102 let mut w = BufferWriter::new(Endianness::Little);
103 w.write_string(&self.color)
104 .map_err(|_| EncodeError::Invalid {
105 what: "ShapeType.color encoding",
106 })?;
107 w.write_u32(self.x as u32)
108 .map_err(|_| EncodeError::Invalid {
109 what: "ShapeType.x encoding",
110 })?;
111 w.write_u32(self.y as u32)
112 .map_err(|_| EncodeError::Invalid {
113 what: "ShapeType.y encoding",
114 })?;
115 w.write_u32(self.shapesize as u32)
116 .map_err(|_| EncodeError::Invalid {
117 what: "ShapeType.shapesize encoding",
118 })?;
119 out.extend_from_slice(w.as_bytes());
120 Ok(())
121 }
122
123 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
124 let mut r = BufferReader::new(bytes, Endianness::Little);
125 let color = r.read_string().map_err(|_| DecodeError::Invalid {
126 what: "ShapeType.color decoding",
127 })?;
128 let x = r.read_u32().map_err(|_| DecodeError::Invalid {
129 what: "ShapeType.x decoding",
130 })? as i32;
131 let y = r.read_u32().map_err(|_| DecodeError::Invalid {
132 what: "ShapeType.y decoding",
133 })? as i32;
134 let shapesize = r.read_u32().map_err(|_| DecodeError::Invalid {
135 what: "ShapeType.shapesize decoding",
136 })? as i32;
137 Ok(Self {
138 color,
139 x,
140 y,
141 shapesize,
142 })
143 }
144}