statime/datastructures/messages/
header.rs

1use super::{control_field::ControlField, MessageType};
2use crate::datastructures::{
3    common::{PortIdentity, TimeInterval},
4    WireFormat, WireFormatError,
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Header {
9    pub(crate) sdo_id: SdoId,
10    pub(crate) version: PtpVersion,
11    pub(crate) domain_number: u8,
12    pub(crate) alternate_master_flag: bool,
13    pub(crate) two_step_flag: bool,
14    pub(crate) unicast_flag: bool,
15    pub(crate) ptp_profile_specific_1: bool,
16    pub(crate) ptp_profile_specific_2: bool,
17    pub(crate) leap61: bool,
18    pub(crate) leap59: bool,
19    pub(crate) current_utc_offset_valid: bool,
20    pub(crate) ptp_timescale: bool,
21    pub(crate) time_tracable: bool,
22    pub(crate) frequency_tracable: bool,
23    pub(crate) synchronization_uncertain: bool,
24    pub(crate) correction_field: TimeInterval,
25    pub(crate) source_port_identity: PortIdentity,
26    pub(crate) sequence_id: u16,
27    pub(crate) log_message_interval: i8,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub(crate) struct DeserializedHeader {
32    pub(crate) header: Header,
33    pub(crate) message_type: MessageType,
34    pub(crate) message_length: u16,
35}
36
37impl Header {
38    pub(crate) fn new(minor_ptp_version: u8) -> Self {
39        Self {
40            sdo_id: SdoId(0),
41            version: PtpVersion {
42                major: 2,
43                minor: minor_ptp_version,
44            },
45            domain_number: 0,
46            alternate_master_flag: false,
47            two_step_flag: false,
48            unicast_flag: false,
49            ptp_profile_specific_1: false,
50            ptp_profile_specific_2: false,
51            leap59: false,
52            leap61: false,
53            current_utc_offset_valid: false,
54            ptp_timescale: false,
55            time_tracable: false,
56            frequency_tracable: false,
57            synchronization_uncertain: false,
58            correction_field: TimeInterval::default(),
59            source_port_identity: PortIdentity::default(),
60            sequence_id: 0,
61            log_message_interval: 0,
62        }
63    }
64
65    pub(crate) fn wire_size(&self) -> usize {
66        34
67    }
68
69    pub(crate) fn serialize_header(
70        &self,
71        content_type: MessageType,
72        content_length: usize,
73        buffer: &mut [u8],
74    ) -> Result<(), WireFormatError> {
75        buffer[0] = ((self.sdo_id.high_byte()) << 4) | ((content_type as u8) & 0x0f);
76        buffer[1] = self.version.as_byte();
77        buffer[2..4].copy_from_slice(&((content_length + self.wire_size()) as u16).to_be_bytes());
78        buffer[4] = self.domain_number;
79        buffer[5] = self.sdo_id.low_byte();
80        buffer[6] = 0;
81        buffer[7] = 0;
82        buffer[6] |= self.alternate_master_flag as u8;
83        buffer[6] |= (self.two_step_flag as u8) << 1;
84        buffer[6] |= (self.unicast_flag as u8) << 2;
85        buffer[6] |= (self.ptp_profile_specific_1 as u8) << 5;
86        buffer[6] |= (self.ptp_profile_specific_2 as u8) << 6;
87        buffer[7] |= self.leap61 as u8;
88        buffer[7] |= (self.leap59 as u8) << 1;
89        buffer[7] |= (self.current_utc_offset_valid as u8) << 2;
90        buffer[7] |= (self.ptp_timescale as u8) << 3;
91        buffer[7] |= (self.time_tracable as u8) << 4;
92        buffer[7] |= (self.frequency_tracable as u8) << 5;
93        buffer[7] |= (self.synchronization_uncertain as u8) << 6;
94        self.correction_field.serialize(&mut buffer[8..16])?;
95        buffer[16..20].copy_from_slice(&[0, 0, 0, 0]);
96        self.source_port_identity.serialize(&mut buffer[20..30])?;
97        buffer[30..32].copy_from_slice(&self.sequence_id.to_be_bytes());
98        buffer[32] = ControlField::from(content_type).to_primitive();
99        buffer[33] = self.log_message_interval as u8;
100
101        Ok(())
102    }
103
104    pub(crate) fn deserialize_header(buffer: &[u8]) -> Result<DeserializedHeader, WireFormatError> {
105        if buffer.len() < 34 {
106            return Err(WireFormatError::BufferTooShort);
107        }
108
109        let version = PtpVersion::from_byte(buffer[1]);
110        let sdo_id = SdoId((((buffer[0] & 0xf0) as u16) << 4) | (buffer[5] as u16));
111
112        Ok(DeserializedHeader {
113            header: Self {
114                sdo_id,
115                version,
116                domain_number: buffer[4],
117                alternate_master_flag: (buffer[6] & (1 << 0)) > 0,
118                two_step_flag: (buffer[6] & (1 << 1)) > 0,
119                unicast_flag: (buffer[6] & (1 << 2)) > 0,
120                ptp_profile_specific_1: (buffer[6] & (1 << 5)) > 0,
121                ptp_profile_specific_2: (buffer[6] & (1 << 6)) > 0,
122                leap61: (buffer[7] & (1 << 0)) > 0,
123                leap59: (buffer[7] & (1 << 1)) > 0,
124                current_utc_offset_valid: (buffer[7] & (1 << 2)) > 0,
125                ptp_timescale: (buffer[7] & (1 << 3)) > 0,
126                time_tracable: (buffer[7] & (1 << 4)) > 0,
127                frequency_tracable: (buffer[7] & (1 << 5)) > 0,
128                synchronization_uncertain: (buffer[7] & (1 << 6)) > 0,
129                correction_field: TimeInterval::deserialize(&buffer[8..16])?,
130                source_port_identity: PortIdentity::deserialize(&buffer[20..30])?,
131                sequence_id: u16::from_be_bytes(buffer[30..32].try_into().unwrap()),
132                log_message_interval: buffer[33] as i8,
133            },
134            message_type: (buffer[0] & 0x0f).try_into()?,
135            message_length: u16::from_be_bytes(buffer[2..4].try_into().unwrap()),
136        })
137    }
138}
139
140/// A wrapper type for PTP Sdo Identifiers.
141///
142/// Because `SdoId`s are 12 bit values they always lie within `0..=0xFFF`.
143///
144/// For more details, see *IEEE1588-2019 table 2 in section 7.1.4*.
145///
146/// # Example
147/// ```
148/// # use statime::config::SdoId;
149/// assert_eq!(SdoId::default(), SdoId::try_from(0x000).unwrap());
150///
151/// let sdo_id = SdoId::try_from(0x100).unwrap();
152/// assert_eq!(u16::from(sdo_id), 0x100);
153///
154/// assert!(SdoId::try_from(0x1000).is_err());
155/// ```
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
157#[cfg_attr(feature = "serde", derive(serde::Serialize))]
158pub struct SdoId(u16);
159
160impl core::fmt::Display for SdoId {
161    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162        self.0.fmt(f)
163    }
164}
165
166impl SdoId {
167    const fn high_byte(self) -> u8 {
168        (self.0 >> 8) as u8
169    }
170
171    const fn low_byte(self) -> u8 {
172        self.0 as u8
173    }
174}
175
176#[cfg(feature = "serde")]
177struct SdoIdVisitor;
178
179#[cfg(feature = "serde")]
180impl<'de> serde::Deserialize<'de> for SdoId {
181    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
182    where
183        D: serde::Deserializer<'de>,
184    {
185        deserializer.deserialize_newtype_struct("SdoId", SdoIdVisitor)
186    }
187}
188
189#[cfg(all(feature = "serde", feature = "std"))]
190impl<'de> serde::de::Visitor<'de> for SdoIdVisitor {
191    type Value = SdoId;
192
193    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
194        formatter.write_str("a 12 bit value within the 0..=0xFFF range")
195    }
196
197    fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
198    where
199        D: serde::Deserializer<'de>,
200    {
201        use serde::{de::Error, Deserialize};
202        let v = u16::deserialize(deserializer)?;
203        SdoId::try_from(v).or(Err(D::Error::custom(std::format!(
204            "SdoId not in range of 0..=0xFFF: {}",
205            v
206        ))))
207    }
208
209    fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
210    where
211        E: serde::de::Error,
212    {
213        SdoId::try_from(v).or(Err(E::custom(std::format!(
214            "SdoId not in range of 0..=0xFFF: {}",
215            v
216        ))))
217    }
218}
219
220impl TryFrom<u16> for SdoId {
221    type Error = ();
222
223    /// Turn a `u16` into a `SdoId` ensuring it is in the range `0..=0xFFF`.
224    fn try_from(sdo_id: u16) -> Result<Self, Self::Error> {
225        (0..=0xfff)
226            .contains(&sdo_id)
227            .then_some(Self(sdo_id))
228            .ok_or(())
229    }
230}
231
232impl From<SdoId> for u16 {
233    fn from(value: SdoId) -> Self {
234        value.0
235    }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub struct PtpVersion {
240    major: u8,
241    minor: u8,
242}
243
244impl PtpVersion {
245    #[allow(unused)]
246    pub fn new(major: u8, minor: u8) -> Option<Self> {
247        if major >= 0x10 || minor >= 0x10 {
248            None
249        } else {
250            Some(Self { major, minor })
251        }
252    }
253
254    fn as_byte(&self) -> u8 {
255        (self.minor << 4) | self.major
256    }
257
258    fn from_byte(byte: u8) -> Self {
259        Self {
260            major: byte & 0x0f,
261            minor: byte >> 4,
262        }
263    }
264}
265
266#[cfg(test)]
267mod tests {
268    use fixed::types::I48F16;
269
270    use super::*;
271    use crate::datastructures::common::ClockIdentity;
272
273    #[test]
274    fn flagfield_wireformat() {
275        #[rustfmt::skip]
276        let representations = [
277            ([0x00, 0x00u8], Header::new(1)),
278            ([0x01, 0x00u8], Header { alternate_master_flag: true, ..Header::new(1) }),
279            ([0x02, 0x00u8], Header { two_step_flag: true, ..Header::new(1) }),
280            ([0x04, 0x00u8], Header { unicast_flag: true, ..Header::new(1) }),
281            ([0x20, 0x00u8], Header { ptp_profile_specific_1: true, ..Header::new(1) }),
282            ([0x40, 0x00u8], Header { ptp_profile_specific_2: true, ..Header::new(1) }),
283            ([0x00, 0x01u8], Header { leap61: true, ..Header::new(1) }),
284            ([0x00, 0x02u8], Header { leap59: true, ..Header::new(1) }),
285            ([0x00, 0x04u8], Header { current_utc_offset_valid: true, ..Header::new(1) }),
286            ([0x00, 0x08u8], Header { ptp_timescale: true, ..Header::new(1) }),
287            ([0x00, 0x10u8], Header { time_tracable: true, ..Header::new(1) }),
288            ([0x00, 0x20u8], Header { frequency_tracable: true, ..Header::new(1) }),
289            ([0x00, 0x40u8], Header { synchronization_uncertain: true, ..Header::new(1) }),
290        ];
291
292        for (i, (byte_representation, flag_representation)) in
293            representations.into_iter().enumerate()
294        {
295            // Test the serialization output
296            let mut serialization_buffer = [0; 34];
297            flag_representation
298                .serialize_header(MessageType::Sync, 0, &mut serialization_buffer)
299                .unwrap();
300            assert_eq!(
301                serialization_buffer[6..8],
302                byte_representation,
303                "The serialized flag field is not what it's supposed to for variant {}",
304                i
305            );
306
307            // Test the deserialization output
308            serialization_buffer[6] = byte_representation[0];
309            serialization_buffer[7] = byte_representation[1];
310            let deserialized_flag_field =
311                Header::deserialize_header(&serialization_buffer).unwrap();
312            assert_eq!(
313                deserialized_flag_field.header, flag_representation,
314                "The deserialized flag field is not what it's supposed to for variant {}",
315                i
316            );
317        }
318    }
319
320    #[test]
321    fn header_wireformat() {
322        let representations = [(
323            [
324                0x59,
325                0xa1,
326                0x12,
327                0x34,
328                0xaa,
329                0xbb,
330                0b0100_0101,
331                0b0010_1010,
332                0x00,
333                0x00,
334                0x00,
335                0x00,
336                0x00,
337                0x01,
338                0x80,
339                0x00,
340                0,
341                0,
342                0,
343                0,
344                0,
345                1,
346                2,
347                3,
348                4,
349                5,
350                6,
351                7,
352                0x55,
353                0x55,
354                0xde,
355                0xad,
356                0x03,
357                0x16,
358            ],
359            DeserializedHeader {
360                header: Header {
361                    sdo_id: SdoId(0x5bb),
362                    version: PtpVersion {
363                        major: 0x1,
364                        minor: 0xa,
365                    },
366                    domain_number: 0xaa,
367                    alternate_master_flag: true,
368                    two_step_flag: false,
369                    unicast_flag: true,
370                    ptp_profile_specific_1: false,
371                    ptp_profile_specific_2: true,
372                    leap61: false,
373                    leap59: true,
374                    current_utc_offset_valid: false,
375                    ptp_timescale: true,
376                    time_tracable: false,
377                    frequency_tracable: true,
378                    synchronization_uncertain: false,
379                    correction_field: TimeInterval(I48F16::from_num(1.5f64)),
380                    source_port_identity: PortIdentity {
381                        clock_identity: ClockIdentity([0, 1, 2, 3, 4, 5, 6, 7]),
382                        port_number: 0x5555,
383                    },
384                    sequence_id: 0xdead,
385                    log_message_interval: 0x16,
386                },
387                message_type: MessageType::DelayResp,
388                message_length: 0x1234,
389            },
390        )];
391
392        for (byte_representation, object_representation) in representations {
393            // Test the serialization output
394            let mut serialization_buffer = [0; 34];
395            object_representation
396                .header
397                .serialize_header(
398                    object_representation.message_type,
399                    object_representation.message_length as usize
400                        - object_representation.header.wire_size(),
401                    &mut serialization_buffer,
402                )
403                .unwrap();
404            assert_eq!(serialization_buffer, byte_representation);
405
406            // Test the deserialization output
407            let deserialized_data = Header::deserialize_header(&byte_representation).unwrap();
408            assert_eq!(deserialized_data, object_representation);
409        }
410    }
411
412    #[test]
413    fn sdo_id_checks() {
414        use serde_test::{assert_de_tokens_error, assert_tokens, Token};
415        let correct_sdo_id = SdoId::try_from(0xfff).unwrap();
416        let faulty_sdo_id = SdoId::try_from(0x1000);
417
418        assert_eq!(0xfff, u16::from(correct_sdo_id));
419        assert!(faulty_sdo_id.is_err());
420
421        assert_tokens(
422            &correct_sdo_id,
423            &[Token::NewtypeStruct { name: "SdoId" }, Token::U16(4095)],
424        );
425
426        assert_de_tokens_error::<SdoId>(
427            &[Token::NewtypeStruct { name: "SdoId" }, Token::U16(4096)],
428            "SdoId not in range of 0..=0xFFF: 4096",
429        );
430    }
431}