Skip to main content

hdds_micro/rtps/
header.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (c) 2025-2026 naskel.com
3
4//! RTPS Header
5
6use super::types::{GuidPrefix, ProtocolVersion, VendorId};
7use crate::error::{Error, Result};
8
9/// RTPS magic number "RTPS"
10pub const RTPS_MAGIC: [u8; 4] = *b"RTPS";
11
12/// RTPS Header (20 bytes)
13///
14/// ```text
15/// 0...3: Magic "RTPS"
16/// 4...5: Protocol version (2.5)
17/// 6...7: Vendor ID
18/// 8..19: GUID Prefix
19/// ```
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21pub struct RtpsHeader {
22    /// Protocol version
23    pub protocol_version: ProtocolVersion,
24    /// Vendor ID
25    pub vendor_id: VendorId,
26    /// GUID prefix of sender
27    pub guid_prefix: GuidPrefix,
28}
29
30impl RtpsHeader {
31    /// Size of RTPS header in bytes
32    pub const SIZE: usize = 20;
33
34    /// Create a new RTPS header
35    pub const fn new(
36        protocol_version: ProtocolVersion,
37        vendor_id: VendorId,
38        guid_prefix: GuidPrefix,
39    ) -> Self {
40        Self {
41            protocol_version,
42            vendor_id,
43            guid_prefix,
44        }
45    }
46
47    /// Encode header to bytes (fixed 20 bytes)
48    ///
49    /// # Arguments
50    ///
51    /// * `buf` - Output buffer (must be at least 20 bytes)
52    ///
53    /// # Returns
54    ///
55    /// Number of bytes written (always 20)
56    pub fn encode(&self, buf: &mut [u8]) -> Result<usize> {
57        if buf.len() < Self::SIZE {
58            return Err(Error::BufferTooSmall);
59        }
60
61        // Magic "RTPS"
62        buf[0..4].copy_from_slice(&RTPS_MAGIC);
63
64        // Protocol version
65        buf[4] = self.protocol_version.major;
66        buf[5] = self.protocol_version.minor;
67
68        // Vendor ID
69        buf[6..8].copy_from_slice(&self.vendor_id.0);
70
71        // GUID Prefix
72        buf[8..20].copy_from_slice(self.guid_prefix.as_bytes());
73
74        Ok(Self::SIZE)
75    }
76
77    /// Decode header from bytes
78    ///
79    /// # Arguments
80    ///
81    /// * `buf` - Input buffer (must be at least 20 bytes)
82    ///
83    /// # Returns
84    ///
85    /// Decoded header
86    pub fn decode(buf: &[u8]) -> Result<Self> {
87        if buf.len() < Self::SIZE {
88            return Err(Error::BufferTooSmall);
89        }
90
91        // Verify magic
92        if buf[0..4] != RTPS_MAGIC {
93            return Err(Error::InvalidHeader);
94        }
95
96        // Protocol version
97        let protocol_version = ProtocolVersion::new(buf[4], buf[5]);
98
99        // Vendor ID
100        let vendor_id = VendorId::new([buf[6], buf[7]]);
101
102        // GUID Prefix
103        let mut guid_prefix_bytes = [0u8; 12];
104        guid_prefix_bytes.copy_from_slice(&buf[8..20]);
105        let guid_prefix = GuidPrefix::new(guid_prefix_bytes);
106
107        Ok(Self {
108            protocol_version,
109            vendor_id,
110            guid_prefix,
111        })
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_rtps_header_encode_decode() {
121        let header = RtpsHeader::new(
122            ProtocolVersion::RTPS_2_5,
123            VendorId::HDDS,
124            GuidPrefix::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
125        );
126
127        let mut buf = [0u8; 64];
128        let written = header.encode(&mut buf).unwrap();
129        assert_eq!(written, RtpsHeader::SIZE);
130
131        // Verify magic
132        assert_eq!(&buf[0..4], b"RTPS");
133
134        // Decode
135        let decoded = RtpsHeader::decode(&buf).unwrap();
136        assert_eq!(decoded, header);
137    }
138
139    #[test]
140    fn test_rtps_header_invalid_magic() {
141        let mut buf = [0u8; 20];
142        buf[0..4].copy_from_slice(b"XXXX"); // Invalid magic
143
144        let result = RtpsHeader::decode(&buf);
145        assert_eq!(result, Err(Error::InvalidHeader));
146    }
147
148    #[test]
149    fn test_rtps_header_buffer_too_small() {
150        let header = RtpsHeader::default();
151        let mut buf = [0u8; 10]; // Too small
152
153        let result = header.encode(&mut buf);
154        assert_eq!(result, Err(Error::BufferTooSmall));
155    }
156}