1extern crate alloc;
34use alloc::string::{String, ToString};
35use alloc::vec::Vec;
36
37use crate::dds_type::{DdsType, DecodeError, EncodeError};
38
39use zerodds_rtps::participant_data as wire_part;
40use zerodds_rtps::publication_data as wire_pub;
41use zerodds_rtps::subscription_data as wire_sub;
42use zerodds_rtps::wire_types::Guid;
43
44pub const TOPIC_NAME_DCPS_PARTICIPANT: &str = "DCPSParticipant";
50pub const TOPIC_NAME_DCPS_TOPIC: &str = "DCPSTopic";
52pub const TOPIC_NAME_DCPS_PUBLICATION: &str = "DCPSPublication";
54pub const TOPIC_NAME_DCPS_SUBSCRIPTION: &str = "DCPSSubscription";
56
57#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct ParticipantBuiltinTopicData {
66 pub key: Guid,
70 pub user_data: Vec<u8>,
74}
75
76impl ParticipantBuiltinTopicData {
77 #[must_use]
79 pub fn from_wire(w: &wire_part::ParticipantBuiltinTopicData) -> Self {
80 Self {
81 key: w.guid,
82 user_data: w.user_data.clone(),
83 }
84 }
85}
86
87impl DdsType for ParticipantBuiltinTopicData {
88 const TYPE_NAME: &'static str = "DDS::ParticipantBuiltinTopicData";
89 const HAS_KEY: bool = true;
90 const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(16);
91
92 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
93 out.extend_from_slice(&self.key.to_bytes());
94 encode_bytes_le(&self.user_data, out)
95 }
96
97 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
98 let mut r = Reader::new(bytes);
99 let key = r.read_guid()?;
100 let user_data = r.read_bytes()?;
101 Ok(Self { key, user_data })
102 }
103
104 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
105 holder.write_bytes(&self.key.to_bytes());
106 }
107}
108
109#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct TopicBuiltinTopicData {
121 pub key: Guid,
124 pub name: String,
126 pub type_name: String,
128 pub durability: zerodds_qos::DurabilityKind,
130 pub reliability: zerodds_qos::ReliabilityKind,
132}
133
134impl TopicBuiltinTopicData {
135 #[must_use]
140 pub fn synthesize_key(topic: &str, type_name: &str) -> Guid {
141 let h1 = fnv1a64(topic.as_bytes(), 0xcbf2_9ce4_8422_2325);
144 let h2 = fnv1a64(type_name.as_bytes(), h1);
145 let mut bytes = [0u8; 16];
146 bytes[0..8].copy_from_slice(&h1.to_le_bytes());
147 bytes[8..16].copy_from_slice(&h2.to_le_bytes());
148 Guid::from_bytes(bytes)
149 }
150
151 #[must_use]
153 pub fn from_publication(w: &wire_pub::PublicationBuiltinTopicData) -> Self {
154 Self {
155 key: Self::synthesize_key(&w.topic_name, &w.type_name),
156 name: w.topic_name.clone(),
157 type_name: w.type_name.clone(),
158 durability: w.durability,
159 reliability: w.reliability.kind,
160 }
161 }
162
163 #[must_use]
165 pub fn from_subscription(w: &wire_sub::SubscriptionBuiltinTopicData) -> Self {
166 Self {
167 key: Self::synthesize_key(&w.topic_name, &w.type_name),
168 name: w.topic_name.clone(),
169 type_name: w.type_name.clone(),
170 durability: w.durability,
171 reliability: w.reliability.kind,
172 }
173 }
174}
175
176impl DdsType for TopicBuiltinTopicData {
177 const TYPE_NAME: &'static str = "DDS::TopicBuiltinTopicData";
178 const HAS_KEY: bool = true;
179 const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(16);
180
181 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
182 out.extend_from_slice(&self.key.to_bytes());
183 encode_string_le(&self.name, out)?;
184 encode_string_le(&self.type_name, out)?;
185 out.push(self.durability as u8);
186 out.push(self.reliability as u8);
187 Ok(())
188 }
189
190 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
191 let mut r = Reader::new(bytes);
192 let key = r.read_guid()?;
193 let name = r.read_string()?;
194 let type_name = r.read_string()?;
195 let durability = decode_durability(r.read_u8()?)?;
196 let reliability = decode_reliability(r.read_u8()?)?;
197 Ok(Self {
198 key,
199 name,
200 type_name,
201 durability,
202 reliability,
203 })
204 }
205
206 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
207 holder.write_bytes(&self.key.to_bytes());
208 }
209}
210
211#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct PublicationBuiltinTopicData {
220 pub key: Guid,
222 pub participant_key: Guid,
224 pub topic_name: String,
226 pub type_name: String,
228 pub durability: zerodds_qos::DurabilityKind,
230 pub reliability: zerodds_qos::ReliabilityKind,
232 pub ownership: zerodds_qos::OwnershipKind,
234 pub ownership_strength: i32,
236 pub liveliness_lease_seconds: i32,
238 pub deadline_seconds: i32,
240 pub lifespan_seconds: i32,
242 pub partition: Vec<String>,
244}
245
246impl PublicationBuiltinTopicData {
247 #[must_use]
249 pub fn from_wire(w: &wire_pub::PublicationBuiltinTopicData) -> Self {
250 Self {
251 key: w.key,
252 participant_key: w.participant_key,
253 topic_name: w.topic_name.clone(),
254 type_name: w.type_name.clone(),
255 durability: w.durability,
256 reliability: w.reliability.kind,
257 ownership: w.ownership,
258 ownership_strength: w.ownership_strength,
259 liveliness_lease_seconds: w.liveliness.lease_duration.seconds,
260 deadline_seconds: w.deadline.period.seconds,
261 lifespan_seconds: w.lifespan.duration.seconds,
262 partition: w.partition.clone(),
263 }
264 }
265}
266
267impl DdsType for PublicationBuiltinTopicData {
268 const TYPE_NAME: &'static str = "DDS::PublicationBuiltinTopicData";
269 const HAS_KEY: bool = true;
270 const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(16);
271
272 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
273 out.extend_from_slice(&self.key.to_bytes());
274 out.extend_from_slice(&self.participant_key.to_bytes());
275 encode_string_le(&self.topic_name, out)?;
276 encode_string_le(&self.type_name, out)?;
277 out.push(self.durability as u8);
278 out.push(self.reliability as u8);
279 out.push(self.ownership as u8);
280 out.extend_from_slice(&self.ownership_strength.to_le_bytes());
281 out.extend_from_slice(&self.liveliness_lease_seconds.to_le_bytes());
282 out.extend_from_slice(&self.deadline_seconds.to_le_bytes());
283 out.extend_from_slice(&self.lifespan_seconds.to_le_bytes());
284 encode_string_seq_le(&self.partition, out)?;
285 Ok(())
286 }
287
288 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
289 let mut r = Reader::new(bytes);
290 let key = r.read_guid()?;
291 let participant_key = r.read_guid()?;
292 let topic_name = r.read_string()?;
293 let type_name = r.read_string()?;
294 let durability = decode_durability(r.read_u8()?)?;
295 let reliability = decode_reliability(r.read_u8()?)?;
296 let ownership = decode_ownership(r.read_u8()?)?;
297 let ownership_strength = r.read_i32()?;
298 let liveliness_lease_seconds = r.read_i32()?;
299 let deadline_seconds = r.read_i32()?;
300 let lifespan_seconds = r.read_i32()?;
301 let partition = r.read_string_seq()?;
302 Ok(Self {
303 key,
304 participant_key,
305 topic_name,
306 type_name,
307 durability,
308 reliability,
309 ownership,
310 ownership_strength,
311 liveliness_lease_seconds,
312 deadline_seconds,
313 lifespan_seconds,
314 partition,
315 })
316 }
317
318 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
319 holder.write_bytes(&self.key.to_bytes());
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
331pub struct SubscriptionBuiltinTopicData {
332 pub key: Guid,
334 pub participant_key: Guid,
336 pub topic_name: String,
338 pub type_name: String,
340 pub durability: zerodds_qos::DurabilityKind,
342 pub reliability: zerodds_qos::ReliabilityKind,
344 pub ownership: zerodds_qos::OwnershipKind,
346 pub liveliness_lease_seconds: i32,
348 pub deadline_seconds: i32,
350 pub partition: Vec<String>,
352}
353
354impl SubscriptionBuiltinTopicData {
355 #[must_use]
357 pub fn from_wire(w: &wire_sub::SubscriptionBuiltinTopicData) -> Self {
358 Self {
359 key: w.key,
360 participant_key: w.participant_key,
361 topic_name: w.topic_name.clone(),
362 type_name: w.type_name.clone(),
363 durability: w.durability,
364 reliability: w.reliability.kind,
365 ownership: w.ownership,
366 liveliness_lease_seconds: w.liveliness.lease_duration.seconds,
367 deadline_seconds: w.deadline.period.seconds,
368 partition: w.partition.clone(),
369 }
370 }
371}
372
373impl DdsType for SubscriptionBuiltinTopicData {
374 const TYPE_NAME: &'static str = "DDS::SubscriptionBuiltinTopicData";
375 const HAS_KEY: bool = true;
376 const KEY_HOLDER_MAX_SIZE: Option<usize> = Some(16);
377
378 fn encode(&self, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
379 out.extend_from_slice(&self.key.to_bytes());
380 out.extend_from_slice(&self.participant_key.to_bytes());
381 encode_string_le(&self.topic_name, out)?;
382 encode_string_le(&self.type_name, out)?;
383 out.push(self.durability as u8);
384 out.push(self.reliability as u8);
385 out.push(self.ownership as u8);
386 out.extend_from_slice(&self.liveliness_lease_seconds.to_le_bytes());
387 out.extend_from_slice(&self.deadline_seconds.to_le_bytes());
388 encode_string_seq_le(&self.partition, out)?;
389 Ok(())
390 }
391
392 fn decode(bytes: &[u8]) -> core::result::Result<Self, DecodeError> {
393 let mut r = Reader::new(bytes);
394 let key = r.read_guid()?;
395 let participant_key = r.read_guid()?;
396 let topic_name = r.read_string()?;
397 let type_name = r.read_string()?;
398 let durability = decode_durability(r.read_u8()?)?;
399 let reliability = decode_reliability(r.read_u8()?)?;
400 let ownership = decode_ownership(r.read_u8()?)?;
401 let liveliness_lease_seconds = r.read_i32()?;
402 let deadline_seconds = r.read_i32()?;
403 let partition = r.read_string_seq()?;
404 Ok(Self {
405 key,
406 participant_key,
407 topic_name,
408 type_name,
409 durability,
410 reliability,
411 ownership,
412 liveliness_lease_seconds,
413 deadline_seconds,
414 partition,
415 })
416 }
417
418 fn encode_key_holder_be(&self, holder: &mut crate::dds_type::PlainCdr2BeKeyHolder) {
419 holder.write_bytes(&self.key.to_bytes());
420 }
421}
422
423fn encode_string_le(s: &str, out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
428 let len = u32::try_from(s.len()).map_err(|_| EncodeError::Invalid {
429 what: "builtin-topic string > 4 GiB",
430 })?;
431 out.extend_from_slice(&len.to_le_bytes());
432 out.extend_from_slice(s.as_bytes());
433 Ok(())
434}
435
436fn encode_bytes_le(b: &[u8], out: &mut Vec<u8>) -> core::result::Result<(), EncodeError> {
437 let len = u32::try_from(b.len()).map_err(|_| EncodeError::Invalid {
438 what: "builtin-topic bytes > 4 GiB",
439 })?;
440 out.extend_from_slice(&len.to_le_bytes());
441 out.extend_from_slice(b);
442 Ok(())
443}
444
445fn encode_string_seq_le(
446 seq: &[String],
447 out: &mut Vec<u8>,
448) -> core::result::Result<(), EncodeError> {
449 let len = u32::try_from(seq.len()).map_err(|_| EncodeError::Invalid {
450 what: "builtin-topic seq len > 4 GiB",
451 })?;
452 out.extend_from_slice(&len.to_le_bytes());
453 for s in seq {
454 encode_string_le(s, out)?;
455 }
456 Ok(())
457}
458
459struct Reader<'a> {
460 bytes: &'a [u8],
461 pos: usize,
462}
463
464impl<'a> Reader<'a> {
465 fn new(bytes: &'a [u8]) -> Self {
466 Self { bytes, pos: 0 }
467 }
468
469 fn need(&self, n: usize) -> core::result::Result<(), DecodeError> {
470 if self.pos.saturating_add(n) > self.bytes.len() {
471 return Err(DecodeError::Invalid {
472 what: "builtin-topic truncated",
473 });
474 }
475 Ok(())
476 }
477
478 fn read_u8(&mut self) -> core::result::Result<u8, DecodeError> {
479 self.need(1)?;
480 let v = self.bytes[self.pos];
481 self.pos += 1;
482 Ok(v)
483 }
484
485 fn read_u32(&mut self) -> core::result::Result<u32, DecodeError> {
486 self.need(4)?;
487 let mut a = [0u8; 4];
488 a.copy_from_slice(&self.bytes[self.pos..self.pos + 4]);
489 self.pos += 4;
490 Ok(u32::from_le_bytes(a))
491 }
492
493 fn read_i32(&mut self) -> core::result::Result<i32, DecodeError> {
494 self.read_u32().map(|v| v as i32)
495 }
496
497 fn read_guid(&mut self) -> core::result::Result<Guid, DecodeError> {
498 self.need(16)?;
499 let mut b = [0u8; 16];
500 b.copy_from_slice(&self.bytes[self.pos..self.pos + 16]);
501 self.pos += 16;
502 Ok(Guid::from_bytes(b))
503 }
504
505 fn read_string(&mut self) -> core::result::Result<String, DecodeError> {
506 let len = self.read_u32()? as usize;
507 self.need(len)?;
508 let s = core::str::from_utf8(&self.bytes[self.pos..self.pos + len])
509 .map_err(|_| DecodeError::Invalid {
510 what: "builtin-topic invalid utf8",
511 })?
512 .to_string();
513 self.pos += len;
514 Ok(s)
515 }
516
517 fn read_bytes(&mut self) -> core::result::Result<Vec<u8>, DecodeError> {
518 let len = self.read_u32()? as usize;
519 self.need(len)?;
520 let v = self.bytes[self.pos..self.pos + len].to_vec();
521 self.pos += len;
522 Ok(v)
523 }
524
525 fn read_string_seq(&mut self) -> core::result::Result<Vec<String>, DecodeError> {
526 let n = self.read_u32()? as usize;
527 let mut v = Vec::with_capacity(n);
528 for _ in 0..n {
529 v.push(self.read_string()?);
530 }
531 Ok(v)
532 }
533}
534
535fn decode_durability(b: u8) -> core::result::Result<zerodds_qos::DurabilityKind, DecodeError> {
536 use zerodds_qos::DurabilityKind::*;
537 Ok(match b {
538 0 => Volatile,
539 1 => TransientLocal,
540 2 => Transient,
541 3 => Persistent,
542 _ => {
543 return Err(DecodeError::Invalid {
544 what: "durability kind out of range",
545 });
546 }
547 })
548}
549
550fn decode_reliability(b: u8) -> core::result::Result<zerodds_qos::ReliabilityKind, DecodeError> {
551 use zerodds_qos::ReliabilityKind::*;
552 Ok(match b {
553 1 => BestEffort,
554 2 => Reliable,
555 _ => {
556 return Err(DecodeError::Invalid {
557 what: "reliability kind out of range",
558 });
559 }
560 })
561}
562
563fn decode_ownership(b: u8) -> core::result::Result<zerodds_qos::OwnershipKind, DecodeError> {
564 use zerodds_qos::OwnershipKind::*;
565 Ok(match b {
566 0 => Shared,
567 1 => Exclusive,
568 _ => {
569 return Err(DecodeError::Invalid {
570 what: "ownership kind out of range",
571 });
572 }
573 })
574}
575
576fn fnv1a64(data: &[u8], seed: u64) -> u64 {
578 let mut h = seed;
579 for &b in data {
580 h ^= u64::from(b);
581 h = h.wrapping_mul(0x0000_0100_0000_01b3);
582 }
583 h
584}
585
586#[cfg(test)]
587#[allow(clippy::expect_used, clippy::unwrap_used)]
588mod tests {
589 use super::*;
590 use zerodds_rtps::wire_types::GuidPrefix;
591
592 fn mk_guid(seed: u8) -> Guid {
593 let mut b = [0u8; 16];
594 for (i, slot) in b.iter_mut().enumerate() {
595 *slot = seed.wrapping_add(i as u8);
596 }
597 Guid::from_bytes(b)
598 }
599
600 #[test]
601 fn participant_dds_type_name_matches_spec() {
602 assert_eq!(
603 <ParticipantBuiltinTopicData as DdsType>::TYPE_NAME,
604 "DDS::ParticipantBuiltinTopicData"
605 );
606 }
607
608 #[test]
609 fn topic_dds_type_name_matches_spec() {
610 assert_eq!(
611 <TopicBuiltinTopicData as DdsType>::TYPE_NAME,
612 "DDS::TopicBuiltinTopicData"
613 );
614 }
615
616 #[test]
617 fn publication_dds_type_name_matches_spec() {
618 assert_eq!(
619 <PublicationBuiltinTopicData as DdsType>::TYPE_NAME,
620 "DDS::PublicationBuiltinTopicData"
621 );
622 }
623
624 #[test]
625 fn subscription_dds_type_name_matches_spec() {
626 assert_eq!(
627 <SubscriptionBuiltinTopicData as DdsType>::TYPE_NAME,
628 "DDS::SubscriptionBuiltinTopicData"
629 );
630 }
631
632 #[test]
633 fn participant_roundtrip() {
634 let p = ParticipantBuiltinTopicData {
635 key: mk_guid(0xA0),
636 user_data: alloc::vec![1, 2, 3],
637 };
638 let mut buf = Vec::new();
639 p.encode(&mut buf).unwrap();
640 let back = ParticipantBuiltinTopicData::decode(&buf).unwrap();
641 assert_eq!(p, back);
642 }
643
644 #[test]
645 fn topic_roundtrip() {
646 let t = TopicBuiltinTopicData {
647 key: TopicBuiltinTopicData::synthesize_key("Chatter", "std::String"),
648 name: "Chatter".to_string(),
649 type_name: "std::String".to_string(),
650 durability: zerodds_qos::DurabilityKind::TransientLocal,
651 reliability: zerodds_qos::ReliabilityKind::Reliable,
652 };
653 let mut buf = Vec::new();
654 t.encode(&mut buf).unwrap();
655 let back = TopicBuiltinTopicData::decode(&buf).unwrap();
656 assert_eq!(t, back);
657 }
658
659 #[test]
660 fn topic_synthesize_key_is_stable_and_distinct() {
661 let a = TopicBuiltinTopicData::synthesize_key("X", "Foo");
662 let a2 = TopicBuiltinTopicData::synthesize_key("X", "Foo");
663 let b = TopicBuiltinTopicData::synthesize_key("X", "Bar");
664 let c = TopicBuiltinTopicData::synthesize_key("Y", "Foo");
665 assert_eq!(a, a2);
666 assert_ne!(a, b);
667 assert_ne!(a, c);
668 assert_ne!(b, c);
669 }
670
671 #[test]
672 fn publication_roundtrip() {
673 let p = PublicationBuiltinTopicData {
674 key: mk_guid(0xB0),
675 participant_key: mk_guid(0xC0),
676 topic_name: "T".to_string(),
677 type_name: "Tt".to_string(),
678 durability: zerodds_qos::DurabilityKind::Volatile,
679 reliability: zerodds_qos::ReliabilityKind::BestEffort,
680 ownership: zerodds_qos::OwnershipKind::Exclusive,
681 ownership_strength: 17,
682 liveliness_lease_seconds: 30,
683 deadline_seconds: 5,
684 lifespan_seconds: 60,
685 partition: alloc::vec!["A".to_string(), "B".to_string()],
686 };
687 let mut buf = Vec::new();
688 p.encode(&mut buf).unwrap();
689 let back = PublicationBuiltinTopicData::decode(&buf).unwrap();
690 assert_eq!(p, back);
691 }
692
693 #[test]
694 fn subscription_roundtrip() {
695 let s = SubscriptionBuiltinTopicData {
696 key: mk_guid(0xD0),
697 participant_key: mk_guid(0xE0),
698 topic_name: "T2".to_string(),
699 type_name: "T2t".to_string(),
700 durability: zerodds_qos::DurabilityKind::Persistent,
701 reliability: zerodds_qos::ReliabilityKind::Reliable,
702 ownership: zerodds_qos::OwnershipKind::Shared,
703 liveliness_lease_seconds: 10,
704 deadline_seconds: 1,
705 partition: alloc::vec![],
706 };
707 let mut buf = Vec::new();
708 s.encode(&mut buf).unwrap();
709 let back = SubscriptionBuiltinTopicData::decode(&buf).unwrap();
710 assert_eq!(s, back);
711 }
712
713 #[test]
714 fn participant_keyhash_is_first_16_bytes_of_guid() {
715 let p = ParticipantBuiltinTopicData {
716 key: mk_guid(7),
717 user_data: alloc::vec![],
718 };
719 let kh = p.compute_key_hash().expect("keyed");
720 assert_eq!(kh, p.key.to_bytes());
721 }
722
723 #[test]
724 fn participant_from_wire_strips_to_guid() {
725 use zerodds_rtps::wire_types::{EntityId, ProtocolVersion, VendorId};
726 let g = Guid::new(GuidPrefix::from_bytes([7; 12]), EntityId::PARTICIPANT);
727 let w = wire_part::ParticipantBuiltinTopicData {
728 guid: g,
729 protocol_version: ProtocolVersion::V2_5,
730 vendor_id: VendorId::ZERODDS,
731 default_unicast_locator: None,
732 default_multicast_locator: None,
733 metatraffic_unicast_locator: None,
734 metatraffic_multicast_locator: None,
735 domain_id: None,
736 builtin_endpoint_set: 0,
737 lease_duration: zerodds_qos::Duration::from_secs(100),
738 user_data: alloc::vec::Vec::new(),
739 properties: Default::default(),
740 identity_token: None,
741 permissions_token: None,
742 identity_status_token: None,
743 sig_algo_info: None,
744 kx_algo_info: None,
745 sym_cipher_algo_info: None,
746 };
747 let dcps = ParticipantBuiltinTopicData::from_wire(&w);
748 assert_eq!(dcps.key, g);
749 assert!(dcps.user_data.is_empty());
750 }
751
752 #[test]
753 fn publication_from_wire_copies_topic_and_type() {
754 let g_w = mk_guid(1);
755 let g_p = mk_guid(2);
756 let w = wire_pub::PublicationBuiltinTopicData {
757 key: g_w,
758 participant_key: g_p,
759 topic_name: "MyT".to_string(),
760 type_name: "MyType".to_string(),
761 durability: zerodds_qos::DurabilityKind::TransientLocal,
762 reliability: zerodds_qos::ReliabilityQosPolicy {
763 kind: zerodds_qos::ReliabilityKind::Reliable,
764 max_blocking_time: zerodds_qos::Duration::from_millis(100),
765 },
766 ownership: zerodds_qos::OwnershipKind::Shared,
767 ownership_strength: 0,
768 liveliness: zerodds_qos::LivelinessQosPolicy::default(),
769 deadline: zerodds_qos::DeadlineQosPolicy::default(),
770 lifespan: zerodds_qos::LifespanQosPolicy::default(),
771 partition: alloc::vec![],
772 user_data: alloc::vec![],
773 topic_data: alloc::vec![],
774 group_data: alloc::vec![],
775 type_information: None,
776 data_representation: alloc::vec![],
777 security_info: None,
778 service_instance_name: None,
779 related_entity_guid: None,
780 topic_aliases: None,
781 type_identifier: zerodds_types::TypeIdentifier::None,
782 };
783 let d = PublicationBuiltinTopicData::from_wire(&w);
784 assert_eq!(d.key, g_w);
785 assert_eq!(d.participant_key, g_p);
786 assert_eq!(d.topic_name, "MyT");
787 assert_eq!(d.type_name, "MyType");
788 assert_eq!(d.durability, zerodds_qos::DurabilityKind::TransientLocal);
789 assert_eq!(d.reliability, zerodds_qos::ReliabilityKind::Reliable);
790 }
791
792 #[test]
793 fn subscription_from_wire_copies_topic_and_type() {
794 let g_r = mk_guid(3);
795 let g_p = mk_guid(4);
796 let w = wire_sub::SubscriptionBuiltinTopicData {
797 key: g_r,
798 participant_key: g_p,
799 topic_name: "S".to_string(),
800 type_name: "St".to_string(),
801 durability: zerodds_qos::DurabilityKind::Volatile,
802 reliability: zerodds_qos::ReliabilityQosPolicy::default(),
803 ownership: zerodds_qos::OwnershipKind::Exclusive,
804 liveliness: zerodds_qos::LivelinessQosPolicy::default(),
805 deadline: zerodds_qos::DeadlineQosPolicy::default(),
806 partition: alloc::vec!["A".to_string()],
807 user_data: alloc::vec![],
808 topic_data: alloc::vec![],
809 group_data: alloc::vec![],
810 type_information: None,
811 data_representation: alloc::vec![],
812 content_filter: None,
813 security_info: None,
814 service_instance_name: None,
815 related_entity_guid: None,
816 topic_aliases: None,
817 type_identifier: zerodds_types::TypeIdentifier::None,
818 };
819 let d = SubscriptionBuiltinTopicData::from_wire(&w);
820 assert_eq!(d.key, g_r);
821 assert_eq!(d.participant_key, g_p);
822 assert_eq!(d.partition, alloc::vec!["A".to_string()]);
823 }
824
825 #[test]
826 fn topic_from_publication_synthesizes_consistent_key() {
827 let g_w = mk_guid(5);
828 let g_p = mk_guid(6);
829 let w = wire_pub::PublicationBuiltinTopicData {
830 key: g_w,
831 participant_key: g_p,
832 topic_name: "Same".to_string(),
833 type_name: "T".to_string(),
834 durability: zerodds_qos::DurabilityKind::Volatile,
835 reliability: zerodds_qos::ReliabilityQosPolicy::default(),
836 ownership: zerodds_qos::OwnershipKind::Shared,
837 ownership_strength: 0,
838 liveliness: zerodds_qos::LivelinessQosPolicy::default(),
839 deadline: zerodds_qos::DeadlineQosPolicy::default(),
840 lifespan: zerodds_qos::LifespanQosPolicy::default(),
841 partition: alloc::vec![],
842 user_data: alloc::vec![],
843 topic_data: alloc::vec![],
844 group_data: alloc::vec![],
845 type_information: None,
846 data_representation: alloc::vec![],
847 security_info: None,
848 service_instance_name: None,
849 related_entity_guid: None,
850 topic_aliases: None,
851 type_identifier: zerodds_types::TypeIdentifier::None,
852 };
853 let t = TopicBuiltinTopicData::from_publication(&w);
854 let expected_key = TopicBuiltinTopicData::synthesize_key("Same", "T");
855 assert_eq!(t.key, expected_key);
856 assert_eq!(t.name, "Same");
857 }
858
859 #[test]
860 fn decode_rejects_truncated() {
861 let bad = alloc::vec![1u8, 2, 3];
862 assert!(ParticipantBuiltinTopicData::decode(&bad).is_err());
863 assert!(TopicBuiltinTopicData::decode(&bad).is_err());
864 assert!(PublicationBuiltinTopicData::decode(&bad).is_err());
865 assert!(SubscriptionBuiltinTopicData::decode(&bad).is_err());
866 }
867
868 #[test]
869 fn decode_rejects_invalid_durability_kind() {
870 let t = TopicBuiltinTopicData {
872 key: mk_guid(0),
873 name: "x".to_string(),
874 type_name: "y".to_string(),
875 durability: zerodds_qos::DurabilityKind::Volatile,
876 reliability: zerodds_qos::ReliabilityKind::Reliable,
877 };
878 let mut buf = Vec::new();
879 t.encode(&mut buf).unwrap();
880 let dur_idx = buf.len() - 2;
882 buf[dur_idx] = 99;
883 assert!(TopicBuiltinTopicData::decode(&buf).is_err());
884 }
885
886 #[test]
887 fn decode_rejects_invalid_reliability_kind() {
888 let t = TopicBuiltinTopicData {
889 key: mk_guid(0),
890 name: "x".to_string(),
891 type_name: "y".to_string(),
892 durability: zerodds_qos::DurabilityKind::Volatile,
893 reliability: zerodds_qos::ReliabilityKind::Reliable,
894 };
895 let mut buf = Vec::new();
896 t.encode(&mut buf).unwrap();
897 *buf.last_mut().unwrap() = 99;
898 assert!(TopicBuiltinTopicData::decode(&buf).is_err());
899 }
900
901 #[test]
902 fn decode_rejects_invalid_ownership_kind() {
903 let p = PublicationBuiltinTopicData {
904 key: mk_guid(0),
905 participant_key: mk_guid(1),
906 topic_name: "T".to_string(),
907 type_name: "T".to_string(),
908 durability: zerodds_qos::DurabilityKind::Volatile,
909 reliability: zerodds_qos::ReliabilityKind::Reliable,
910 ownership: zerodds_qos::OwnershipKind::Shared,
911 ownership_strength: 0,
912 liveliness_lease_seconds: 0,
913 deadline_seconds: 0,
914 lifespan_seconds: 0,
915 partition: alloc::vec![],
916 };
917 let mut buf = Vec::new();
918 p.encode(&mut buf).unwrap();
919 buf[44] = 99;
922 assert!(PublicationBuiltinTopicData::decode(&buf).is_err());
923 }
924}