1extern crate alloc;
10use alloc::vec::Vec;
11
12use crate::error::WireError;
13use crate::parameter_list::{Parameter, ParameterList, pid};
14use crate::property_list::WirePropertyList;
15use crate::wire_types::{Guid, Locator, ProtocolVersion, VendorId};
16
17pub const ENCAPSULATION_PL_CDR_LE: [u8; 2] = [0x00, 0x03];
19
20pub mod endpoint_flag {
37 pub const PARTICIPANT_ANNOUNCER: u32 = 1 << 0;
43 pub const PARTICIPANT_DETECTOR: u32 = 1 << 1;
45 pub const PUBLICATIONS_ANNOUNCER: u32 = 1 << 2;
47 pub const PUBLICATIONS_DETECTOR: u32 = 1 << 3;
49 pub const SUBSCRIPTIONS_ANNOUNCER: u32 = 1 << 4;
51 pub const SUBSCRIPTIONS_DETECTOR: u32 = 1 << 5;
53
54 pub const PARTICIPANT_MESSAGE_DATA_WRITER: u32 = 1 << 10;
62 pub const PARTICIPANT_MESSAGE_DATA_READER: u32 = 1 << 11;
65
66 pub const TYPE_LOOKUP_REQUEST: u32 = 1 << 12;
74 pub const TYPE_LOOKUP_REPLY: u32 = 1 << 13;
78
79 pub const PUBLICATIONS_SECURE_WRITER: u32 = 1 << 16;
89 pub const PUBLICATIONS_SECURE_READER: u32 = 1 << 17;
92 pub const SUBSCRIPTIONS_SECURE_WRITER: u32 = 1 << 18;
95 pub const SUBSCRIPTIONS_SECURE_READER: u32 = 1 << 19;
98 pub const PARTICIPANT_MESSAGE_SECURE_WRITER: u32 = 1 << 20;
101 pub const PARTICIPANT_MESSAGE_SECURE_READER: u32 = 1 << 21;
104 pub const PARTICIPANT_STATELESS_MESSAGE_WRITER: u32 = 1 << 22;
107 pub const PARTICIPANT_STATELESS_MESSAGE_READER: u32 = 1 << 23;
110 pub const PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER: u32 = 1 << 24;
113 pub const PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER: u32 = 1 << 25;
116 pub const PARTICIPANT_SECURE_WRITER: u32 = 1 << 26;
119 pub const PARTICIPANT_SECURE_READER: u32 = 1 << 27;
122
123 pub const TOPICS_ANNOUNCER: u32 = 1 << 28;
130 pub const TOPICS_DETECTOR: u32 = 1 << 29;
133
134 pub const ALL_SECURE: u32 = PUBLICATIONS_SECURE_WRITER
141 | PUBLICATIONS_SECURE_READER
142 | SUBSCRIPTIONS_SECURE_WRITER
143 | SUBSCRIPTIONS_SECURE_READER
144 | PARTICIPANT_MESSAGE_SECURE_WRITER
145 | PARTICIPANT_MESSAGE_SECURE_READER
146 | PARTICIPANT_STATELESS_MESSAGE_WRITER
147 | PARTICIPANT_STATELESS_MESSAGE_READER
148 | PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER
149 | PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER
150 | PARTICIPANT_SECURE_WRITER
151 | PARTICIPANT_SECURE_READER;
152
153 pub const ALL_STANDARD: u32 = PARTICIPANT_ANNOUNCER
167 | PARTICIPANT_DETECTOR
168 | PUBLICATIONS_ANNOUNCER
169 | PUBLICATIONS_DETECTOR
170 | SUBSCRIPTIONS_ANNOUNCER
171 | SUBSCRIPTIONS_DETECTOR
172 | PARTICIPANT_MESSAGE_DATA_WRITER
173 | PARTICIPANT_MESSAGE_DATA_READER
174 | TYPE_LOOKUP_REQUEST
175 | TYPE_LOOKUP_REPLY;
176}
177
178pub use zerodds_qos::Duration;
184
185#[derive(Debug, Clone, PartialEq, Eq)]
187pub struct ParticipantBuiltinTopicData {
188 pub guid: Guid,
190 pub protocol_version: ProtocolVersion,
192 pub vendor_id: VendorId,
194 pub default_unicast_locator: Option<Locator>,
196 pub default_multicast_locator: Option<Locator>,
198 pub metatraffic_unicast_locator: Option<Locator>,
202 pub metatraffic_multicast_locator: Option<Locator>,
204 pub domain_id: Option<u32>,
207 pub builtin_endpoint_set: u32,
210 pub lease_duration: Duration,
212 pub user_data: Vec<u8>,
215 pub properties: WirePropertyList,
220 pub identity_token: Option<Vec<u8>>,
226 pub permissions_token: Option<Vec<u8>>,
229 pub identity_status_token: Option<Vec<u8>>,
233 pub sig_algo_info:
237 Option<crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo>,
238 pub kx_algo_info:
242 Option<crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo>,
243 pub sym_cipher_algo_info:
247 Option<crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo>,
248}
249
250impl ParticipantBuiltinTopicData {
251 #[must_use]
255 pub fn to_pl_cdr_le(&self) -> Vec<u8> {
256 let mut params = ParameterList::new();
257
258 let mut pv = Vec::with_capacity(4);
260 pv.extend_from_slice(&self.protocol_version.to_bytes());
261 pv.extend_from_slice(&[0, 0]);
262 params.push(Parameter::new(pid::PROTOCOL_VERSION, pv));
263
264 let mut vid = Vec::with_capacity(4);
266 vid.extend_from_slice(&self.vendor_id.to_bytes());
267 vid.extend_from_slice(&[0, 0]);
268 params.push(Parameter::new(pid::VENDOR_ID, vid));
269
270 params.push(Parameter::new(
272 pid::PARTICIPANT_GUID,
273 self.guid.to_bytes().to_vec(),
274 ));
275
276 if let Some(loc) = self.default_unicast_locator {
278 params.push(Parameter::new(
279 pid::DEFAULT_UNICAST_LOCATOR,
280 loc.to_bytes_le().to_vec(),
281 ));
282 }
283
284 if let Some(loc) = self.default_multicast_locator {
286 params.push(Parameter::new(
287 pid::DEFAULT_MULTICAST_LOCATOR,
288 loc.to_bytes_le().to_vec(),
289 ));
290 }
291
292 if let Some(loc) = self.metatraffic_unicast_locator {
294 params.push(Parameter::new(
295 pid::METATRAFFIC_UNICAST_LOCATOR,
296 loc.to_bytes_le().to_vec(),
297 ));
298 }
299
300 if let Some(loc) = self.metatraffic_multicast_locator {
302 params.push(Parameter::new(
303 pid::METATRAFFIC_MULTICAST_LOCATOR,
304 loc.to_bytes_le().to_vec(),
305 ));
306 }
307
308 if let Some(dom) = self.domain_id {
310 params.push(Parameter::new(pid::DOMAIN_ID, dom.to_le_bytes().to_vec()));
311 }
312
313 params.push(Parameter::new(
315 pid::BUILTIN_ENDPOINT_SET,
316 self.builtin_endpoint_set.to_le_bytes().to_vec(),
317 ));
318
319 params.push(Parameter::new(
321 pid::PARTICIPANT_LEASE_DURATION,
322 self.lease_duration.to_bytes_le().to_vec(),
323 ));
324
325 if !self.user_data.is_empty() {
327 if let Ok(v) = crate::publication_data::encode_octet_seq_le(&self.user_data) {
328 params.push(Parameter::new(pid::USER_DATA, v));
329 }
330 }
331
332 if let Some(blob) = self.identity_token.as_ref() {
336 params.push(Parameter::new(pid::IDENTITY_TOKEN, blob.clone()));
337 }
338 if let Some(blob) = self.permissions_token.as_ref() {
339 params.push(Parameter::new(pid::PERMISSIONS_TOKEN, blob.clone()));
340 }
341 if let Some(blob) = self.identity_status_token.as_ref() {
342 params.push(Parameter::new(pid::IDENTITY_STATUS_TOKEN, blob.clone()));
343 }
344
345 if let Some(sig) = self.sig_algo_info.as_ref() {
348 params.push(Parameter::new(
349 pid::PARTICIPANT_SECURITY_DIGITAL_SIGNATURE_ALGORITHM_INFO,
350 sig.to_bytes(true).to_vec(),
351 ));
352 }
353 if let Some(kx) = self.kx_algo_info.as_ref() {
354 params.push(Parameter::new(
355 pid::PARTICIPANT_SECURITY_KEY_ESTABLISHMENT_ALGORITHM_INFO,
356 kx.to_bytes(true).to_vec(),
357 ));
358 }
359 if let Some(sym) = self.sym_cipher_algo_info.as_ref() {
360 params.push(Parameter::new(
361 pid::PARTICIPANT_SECURITY_SYMMETRIC_CIPHER_ALGORITHM_INFO,
362 sym.to_bytes(true).to_vec(),
363 ));
364 }
365
366 if !self.properties.is_empty() {
372 if let Ok(bytes) = self.properties.encode(true) {
373 params.push(Parameter::new(pid::PROPERTY_LIST, bytes));
374 }
375 }
376
377 let mut out = Vec::new();
379 out.extend_from_slice(&ENCAPSULATION_PL_CDR_LE);
380 out.extend_from_slice(&[0, 0]); out.extend_from_slice(¶ms.to_bytes(true));
382 out
383 }
384
385 pub fn from_pl_cdr_le(bytes: &[u8]) -> Result<Self, WireError> {
391 if bytes.len() < 4 {
392 return Err(WireError::UnexpectedEof {
393 needed: 4,
394 offset: 0,
395 });
396 }
397 let little_endian = match &bytes[..2] {
400 b if b == ENCAPSULATION_PL_CDR_LE => true,
401 [0x00, 0x02] => false,
402 other => {
403 return Err(WireError::UnsupportedEncapsulation {
404 kind: [other[0], other[1]],
405 });
406 }
407 };
408 let pl = ParameterList::from_bytes(&bytes[4..], little_endian)?;
409
410 let guid = pl
411 .find(pid::PARTICIPANT_GUID)
412 .and_then(|p| {
413 if p.value.len() == 16 {
414 let mut g = [0u8; 16];
415 g.copy_from_slice(&p.value);
416 Some(Guid::from_bytes(g))
417 } else {
418 None
419 }
420 })
421 .ok_or(WireError::ValueOutOfRange {
422 message: "PARTICIPANT_GUID missing or wrong length",
423 })?;
424
425 let protocol_version = pl
426 .find(pid::PROTOCOL_VERSION)
427 .and_then(|p| {
428 if p.value.len() >= 2 {
429 let mut bs = [0u8; 2];
430 bs.copy_from_slice(&p.value[..2]);
431 Some(ProtocolVersion::from_bytes(bs))
432 } else {
433 None
434 }
435 })
436 .unwrap_or_default();
437
438 let vendor_id = pl
439 .find(pid::VENDOR_ID)
440 .and_then(|p| {
441 if p.value.len() >= 2 {
442 let mut bs = [0u8; 2];
443 bs.copy_from_slice(&p.value[..2]);
444 Some(VendorId::from_bytes(bs))
445 } else {
446 None
447 }
448 })
449 .unwrap_or(VendorId::UNKNOWN);
450
451 let default_unicast_locator = pl
452 .find(pid::DEFAULT_UNICAST_LOCATOR)
453 .and_then(|p| decode_locator(&p.value, little_endian));
454
455 let default_multicast_locator = pl
456 .find(pid::DEFAULT_MULTICAST_LOCATOR)
457 .and_then(|p| decode_locator(&p.value, little_endian));
458
459 let metatraffic_unicast_locator = pl
460 .find(pid::METATRAFFIC_UNICAST_LOCATOR)
461 .and_then(|p| decode_locator(&p.value, little_endian));
462
463 let metatraffic_multicast_locator = pl
464 .find(pid::METATRAFFIC_MULTICAST_LOCATOR)
465 .and_then(|p| decode_locator(&p.value, little_endian));
466
467 let domain_id = pl.find(pid::DOMAIN_ID).and_then(|p| {
468 if p.value.len() == 4 {
469 let mut bs = [0u8; 4];
470 bs.copy_from_slice(&p.value);
471 Some(if little_endian {
472 u32::from_le_bytes(bs)
473 } else {
474 u32::from_be_bytes(bs)
475 })
476 } else {
477 None
478 }
479 });
480
481 let builtin_endpoint_set = pl
482 .find(pid::BUILTIN_ENDPOINT_SET)
483 .and_then(|p| {
484 if p.value.len() == 4 {
485 let mut bs = [0u8; 4];
486 bs.copy_from_slice(&p.value);
487 Some(if little_endian {
488 u32::from_le_bytes(bs)
489 } else {
490 u32::from_be_bytes(bs)
491 })
492 } else {
493 None
494 }
495 })
496 .unwrap_or(0);
497
498 let lease_duration = pl
499 .find(pid::PARTICIPANT_LEASE_DURATION)
500 .and_then(|p| {
501 if p.value.len() == 8 {
502 let mut bs = [0u8; 8];
503 bs.copy_from_slice(&p.value);
504 Some(Duration::from_bytes_le(bs))
505 } else {
506 None
507 }
508 })
509 .unwrap_or(Duration::from_secs(100));
510
511 let user_data = pl
512 .find(pid::USER_DATA)
513 .and_then(|p| crate::publication_data::decode_octet_seq(&p.value, little_endian))
514 .unwrap_or_default();
515
516 let properties = pl
522 .find(pid::PROPERTY_LIST)
523 .and_then(|p| WirePropertyList::decode(&p.value, little_endian).ok())
524 .unwrap_or_default();
525
526 let identity_token = pl.find(pid::IDENTITY_TOKEN).map(|p| p.value.clone());
530 let permissions_token = pl.find(pid::PERMISSIONS_TOKEN).map(|p| p.value.clone());
531 let identity_status_token = pl.find(pid::IDENTITY_STATUS_TOKEN).map(|p| p.value.clone());
532
533 let sig_algo_info = pl
537 .find(pid::PARTICIPANT_SECURITY_DIGITAL_SIGNATURE_ALGORITHM_INFO)
538 .and_then(|p| {
539 crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(
540 &p.value,
541 little_endian,
542 )
543 .ok()
544 });
545 let kx_algo_info = pl
546 .find(pid::PARTICIPANT_SECURITY_KEY_ESTABLISHMENT_ALGORITHM_INFO)
547 .and_then(|p| {
548 crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::from_bytes(
549 &p.value,
550 little_endian,
551 )
552 .ok()
553 });
554 let sym_cipher_algo_info = pl
555 .find(pid::PARTICIPANT_SECURITY_SYMMETRIC_CIPHER_ALGORITHM_INFO)
556 .and_then(|p| {
557 crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(
558 &p.value,
559 little_endian,
560 )
561 .ok()
562 });
563
564 Ok(Self {
565 guid,
566 protocol_version,
567 vendor_id,
568 default_unicast_locator,
569 default_multicast_locator,
570 metatraffic_unicast_locator,
571 metatraffic_multicast_locator,
572 domain_id,
573 builtin_endpoint_set,
574 lease_duration,
575 user_data,
576 properties,
577 identity_token,
578 permissions_token,
579 identity_status_token,
580 sig_algo_info,
581 kx_algo_info,
582 sym_cipher_algo_info,
583 })
584 }
585}
586
587fn decode_locator(value: &[u8], little_endian: bool) -> Option<Locator> {
588 if value.len() != Locator::WIRE_SIZE {
589 return None;
590 }
591 if !little_endian {
592 return None;
594 }
595 let mut bs = [0u8; 24];
596 bs.copy_from_slice(value);
597 Locator::from_bytes_le(bs).ok()
598}
599
600#[cfg(test)]
601mod tests {
602 #![allow(clippy::expect_used, clippy::unwrap_used)]
603 use super::*;
604 use crate::wire_types::{EntityId, GuidPrefix};
605 use alloc::vec;
606
607 fn sample_data() -> ParticipantBuiltinTopicData {
608 ParticipantBuiltinTopicData {
609 guid: Guid::new(
610 GuidPrefix::from_bytes([0xA, 0xB, 0xC, 0xD, 1, 2, 3, 4, 5, 6, 7, 8]),
611 EntityId::PARTICIPANT,
612 ),
613 protocol_version: ProtocolVersion::V2_5,
614 vendor_id: VendorId::ZERODDS,
615 default_unicast_locator: Some(Locator::udp_v4([192, 168, 1, 100], 7410)),
616 default_multicast_locator: Some(Locator::udp_v4([239, 255, 0, 1], 7400)),
617 metatraffic_unicast_locator: None,
618 metatraffic_multicast_locator: None,
619 domain_id: None,
620 builtin_endpoint_set: endpoint_flag::PARTICIPANT_ANNOUNCER
621 | endpoint_flag::PARTICIPANT_DETECTOR,
622 lease_duration: Duration::from_secs(100),
623 user_data: alloc::vec::Vec::new(),
624 properties: Default::default(),
625 identity_token: None,
626 permissions_token: None,
627 identity_status_token: None,
628 sig_algo_info: None,
629 kx_algo_info: None,
630 sym_cipher_algo_info: None,
631 }
632 }
633
634 #[test]
635 fn duration_roundtrip_le() {
636 let d = Duration {
637 seconds: 30,
638 fraction: 500_000_000,
639 };
640 assert_eq!(Duration::from_bytes_le(d.to_bytes_le()), d);
641 }
642
643 #[test]
644 fn participant_data_roundtrip_full() {
645 let d = sample_data();
646 let bytes = d.to_pl_cdr_le();
647 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
648 assert_eq!(decoded, d);
649 }
650
651 #[test]
652 fn participant_data_first_4_bytes_are_pl_cdr_le_encapsulation() {
653 let d = sample_data();
654 let bytes = d.to_pl_cdr_le();
655 assert_eq!(&bytes[..4], &[0x00, 0x03, 0x00, 0x00]);
656 }
657
658 #[test]
659 fn participant_data_properties_roundtrip() {
660 use crate::property_list::WireProperty;
661 let mut d = sample_data();
662 d.properties.push(WireProperty::new(
663 "dds.sec.auth.plugin_class",
664 "DDS:Auth:PKI-DH:1.2",
665 ));
666 d.properties.push(WireProperty::new(
667 "zerodds.sec.offered_protection",
668 "ENCRYPT",
669 ));
670 let bytes = d.to_pl_cdr_le();
671 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
672 assert_eq!(decoded.properties, d.properties);
673 assert_eq!(
674 decoded.properties.get("zerodds.sec.offered_protection"),
675 Some("ENCRYPT")
676 );
677 }
678
679 #[test]
680 fn participant_data_empty_properties_omits_pid() {
681 let d = sample_data();
685 assert!(d.properties.is_empty());
686 let bytes = d.to_pl_cdr_le();
687 let has_pid = bytes.windows(2).any(|w| w == [0x59, 0x00]);
690 assert!(!has_pid, "leere properties muessen PID weglassen");
691 }
692
693 #[test]
694 fn participant_data_legacy_peer_without_properties_parses_ok() {
695 let d = sample_data();
699 let bytes = d.to_pl_cdr_le();
700 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
701 assert!(decoded.properties.is_empty());
702 }
703
704 #[test]
705 fn participant_data_identity_token_pid_roundtrip() {
706 let mut d = sample_data();
710 d.identity_token = Some(vec![0xCA, 0xFE, 0xBA, 0xBE, 0x01, 0x02, 0x03, 0x04]);
717 let bytes = d.to_pl_cdr_le();
718 let has_pid = bytes.windows(2).any(|w| w == [0x01, 0x10]);
720 assert!(has_pid, "PID_IDENTITY_TOKEN fehlt im PL_CDR_LE-Stream");
721 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
722 assert_eq!(decoded.identity_token, d.identity_token);
723 }
724
725 #[test]
726 fn participant_data_permissions_token_pid_roundtrip() {
727 let mut d = sample_data();
728 d.permissions_token = Some(vec![0xDE, 0xAD, 0xBE, 0xEF]);
729 let bytes = d.to_pl_cdr_le();
730 let has_pid = bytes.windows(2).any(|w| w == [0x02, 0x10]);
731 assert!(has_pid, "PID_PERMISSIONS_TOKEN fehlt");
732 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
733 assert_eq!(decoded.permissions_token, d.permissions_token);
734 }
735
736 #[test]
737 fn participant_data_identity_status_token_pid_roundtrip() {
738 let mut d = sample_data();
739 d.identity_status_token = Some(vec![0x77, 0x88, 0x99, 0xAA]);
740 let bytes = d.to_pl_cdr_le();
741 let has_pid = bytes.windows(2).any(|w| w == [0x06, 0x10]);
742 assert!(has_pid, "PID_IDENTITY_STATUS_TOKEN fehlt");
743 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
744 assert_eq!(decoded.identity_status_token, d.identity_status_token);
745 }
746
747 #[test]
748 fn participant_data_no_token_pids_in_legacy_announce() {
749 let d = sample_data();
752 let bytes = d.to_pl_cdr_le();
753 for pid_le in [[0x01u8, 0x10], [0x02, 0x10], [0x06, 0x10]] {
754 let found = bytes.windows(2).any(|w| w == pid_le);
755 assert!(
756 !found,
757 "Token-PID {pid_le:?} darf in Legacy nicht auftauchen"
758 );
759 }
760 }
761
762 #[test]
763 fn participant_data_three_tokens_combined_roundtrip() {
764 let mut d = sample_data();
767 d.identity_token = Some(vec![0x01; 64]);
768 d.permissions_token = Some(vec![0x02; 32]);
769 d.identity_status_token = Some(vec![0x03; 16]);
770 let bytes = d.to_pl_cdr_le();
771 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
772 assert_eq!(decoded, d);
773 }
774
775 #[test]
778 fn participant_data_sig_algo_info_roundtrip() {
779 let mut d = sample_data();
780 d.sig_algo_info =
781 Some(crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default());
782 let bytes = d.to_pl_cdr_le();
783 assert!(
785 bytes.windows(2).any(|w| w == [0x10, 0x10]),
786 "PID 0x1010 fehlt im PL_CDR_LE-Stream"
787 );
788 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
789 assert_eq!(decoded.sig_algo_info, d.sig_algo_info);
790 }
791
792 #[test]
793 fn participant_data_kx_algo_info_roundtrip() {
794 let mut d = sample_data();
795 d.kx_algo_info =
796 Some(crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default());
797 let bytes = d.to_pl_cdr_le();
798 assert!(
799 bytes.windows(2).any(|w| w == [0x11, 0x10]),
800 "PID 0x1011 fehlt"
801 );
802 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
803 assert_eq!(decoded.kx_algo_info, d.kx_algo_info);
804 }
805
806 #[test]
807 fn participant_data_sym_cipher_algo_info_roundtrip() {
808 let mut d = sample_data();
809 d.sym_cipher_algo_info =
810 Some(crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default());
811 let bytes = d.to_pl_cdr_le();
812 assert!(
813 bytes.windows(2).any(|w| w == [0x12, 0x10]),
814 "PID 0x1012 fehlt"
815 );
816 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
817 assert_eq!(decoded.sym_cipher_algo_info, d.sym_cipher_algo_info);
818 }
819
820 #[test]
821 fn participant_data_no_algo_info_in_legacy_announce() {
822 let d = sample_data();
824 let bytes = d.to_pl_cdr_le();
825 for pid_le in [[0x10u8, 0x10], [0x11, 0x10], [0x12, 0x10]] {
826 assert!(
827 !bytes.windows(2).any(|w| w == pid_le),
828 "Algo-PID {pid_le:?} darf in Legacy nicht auftauchen"
829 );
830 }
831 }
832
833 #[test]
834 fn participant_data_all_three_algo_infos_combined() {
835 let mut d = sample_data();
836 d.sig_algo_info =
837 Some(crate::security_algo_info::ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default());
838 d.kx_algo_info =
839 Some(crate::security_algo_info::ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default());
840 d.sym_cipher_algo_info =
841 Some(crate::security_algo_info::ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default());
842 let bytes = d.to_pl_cdr_le();
843 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
844 assert_eq!(decoded, d);
845 }
846
847 #[test]
848 fn participant_data_without_optional_locators() {
849 let mut d = sample_data();
850 d.default_unicast_locator = None;
851 d.default_multicast_locator = None;
852 let bytes = d.to_pl_cdr_le();
853 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
854 assert!(decoded.default_unicast_locator.is_none());
855 assert!(decoded.default_multicast_locator.is_none());
856 }
857
858 #[test]
859 fn participant_data_decode_rejects_unknown_encapsulation() {
860 let mut bytes = vec![0x99, 0x99, 0, 0]; bytes.extend_from_slice(&[0x01, 0x00, 0x00, 0x00]); let res = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes);
863 assert!(matches!(
864 res,
865 Err(WireError::UnsupportedEncapsulation { kind: [0x99, 0x99] })
866 ));
867 }
868
869 #[test]
870 fn participant_data_decode_requires_guid_pid() {
871 let bytes = vec![0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
873 let res = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes);
874 assert!(matches!(res, Err(WireError::ValueOutOfRange { .. })));
875 }
876
877 #[test]
878 fn endpoint_flags_have_distinct_bits() {
879 let flags = [
881 endpoint_flag::PARTICIPANT_ANNOUNCER,
882 endpoint_flag::PARTICIPANT_DETECTOR,
883 endpoint_flag::PUBLICATIONS_ANNOUNCER,
884 endpoint_flag::PUBLICATIONS_DETECTOR,
885 endpoint_flag::SUBSCRIPTIONS_ANNOUNCER,
886 endpoint_flag::SUBSCRIPTIONS_DETECTOR,
887 endpoint_flag::PARTICIPANT_MESSAGE_DATA_WRITER,
888 endpoint_flag::PARTICIPANT_MESSAGE_DATA_READER,
889 endpoint_flag::PUBLICATIONS_SECURE_WRITER,
890 endpoint_flag::PUBLICATIONS_SECURE_READER,
891 endpoint_flag::SUBSCRIPTIONS_SECURE_WRITER,
892 endpoint_flag::SUBSCRIPTIONS_SECURE_READER,
893 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_WRITER,
894 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_READER,
895 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_WRITER,
896 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_READER,
897 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER,
898 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER,
899 endpoint_flag::PARTICIPANT_SECURE_WRITER,
900 endpoint_flag::PARTICIPANT_SECURE_READER,
901 endpoint_flag::TOPICS_ANNOUNCER,
902 endpoint_flag::TOPICS_DETECTOR,
903 ];
904 for (i, &a) in flags.iter().enumerate() {
905 for &b in &flags[i + 1..] {
906 assert_eq!(a & b, 0, "flag bits must be distinct");
907 }
908 }
909 }
910
911 #[test]
912 fn endpoint_flag_bit_positions_match_spec() {
913 assert_eq!(endpoint_flag::PARTICIPANT_ANNOUNCER, 0x0000_0001);
918 assert_eq!(endpoint_flag::PARTICIPANT_DETECTOR, 0x0000_0002);
919 assert_eq!(endpoint_flag::PUBLICATIONS_ANNOUNCER, 0x0000_0004);
920 assert_eq!(endpoint_flag::PUBLICATIONS_DETECTOR, 0x0000_0008);
921 assert_eq!(endpoint_flag::SUBSCRIPTIONS_ANNOUNCER, 0x0000_0010);
922 assert_eq!(endpoint_flag::SUBSCRIPTIONS_DETECTOR, 0x0000_0020);
923 assert_eq!(endpoint_flag::PARTICIPANT_MESSAGE_DATA_WRITER, 0x0000_0400);
924 assert_eq!(endpoint_flag::PARTICIPANT_MESSAGE_DATA_READER, 0x0000_0800);
925 assert_eq!(endpoint_flag::PUBLICATIONS_SECURE_WRITER, 0x0001_0000);
926 assert_eq!(endpoint_flag::PUBLICATIONS_SECURE_READER, 0x0002_0000);
927 assert_eq!(endpoint_flag::SUBSCRIPTIONS_SECURE_WRITER, 0x0004_0000);
928 assert_eq!(endpoint_flag::SUBSCRIPTIONS_SECURE_READER, 0x0008_0000);
929 assert_eq!(
930 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_WRITER,
931 0x0010_0000
932 );
933 assert_eq!(
934 endpoint_flag::PARTICIPANT_MESSAGE_SECURE_READER,
935 0x0020_0000
936 );
937 assert_eq!(
938 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_WRITER,
939 0x0040_0000
940 );
941 assert_eq!(
942 endpoint_flag::PARTICIPANT_STATELESS_MESSAGE_READER,
943 0x0080_0000
944 );
945 assert_eq!(
946 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER,
947 0x0100_0000
948 );
949 assert_eq!(
950 endpoint_flag::PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER,
951 0x0200_0000
952 );
953 assert_eq!(endpoint_flag::PARTICIPANT_SECURE_WRITER, 0x0400_0000);
954 assert_eq!(endpoint_flag::PARTICIPANT_SECURE_READER, 0x0800_0000);
955 assert_eq!(endpoint_flag::TOPICS_ANNOUNCER, 0x1000_0000);
956 assert_eq!(endpoint_flag::TOPICS_DETECTOR, 0x2000_0000);
957 }
958
959 #[test]
960 fn endpoint_flag_all_secure_covers_bits_16_to_27() {
961 let mask = endpoint_flag::ALL_SECURE;
965 for bit in 16u32..=27 {
966 assert!(mask & (1u32 << bit) != 0, "bit {bit} fehlt in ALL_SECURE");
967 }
968 let outside_mask: u32 = !((1u32 << 28) - (1u32 << 16));
970 assert_eq!(
971 mask & outside_mask,
972 0,
973 "ALL_SECURE darf nur Bits 16..27 setzen"
974 );
975 }
976
977 #[test]
978 fn endpoint_flag_all_standard_excludes_secure_bits() {
979 assert_eq!(endpoint_flag::ALL_STANDARD & endpoint_flag::ALL_SECURE, 0);
983 }
984
985 #[test]
986 fn endpoint_flag_roundtrip_through_pl_cdr() {
987 let combined = endpoint_flag::ALL_STANDARD | endpoint_flag::ALL_SECURE;
990 let mut d = sample_data();
991 d.builtin_endpoint_set = combined;
992 let bytes = d.to_pl_cdr_le();
993 let decoded = ParticipantBuiltinTopicData::from_pl_cdr_le(&bytes).unwrap();
994 assert_eq!(decoded.builtin_endpoint_set, combined);
995 }
996}