rtcp_types/
utils.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3#[track_caller]
4#[inline(always)]
5pub(crate) fn u16_from_be_bytes(bytes: &[u8]) -> u16 {
6    u16::from_be_bytes(bytes.try_into().expect("expecting 2 bytes"))
7}
8
9#[track_caller]
10#[inline(always)]
11pub(crate) fn u32_from_be_bytes(bytes: &[u8]) -> u32 {
12    u32::from_be_bytes(bytes.try_into().expect("expecting 4 bytes"))
13}
14
15#[track_caller]
16#[inline(always)]
17pub(crate) fn u64_from_be_bytes(bytes: &[u8]) -> u64 {
18    u64::from_be_bytes(bytes.try_into().expect("expecting 8 bytes"))
19}
20
21#[inline(always)]
22pub(crate) const fn pad_to_4bytes(num: usize) -> usize {
23    (num + 3) & !3
24}
25
26/// Parsing utility helpers
27pub mod parser {
28    use crate::{RtcpPacket, RtcpParseError};
29
30    /// Performs checks common to every RTCP packets.
31    ///
32    /// Call this before parsing the specificities of the RTCP packet.
33    #[inline(always)]
34    pub fn check_packet<P: RtcpPacket>(packet: &[u8]) -> Result<(), RtcpParseError> {
35        if packet.len() < P::MIN_PACKET_LEN {
36            return Err(RtcpParseError::Truncated {
37                expected: P::MIN_PACKET_LEN,
38                actual: packet.len(),
39            });
40        }
41
42        let version = parse_version(packet);
43        if version != P::VERSION {
44            return Err(RtcpParseError::UnsupportedVersion(version));
45        }
46
47        if parse_packet_type(packet) != P::PACKET_TYPE {
48            return Err(RtcpParseError::PacketTypeMismatch {
49                actual: parse_packet_type(packet),
50                requested: P::PACKET_TYPE,
51            });
52        }
53
54        let length = parse_length(packet);
55        if packet.len() < length {
56            return Err(RtcpParseError::Truncated {
57                expected: length,
58                actual: packet.len(),
59            });
60        }
61
62        if packet.len() > length {
63            return Err(RtcpParseError::TooLarge {
64                expected: length,
65                actual: packet.len(),
66            });
67        }
68
69        if let Some(padding) = parse_padding(packet) {
70            if padding == 0 {
71                return Err(RtcpParseError::InvalidPadding);
72            }
73        }
74
75        Ok(())
76    }
77
78    /// Parses the version from the provided packet data.
79    #[inline(always)]
80    pub fn parse_version(packet: &[u8]) -> u8 {
81        packet[0] >> 6
82    }
83
84    /// Parses the padding bit from the provided packet data.
85    #[inline(always)]
86    pub fn parse_padding_bit(packet: &[u8]) -> bool {
87        (packet[0] & 0x20) != 0
88    }
89
90    /// Parses the padding from the provided packet data.
91    ///
92    /// Returns the last byte of the packet if the padding bit is set
93    /// otherwise returns `None`.
94    #[inline(always)]
95    pub fn parse_padding(packet: &[u8]) -> Option<u8> {
96        if parse_padding_bit(packet) {
97            let length = parse_length(packet);
98            Some(packet[length - 1])
99        } else {
100            None
101        }
102    }
103
104    /// Parses the count from the provided packet data.
105    #[inline(always)]
106    pub fn parse_count(packet: &[u8]) -> u8 {
107        packet[0] & 0x1f
108    }
109
110    /// Parses the packet type from the provided packet data.
111    #[inline(always)]
112    pub fn parse_packet_type(packet: &[u8]) -> u8 {
113        packet[1]
114    }
115
116    /// Parses the length from the provided packet data.
117    #[inline(always)]
118    pub fn parse_length(packet: &[u8]) -> usize {
119        4 * (super::u16_from_be_bytes(&packet[2..4]) as usize + 1)
120    }
121
122    /// Parses the SSRC from the provided packet data.
123    ///
124    /// This is applicable for packets where SSRC is available at [4..8].
125    #[inline(always)]
126    pub fn parse_ssrc(packet: &[u8]) -> u32 {
127        super::u32_from_be_bytes(&packet[4..8])
128    }
129}
130
131/// Writing utility helpers
132pub mod writer {
133    use crate::{RtcpPacket, RtcpWriteError};
134
135    /// Checks that the provided padding is a mutliple of 4.
136    #[inline(always)]
137    pub fn check_padding(padding: u8) -> Result<(), RtcpWriteError> {
138        if padding % 4 != 0 {
139            return Err(RtcpWriteError::InvalidPadding { padding });
140        }
141
142        Ok(())
143    }
144
145    /// Writes the common header for this RTCP packet into `buf` without any validity checks.
146    ///
147    /// Uses the length of the buffer for the length field.
148    ///
149    /// Returns the number of bytes written.
150    ///
151    /// # Panic
152    ///
153    /// Panics if the buf is not large enough.
154    #[inline(always)]
155    pub fn write_header_unchecked<P: RtcpPacket>(padding: u8, count: u8, buf: &mut [u8]) -> usize {
156        buf[0] = P::VERSION << 6;
157        if padding > 0 {
158            buf[0] |= 0x20;
159        }
160        buf[0] |= count;
161        buf[1] = P::PACKET_TYPE;
162        let len = buf.len();
163        buf[2..4].copy_from_slice(&((len / 4 - 1) as u16).to_be_bytes());
164
165        4
166    }
167
168    /// Writes the padding for this RTCP packet into `buf` without any validity checks.
169    ///
170    /// Returns the number of bytes written.
171    ///
172    /// # Panic
173    ///
174    /// Panics if the buf is not large enough.
175    #[inline(always)]
176    pub fn write_padding_unchecked(padding: u8, buf: &mut [u8]) -> usize {
177        let mut end = 0;
178        if padding > 0 {
179            end += padding as usize;
180
181            buf[0..end - 1].fill(0);
182            buf[end - 1] = padding;
183        }
184
185        end
186    }
187}