use crate::error::WireError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ProtocolVersion {
pub major: u8,
pub minor: u8,
}
impl ProtocolVersion {
pub const WIRE_SIZE: usize = 2;
pub const V1_0: Self = Self { major: 1, minor: 0 };
pub const V1_1: Self = Self { major: 1, minor: 1 };
pub const V2_0: Self = Self { major: 2, minor: 0 };
pub const V2_1: Self = Self { major: 2, minor: 1 };
pub const V2_2: Self = Self { major: 2, minor: 2 };
pub const V2_3: Self = Self { major: 2, minor: 3 };
pub const V2_4: Self = Self { major: 2, minor: 4 };
pub const V2_5: Self = Self { major: 2, minor: 5 };
pub const CURRENT: Self = Self::V2_5;
#[must_use]
pub fn to_bytes(self) -> [u8; 2] {
[self.major, self.minor]
}
#[must_use]
pub fn from_bytes(bytes: [u8; 2]) -> Self {
Self {
major: bytes[0],
minor: bytes[1],
}
}
}
impl Default for ProtocolVersion {
fn default() -> Self {
Self::V2_5
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VendorId(pub [u8; 2]);
impl VendorId {
pub const WIRE_SIZE: usize = 2;
pub const UNKNOWN: Self = Self([0, 0]);
pub const ZERODDS: Self = Self([0x01, 0xF0]);
#[must_use]
pub fn to_bytes(self) -> [u8; 2] {
self.0
}
#[must_use]
pub fn from_bytes(bytes: [u8; 2]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GuidPrefix(pub [u8; 12]);
impl GuidPrefix {
pub const WIRE_SIZE: usize = 12;
pub const UNKNOWN: Self = Self([0; 12]);
#[must_use]
pub fn to_bytes(self) -> [u8; 12] {
self.0
}
#[must_use]
pub fn from_bytes(bytes: [u8; 12]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
#[allow(missing_docs)]
pub enum EntityKind {
Unknown = 0x00,
UserWriterNoKey = 0x03,
UserWriterWithKey = 0x02,
UserReaderNoKey = 0x04,
UserReaderWithKey = 0x07,
BuiltinWriterNoKey = 0xC3,
BuiltinWriterWithKey = 0xC2,
BuiltinReaderNoKey = 0xC4,
BuiltinReaderWithKey = 0xC7,
Participant = 0xC1,
}
impl EntityKind {
#[must_use]
pub fn from_byte(b: u8) -> Self {
match b {
0x03 => Self::UserWriterNoKey,
0x02 => Self::UserWriterWithKey,
0x04 => Self::UserReaderNoKey,
0x07 => Self::UserReaderWithKey,
0xC3 => Self::BuiltinWriterNoKey,
0xC2 => Self::BuiltinWriterWithKey,
0xC4 => Self::BuiltinReaderNoKey,
0xC7 => Self::BuiltinReaderWithKey,
0xC1 => Self::Participant,
_ => Self::Unknown,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EntityId {
pub entity_key: [u8; 3],
pub entity_kind: EntityKind,
}
impl EntityId {
pub const WIRE_SIZE: usize = 4;
pub const UNKNOWN: Self = Self {
entity_key: [0; 3],
entity_kind: EntityKind::Unknown,
};
pub const PARTICIPANT: Self = Self {
entity_key: [0, 0, 1],
entity_kind: EntityKind::Participant,
};
#[must_use]
pub const fn user_writer_with_key(key: [u8; 3]) -> Self {
Self {
entity_key: key,
entity_kind: EntityKind::UserWriterWithKey,
}
}
#[must_use]
pub const fn user_reader_with_key(key: [u8; 3]) -> Self {
Self {
entity_key: key,
entity_kind: EntityKind::UserReaderWithKey,
}
}
pub const SPDP_BUILTIN_PARTICIPANT_WRITER: Self = Self {
entity_key: [0, 0x01, 0x00],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SPDP_BUILTIN_PARTICIPANT_READER: Self = Self {
entity_key: [0, 0x01, 0x00],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const SEDP_BUILTIN_SUBSCRIPTIONS_WRITER: Self = Self {
entity_key: [0, 0x00, 0x04],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SEDP_BUILTIN_SUBSCRIPTIONS_READER: Self = Self {
entity_key: [0, 0x00, 0x04],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const SEDP_BUILTIN_PUBLICATIONS_WRITER: Self = Self {
entity_key: [0, 0x00, 0x03],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SEDP_BUILTIN_PUBLICATIONS_READER: Self = Self {
entity_key: [0, 0x00, 0x03],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const BUILTIN_PARTICIPANT_MESSAGE_WRITER: Self = Self {
entity_key: [0, 0x02, 0x00],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const BUILTIN_PARTICIPANT_MESSAGE_READER: Self = Self {
entity_key: [0, 0x02, 0x00],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const TL_SVC_REQ_WRITER: Self = Self {
entity_key: [0, 0x03, 0x00],
entity_kind: EntityKind::BuiltinWriterNoKey,
};
pub const TL_SVC_REQ_READER: Self = Self {
entity_key: [0, 0x03, 0x00],
entity_kind: EntityKind::BuiltinReaderNoKey,
};
pub const TL_SVC_REPLY_WRITER: Self = Self {
entity_key: [0, 0x03, 0x01],
entity_kind: EntityKind::BuiltinWriterNoKey,
};
pub const TL_SVC_REPLY_READER: Self = Self {
entity_key: [0, 0x03, 0x01],
entity_kind: EntityKind::BuiltinReaderNoKey,
};
pub const SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER: Self = Self {
entity_key: [0xff, 0x00, 0x03],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SEDP_BUILTIN_PUBLICATIONS_SECURE_READER: Self = Self {
entity_key: [0xff, 0x00, 0x03],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER: Self = Self {
entity_key: [0xff, 0x00, 0x04],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_READER: Self = Self {
entity_key: [0xff, 0x00, 0x04],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER: Self = Self {
entity_key: [0xff, 0x02, 0x00],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const BUILTIN_PARTICIPANT_MESSAGE_SECURE_READER: Self = Self {
entity_key: [0xff, 0x02, 0x00],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const BUILTIN_PARTICIPANT_STATELESS_MESSAGE_WRITER: Self = Self {
entity_key: [0x00, 0x02, 0x01],
entity_kind: EntityKind::BuiltinWriterNoKey,
};
pub const BUILTIN_PARTICIPANT_STATELESS_MESSAGE_READER: Self = Self {
entity_key: [0x00, 0x02, 0x01],
entity_kind: EntityKind::BuiltinReaderNoKey,
};
pub const BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER: Self = Self {
entity_key: [0xff, 0x02, 0x02],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER: Self = Self {
entity_key: [0xff, 0x02, 0x02],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
pub const SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_WRITER: Self = Self {
entity_key: [0xff, 0x01, 0x01],
entity_kind: EntityKind::BuiltinWriterWithKey,
};
pub const SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_READER: Self = Self {
entity_key: [0xff, 0x01, 0x01],
entity_kind: EntityKind::BuiltinReaderWithKey,
};
#[must_use]
pub const fn is_secure_builtin(self) -> bool {
matches!(
self,
Self::SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER
| Self::SEDP_BUILTIN_PUBLICATIONS_SECURE_READER
| Self::SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER
| Self::SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_READER
| Self::BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER
| Self::BUILTIN_PARTICIPANT_MESSAGE_SECURE_READER
| Self::BUILTIN_PARTICIPANT_STATELESS_MESSAGE_WRITER
| Self::BUILTIN_PARTICIPANT_STATELESS_MESSAGE_READER
| Self::BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER
| Self::BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER
| Self::SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_WRITER
| Self::SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_READER
)
}
#[must_use]
pub fn to_bytes(self) -> [u8; 4] {
[
self.entity_key[0],
self.entity_key[1],
self.entity_key[2],
self.entity_kind as u8,
]
}
#[must_use]
pub fn from_bytes(bytes: [u8; 4]) -> Self {
Self {
entity_key: [bytes[0], bytes[1], bytes[2]],
entity_kind: EntityKind::from_byte(bytes[3]),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Guid {
pub prefix: GuidPrefix,
pub entity_id: EntityId,
}
impl Guid {
pub const WIRE_SIZE: usize = 16;
pub const UNKNOWN: Self = Self {
prefix: GuidPrefix::UNKNOWN,
entity_id: EntityId::UNKNOWN,
};
#[must_use]
pub const fn new(prefix: GuidPrefix, entity_id: EntityId) -> Self {
Self { prefix, entity_id }
}
#[must_use]
pub fn to_bytes(self) -> [u8; 16] {
let mut out = [0u8; 16];
out[..12].copy_from_slice(&self.prefix.to_bytes());
out[12..].copy_from_slice(&self.entity_id.to_bytes());
out
}
#[must_use]
pub fn from_bytes(bytes: [u8; 16]) -> Self {
let mut prefix_bytes = [0u8; 12];
prefix_bytes.copy_from_slice(&bytes[..12]);
let mut entity_bytes = [0u8; 4];
entity_bytes.copy_from_slice(&bytes[12..]);
Self {
prefix: GuidPrefix::from_bytes(prefix_bytes),
entity_id: EntityId::from_bytes(entity_bytes),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct SequenceNumber(pub i64);
impl SequenceNumber {
pub const WIRE_SIZE: usize = 8;
pub const UNKNOWN: Self = Self(-(1_i64 << 32));
#[must_use]
pub fn split(self) -> (i32, u32) {
let value = self.0;
let high = (value >> 32) as i32;
let low = (value & 0xFFFF_FFFF) as u32;
(high, low)
}
#[must_use]
pub fn from_high_low(high: i32, low: u32) -> Self {
let value = (i64::from(high) << 32) | i64::from(low);
Self(value)
}
#[must_use]
pub fn to_bytes_le(self) -> [u8; 8] {
let (high, low) = self.split();
let mut out = [0u8; 8];
out[..4].copy_from_slice(&high.to_le_bytes());
out[4..].copy_from_slice(&low.to_le_bytes());
out
}
#[must_use]
pub fn to_bytes_be(self) -> [u8; 8] {
let (high, low) = self.split();
let mut out = [0u8; 8];
out[..4].copy_from_slice(&high.to_be_bytes());
out[4..].copy_from_slice(&low.to_be_bytes());
out
}
#[must_use]
pub fn from_bytes_le(bytes: [u8; 8]) -> Self {
let mut hi = [0u8; 4];
hi.copy_from_slice(&bytes[..4]);
let mut lo = [0u8; 4];
lo.copy_from_slice(&bytes[4..]);
Self::from_high_low(i32::from_le_bytes(hi), u32::from_le_bytes(lo))
}
#[must_use]
pub fn from_bytes_be(bytes: [u8; 8]) -> Self {
let mut hi = [0u8; 4];
hi.copy_from_slice(&bytes[..4]);
let mut lo = [0u8; 4];
lo.copy_from_slice(&bytes[4..]);
Self::from_high_low(i32::from_be_bytes(hi), u32::from_be_bytes(lo))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct UExtension4(pub [u8; 4]);
impl UExtension4 {
pub const WIRE_SIZE: usize = 4;
#[must_use]
pub fn from_u32_be(v: u32) -> Self {
Self(v.to_be_bytes())
}
#[must_use]
pub fn to_u32_be(self) -> u32 {
u32::from_be_bytes(self.0)
}
#[must_use]
pub fn to_bytes(self) -> [u8; 4] {
self.0
}
#[must_use]
pub fn from_bytes(bytes: [u8; 4]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct WExtension8(pub [u8; 8]);
impl WExtension8 {
pub const WIRE_SIZE: usize = 8;
#[must_use]
pub fn from_u64_be(v: u64) -> Self {
Self(v.to_be_bytes())
}
#[must_use]
pub fn to_u64_be(self) -> u64 {
u64::from_be_bytes(self.0)
}
#[must_use]
pub fn to_bytes(self) -> [u8; 8] {
self.0
}
#[must_use]
pub fn from_bytes(bytes: [u8; 8]) -> Self {
Self(bytes)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FragmentNumber(pub u32);
impl FragmentNumber {
pub const WIRE_SIZE: usize = 4;
pub const UNKNOWN: Self = Self(0);
#[must_use]
pub fn to_bytes_le(self) -> [u8; 4] {
self.0.to_le_bytes()
}
#[must_use]
pub fn to_bytes_be(self) -> [u8; 4] {
self.0.to_be_bytes()
}
#[must_use]
pub fn from_bytes_le(bytes: [u8; 4]) -> Self {
Self(u32::from_le_bytes(bytes))
}
#[must_use]
pub fn from_bytes_be(bytes: [u8; 4]) -> Self {
Self(u32::from_be_bytes(bytes))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(i32)]
#[allow(missing_docs)]
pub enum LocatorKind {
Invalid = -1,
Reserved = 0,
UdpV4 = 1,
UdpV6 = 2,
Tcpv4 = 4,
Tcpv6 = 8,
Shm = -2_130_706_432, Uds = -2_130_706_431, }
impl LocatorKind {
#[must_use]
pub fn as_i32(self) -> i32 {
self as i32
}
pub fn from_i32(v: i32) -> Result<Self, WireError> {
match v {
-1 => Ok(Self::Invalid),
0 => Ok(Self::Reserved),
1 => Ok(Self::UdpV4),
2 => Ok(Self::UdpV6),
4 => Ok(Self::Tcpv4),
8 => Ok(Self::Tcpv6),
-2_130_706_432 => Ok(Self::Shm),
-2_130_706_431 => Ok(Self::Uds),
other => Err(WireError::InvalidLocatorKind { kind: other }),
}
}
}
pub const SPDP_DEFAULT_MULTICAST_ADDRESS: [u8; 4] = [239, 255, 0, 1];
pub const SPDP_PORT_BASE: u32 = 7400;
pub const SPDP_DOMAIN_GAIN: u32 = 250;
pub const SPDP_DISCOVERY_MULTICAST_OFFSET: u32 = 0;
#[must_use]
pub fn spdp_multicast_port(domain_id: u32) -> u32 {
SPDP_PORT_BASE + SPDP_DOMAIN_GAIN * domain_id + SPDP_DISCOVERY_MULTICAST_OFFSET
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Locator {
pub kind: LocatorKind,
pub port: u32,
pub address: [u8; 16],
}
impl Locator {
pub const WIRE_SIZE: usize = 24;
pub const INVALID: Self = Self {
kind: LocatorKind::Invalid,
port: 0,
address: [0; 16],
};
pub const RESERVED: Self = Self {
kind: LocatorKind::Reserved,
port: 0,
address: [0; 16],
};
pub const UDP_V4_ANY: Self = Self {
kind: LocatorKind::UdpV4,
port: 0,
address: [0; 16],
};
pub const UDP_V6_ANY: Self = Self {
kind: LocatorKind::UdpV6,
port: 0,
address: [0; 16],
};
pub const SHM_ANY: Self = Self {
kind: LocatorKind::Shm,
port: 0,
address: [0; 16],
};
pub const PORT_INVALID: u32 = 0;
pub const ADDRESS_INVALID: [u8; 16] = [0; 16];
#[must_use]
pub fn udp_v6(addr: [u8; 16], port: u32) -> Self {
Self {
kind: LocatorKind::UdpV6,
port,
address: addr,
}
}
#[must_use]
pub fn udp_v4(addr: [u8; 4], port: u32) -> Self {
Self::with_address(LocatorKind::UdpV4, addr, port)
}
#[must_use]
pub fn tcp_v4(addr: [u8; 4], port: u32) -> Self {
Self::with_address(LocatorKind::Tcpv4, addr, port)
}
#[must_use]
#[deprecated(note = "use Locator::tcp_v4 instead (naming consistency)")]
pub fn new_tcp_v4(addr: [u8; 4], port: u32) -> Self {
Self::tcp_v4(addr, port)
}
#[must_use]
pub fn uds(id: [u8; 16]) -> Self {
Self {
kind: LocatorKind::Uds,
port: 0,
address: id,
}
}
#[must_use]
pub fn shm(id: [u8; 16]) -> Self {
Self {
kind: LocatorKind::Shm,
port: 0,
address: id,
}
}
#[must_use]
fn with_address(kind: LocatorKind, addr: [u8; 4], port: u32) -> Self {
let mut address = [0u8; 16];
address[12..].copy_from_slice(&addr);
Self {
kind,
port,
address,
}
}
#[must_use]
pub fn ipv4(self) -> [u8; 4] {
let mut out = [0u8; 4];
out.copy_from_slice(&self.address[12..]);
out
}
#[must_use]
pub fn to_bytes_le(self) -> [u8; 24] {
let mut out = [0u8; 24];
out[..4].copy_from_slice(&self.kind.as_i32().to_le_bytes());
out[4..8].copy_from_slice(&self.port.to_le_bytes());
out[8..].copy_from_slice(&self.address);
out
}
pub fn from_bytes_le(bytes: [u8; 24]) -> Result<Self, WireError> {
let mut kind_bytes = [0u8; 4];
kind_bytes.copy_from_slice(&bytes[..4]);
let kind = LocatorKind::from_i32(i32::from_le_bytes(kind_bytes))?;
let mut port_bytes = [0u8; 4];
port_bytes.copy_from_slice(&bytes[4..8]);
let port = u32::from_le_bytes(port_bytes);
let mut address = [0u8; 16];
address.copy_from_slice(&bytes[8..]);
Ok(Self {
kind,
port,
address,
})
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use super::*;
#[test]
fn protocol_version_default_is_2_5() {
assert_eq!(ProtocolVersion::default(), ProtocolVersion::V2_5);
}
#[test]
fn protocol_version_roundtrip() {
let v = ProtocolVersion::V2_5;
assert_eq!(ProtocolVersion::from_bytes(v.to_bytes()), v);
}
#[test]
fn vendor_id_zerodds_constant() {
assert_eq!(VendorId::ZERODDS.0, [0x01, 0xF0]);
}
#[test]
fn vendor_id_roundtrip() {
let v = VendorId([0xAB, 0xCD]);
assert_eq!(VendorId::from_bytes(v.to_bytes()), v);
}
#[test]
fn guid_prefix_unknown_is_zero() {
assert_eq!(GuidPrefix::UNKNOWN.0, [0u8; 12]);
}
#[test]
fn guid_prefix_roundtrip() {
let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let p = GuidPrefix::from_bytes(bytes);
assert_eq!(p.to_bytes(), bytes);
}
#[test]
fn entity_id_user_writer_with_key_layout() {
let id = EntityId::user_writer_with_key([0xAA, 0xBB, 0xCC]);
assert_eq!(id.to_bytes(), [0xAA, 0xBB, 0xCC, 0x02]);
}
#[test]
fn entity_id_user_reader_with_key_layout() {
let id = EntityId::user_reader_with_key([0x11, 0x22, 0x33]);
assert_eq!(id.to_bytes(), [0x11, 0x22, 0x33, 0x07]);
}
#[test]
fn entity_id_participant_constant() {
assert_eq!(EntityId::PARTICIPANT.to_bytes(), [0, 0, 1, 0xC1]);
}
#[test]
fn entity_id_unknown_kind_byte_maps_to_unknown() {
let id = EntityId::from_bytes([1, 2, 3, 0xEE]);
assert_eq!(id.entity_kind, EntityKind::Unknown);
}
#[test]
fn entity_id_roundtrip() {
let id = EntityId::user_writer_with_key([1, 2, 3]);
assert_eq!(EntityId::from_bytes(id.to_bytes()), id);
}
#[test]
fn secure_publications_writer_layout() {
let id = EntityId::SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER;
assert_eq!(id.to_bytes(), [0xff, 0x00, 0x03, 0xC2]);
}
#[test]
fn stateless_writer_is_no_key_kind() {
let id = EntityId::BUILTIN_PARTICIPANT_STATELESS_MESSAGE_WRITER;
assert_eq!(id.entity_kind, EntityKind::BuiltinWriterNoKey);
assert_eq!(id.to_bytes(), [0x00, 0x02, 0x01, 0xC3]);
}
#[test]
fn volatile_secure_writer_layout() {
let id = EntityId::BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER;
assert_eq!(id.to_bytes(), [0xff, 0x02, 0x02, 0xC2]);
}
#[test]
fn spdp_secure_reader_layout() {
let id = EntityId::SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_READER;
assert_eq!(id.to_bytes(), [0xff, 0x01, 0x01, 0xC7]);
}
#[test]
fn all_12_secure_entityids_roundtrip() {
let ids = [
EntityId::SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER,
EntityId::SEDP_BUILTIN_PUBLICATIONS_SECURE_READER,
EntityId::SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER,
EntityId::SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_READER,
EntityId::BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER,
EntityId::BUILTIN_PARTICIPANT_MESSAGE_SECURE_READER,
EntityId::BUILTIN_PARTICIPANT_STATELESS_MESSAGE_WRITER,
EntityId::BUILTIN_PARTICIPANT_STATELESS_MESSAGE_READER,
EntityId::BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_WRITER,
EntityId::BUILTIN_PARTICIPANT_VOLATILE_MESSAGE_SECURE_READER,
EntityId::SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_WRITER,
EntityId::SPDP_RELIABLE_BUILTIN_PARTICIPANTS_SECURE_READER,
];
assert_eq!(ids.len(), 12);
for id in ids {
assert!(id.is_secure_builtin(), "{id:?}");
assert_eq!(EntityId::from_bytes(id.to_bytes()), id);
}
}
#[test]
fn standard_builtin_is_not_secure_builtin() {
for id in [
EntityId::SPDP_BUILTIN_PARTICIPANT_WRITER,
EntityId::SEDP_BUILTIN_PUBLICATIONS_WRITER,
EntityId::BUILTIN_PARTICIPANT_MESSAGE_WRITER,
EntityId::PARTICIPANT,
EntityId::user_writer_with_key([1, 2, 3]),
] {
assert!(!id.is_secure_builtin(), "{id:?} should not be secure");
}
}
#[test]
fn guid_layout_is_prefix_then_entity_id() {
let g = Guid::new(
GuidPrefix::from_bytes([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
EntityId::user_writer_with_key([0xAA, 0xBB, 0xCC]),
);
let bytes = g.to_bytes();
assert_eq!(&bytes[..12], &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
assert_eq!(&bytes[12..], &[0xAA, 0xBB, 0xCC, 0x02]);
}
#[test]
fn guid_roundtrip() {
let g = Guid::new(GuidPrefix::from_bytes([42; 12]), EntityId::PARTICIPANT);
assert_eq!(Guid::from_bytes(g.to_bytes()), g);
}
#[test]
fn sequence_number_split_zero() {
let (h, l) = SequenceNumber(0).split();
assert_eq!((h, l), (0, 0));
}
#[test]
fn sequence_number_split_one() {
let (h, l) = SequenceNumber(1).split();
assert_eq!((h, l), (0, 1));
}
#[test]
fn sequence_number_split_high_low() {
let sn = SequenceNumber::from_high_low(2, 5);
assert_eq!(sn.0, (2_i64 << 32) | 5);
}
#[test]
fn sequence_number_roundtrip_le() {
let sn = SequenceNumber(0x0102_0304_0506_0708);
assert_eq!(SequenceNumber::from_bytes_le(sn.to_bytes_le()), sn);
}
#[test]
fn sequence_number_roundtrip_be() {
let sn = SequenceNumber(0x0102_0304_0506_0708);
assert_eq!(SequenceNumber::from_bytes_be(sn.to_bytes_be()), sn);
}
#[test]
fn sequence_number_unknown_high_minus_one_low_zero() {
let (h, l) = SequenceNumber::UNKNOWN.split();
assert_eq!((h, l), (-1, 0));
}
#[test]
fn fragment_number_roundtrip_le_be() {
let fns = [
FragmentNumber(0),
FragmentNumber(1),
FragmentNumber(0x1234_5678),
FragmentNumber(u32::MAX),
];
for fnum in fns {
assert_eq!(FragmentNumber::from_bytes_le(fnum.to_bytes_le()), fnum);
assert_eq!(FragmentNumber::from_bytes_be(fnum.to_bytes_be()), fnum);
}
}
#[test]
fn fragment_number_unknown_is_zero() {
assert_eq!(FragmentNumber::UNKNOWN.0, 0);
}
#[test]
fn fragment_number_wire_size_is_four() {
assert_eq!(FragmentNumber::WIRE_SIZE, 4);
}
#[test]
fn locator_kind_roundtrip() {
for kind in [
LocatorKind::Invalid,
LocatorKind::Reserved,
LocatorKind::UdpV4,
LocatorKind::UdpV6,
LocatorKind::Tcpv4,
LocatorKind::Tcpv6,
LocatorKind::Shm,
LocatorKind::Uds,
] {
assert_eq!(LocatorKind::from_i32(kind.as_i32()).unwrap(), kind);
}
}
#[test]
fn locator_uds_layout() {
let id = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10,
];
let l = Locator::uds(id);
assert_eq!(l.kind, LocatorKind::Uds);
assert_eq!(l.port, 0);
assert_eq!(l.address, id);
}
#[test]
fn locator_kind_rejects_unknown() {
let res = LocatorKind::from_i32(99);
assert!(matches!(
res,
Err(WireError::InvalidLocatorKind { kind: 99 })
));
}
#[test]
fn locator_udp_v4_layout() {
let l = Locator::udp_v4([192, 168, 1, 100], 7400);
assert_eq!(l.kind, LocatorKind::UdpV4);
assert_eq!(l.port, 7400);
assert_eq!(&l.address[..12], &[0u8; 12]);
assert_eq!(&l.address[12..], &[192, 168, 1, 100]);
assert_eq!(l.ipv4(), [192, 168, 1, 100]);
}
#[test]
fn locator_roundtrip_le() {
let l = Locator::udp_v4([10, 0, 0, 1], 7400);
let bytes = l.to_bytes_le();
assert_eq!(Locator::from_bytes_le(bytes).unwrap(), l);
}
#[test]
fn locator_invalid_kind_decoded() {
let mut bytes = [0u8; 24];
bytes[..4].copy_from_slice(&99_i32.to_le_bytes());
let res = Locator::from_bytes_le(bytes);
assert!(matches!(
res,
Err(WireError::InvalidLocatorKind { kind: 99 })
));
}
#[test]
fn locator_invalid_constant_matches_spec() {
assert_eq!(Locator::INVALID.kind, LocatorKind::Invalid);
assert_eq!(Locator::INVALID.port, Locator::PORT_INVALID);
assert_eq!(Locator::INVALID.address, Locator::ADDRESS_INVALID);
}
#[test]
fn locator_reserved_constant_kind_is_zero() {
assert_eq!(Locator::RESERVED.kind.as_i32(), 0);
}
#[test]
fn locator_udp_v4_any_kind_is_one() {
assert_eq!(Locator::UDP_V4_ANY.kind.as_i32(), 1);
assert_eq!(Locator::UDP_V4_ANY.port, 0);
}
#[test]
fn locator_udp_v6_any_kind_is_two() {
assert_eq!(Locator::UDP_V6_ANY.kind.as_i32(), 2);
}
#[test]
fn locator_shm_any_uses_vendor_kind() {
assert!(Locator::SHM_ANY.kind.as_i32() < 0); }
#[test]
fn locator_udp_v6_constructor_keeps_full_address() {
let addr: [u8; 16] = [
0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01,
];
let l = Locator::udp_v6(addr, 1234);
assert_eq!(l.kind, LocatorKind::UdpV6);
assert_eq!(l.address, addr);
assert_eq!(l.port, 1234);
}
#[test]
fn locator_udp_v6_roundtrip_le() {
let l = Locator::udp_v6([1; 16], 9000);
assert_eq!(Locator::from_bytes_le(l.to_bytes_le()).unwrap(), l);
}
#[test]
fn protocol_version_aliases_match_spec() {
assert_eq!(
ProtocolVersion::V1_0,
ProtocolVersion { major: 1, minor: 0 }
);
assert_eq!(
ProtocolVersion::V1_1,
ProtocolVersion { major: 1, minor: 1 }
);
assert_eq!(
ProtocolVersion::V2_0,
ProtocolVersion { major: 2, minor: 0 }
);
assert_eq!(
ProtocolVersion::V2_1,
ProtocolVersion { major: 2, minor: 1 }
);
assert_eq!(
ProtocolVersion::V2_2,
ProtocolVersion { major: 2, minor: 2 }
);
assert_eq!(
ProtocolVersion::V2_3,
ProtocolVersion { major: 2, minor: 3 }
);
assert_eq!(
ProtocolVersion::V2_4,
ProtocolVersion { major: 2, minor: 4 }
);
assert_eq!(
ProtocolVersion::V2_5,
ProtocolVersion { major: 2, minor: 5 }
);
assert_eq!(ProtocolVersion::CURRENT, ProtocolVersion::V2_5);
}
#[test]
fn protocol_version_all_aliases_roundtrip() {
for v in [
ProtocolVersion::V1_0,
ProtocolVersion::V1_1,
ProtocolVersion::V2_0,
ProtocolVersion::V2_1,
ProtocolVersion::V2_2,
ProtocolVersion::V2_3,
ProtocolVersion::V2_4,
ProtocolVersion::V2_5,
] {
assert_eq!(ProtocolVersion::from_bytes(v.to_bytes()), v);
}
}
#[test]
fn uextension4_roundtrip() {
let u = UExtension4([0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(UExtension4::from_bytes(u.to_bytes()), u);
}
#[test]
fn uextension4_u32_be_roundtrip() {
let u = UExtension4::from_u32_be(0x1122_3344);
assert_eq!(u.to_u32_be(), 0x1122_3344);
assert_eq!(u.0, [0x11, 0x22, 0x33, 0x44]);
}
#[test]
fn uextension4_default_is_zero() {
assert_eq!(UExtension4::default().0, [0u8; 4]);
assert_eq!(UExtension4::WIRE_SIZE, 4);
}
#[test]
fn wextension8_roundtrip() {
let w = WExtension8([1, 2, 3, 4, 5, 6, 7, 8]);
assert_eq!(WExtension8::from_bytes(w.to_bytes()), w);
}
#[test]
fn wextension8_u64_be_roundtrip() {
let w = WExtension8::from_u64_be(0x1122_3344_5566_7788);
assert_eq!(w.to_u64_be(), 0x1122_3344_5566_7788);
assert_eq!(w.0, [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]);
}
#[test]
fn wextension8_default_is_zero() {
assert_eq!(WExtension8::default().0, [0u8; 8]);
assert_eq!(WExtension8::WIRE_SIZE, 8);
}
#[test]
fn spdp_builtin_participant_writer_layout() {
let bytes = EntityId::SPDP_BUILTIN_PARTICIPANT_WRITER.to_bytes();
assert_eq!(bytes, [0x00, 0x01, 0x00, 0xC2]);
}
#[test]
fn spdp_builtin_participant_reader_layout() {
let bytes = EntityId::SPDP_BUILTIN_PARTICIPANT_READER.to_bytes();
assert_eq!(bytes, [0x00, 0x01, 0x00, 0xC7]);
}
#[test]
fn sedp_publications_writer_layout() {
let bytes = EntityId::SEDP_BUILTIN_PUBLICATIONS_WRITER.to_bytes();
assert_eq!(bytes, [0x00, 0x00, 0x03, 0xC2]);
}
#[test]
fn sedp_subscriptions_reader_layout() {
let bytes = EntityId::SEDP_BUILTIN_SUBSCRIPTIONS_READER.to_bytes();
assert_eq!(bytes, [0x00, 0x00, 0x04, 0xC7]);
}
#[test]
fn spdp_default_multicast_is_239_255_0_1() {
assert_eq!(SPDP_DEFAULT_MULTICAST_ADDRESS, [239, 255, 0, 1]);
}
#[test]
fn spdp_multicast_port_domain_0_is_7400() {
assert_eq!(spdp_multicast_port(0), 7400);
}
#[test]
fn spdp_multicast_port_domain_1_is_7650() {
assert_eq!(spdp_multicast_port(1), 7650);
}
#[test]
fn spdp_multicast_port_domain_5_is_8650() {
assert_eq!(spdp_multicast_port(5), 8650);
}
}