dvb_t2mi/payload/
bbframe.rs1use dvb_common::{Parse, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct BbframePayload<'a> {
16 pub frame_idx: u8,
18 pub plp_id: u8,
20 pub intl_frame_start: bool,
22 #[cfg_attr(feature = "serde", serde(borrow))]
24 pub bbframe: &'a [u8],
25}
26
27impl<'a> Parse<'a> for BbframePayload<'a> {
28 type Error = crate::error::Error;
29
30 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
31 if bytes.len() < 3 {
32 return Err(crate::Error::BufferTooShort {
33 need: 3,
34 have: bytes.len(),
35 what: "BbframePayload header (frame_idx + plp_id + intl_frame_start + rfu)",
36 });
37 }
38
39 let frame_idx = bytes[0];
40 let plp_id = bytes[1];
41 let intl_frame_start = bytes[2] & 0x80 != 0;
42
43 if bytes[2] & 0x7F != 0 {
45 return Err(crate::Error::ReservedBitsViolation {
46 field: "byte 2 bits 6..0",
47 reason: "RFU must be zero (ETSI TS 102 773 §5.2.1)",
48 });
49 }
50
51 let bbframe = &bytes[3..];
52
53 Ok(BbframePayload {
54 frame_idx,
55 plp_id,
56 intl_frame_start,
57 bbframe,
58 })
59 }
60}
61
62impl Serialize for BbframePayload<'_> {
63 type Error = crate::error::Error;
64
65 fn serialized_len(&self) -> usize {
66 3 + self.bbframe.len()
67 }
68
69 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
70 if buf.len() < self.serialized_len() {
71 return Err(crate::Error::OutputBufferTooSmall {
72 need: self.serialized_len(),
73 have: buf.len(),
74 });
75 }
76
77 buf[0] = self.frame_idx;
78 buf[1] = self.plp_id;
79 buf[2] = if self.intl_frame_start { 0x80 } else { 0x00 };
80 if !self.bbframe.is_empty() {
81 buf[3..3 + self.bbframe.len()].copy_from_slice(self.bbframe);
82 }
83
84 Ok(self.serialized_len())
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn parse_extracts_frame_idx_and_plp_id() {
94 let buf = [0x42u8, 0x05, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
95 let result = BbframePayload::parse(&buf).unwrap();
96 assert_eq!(result.frame_idx, 0x42);
97 assert_eq!(result.plp_id, 0x05);
98 assert_eq!(result.bbframe, &[0xDE, 0xAD, 0xBE, 0xEF]);
99 }
100
101 #[test]
102 fn parse_extracts_intl_frame_start_flag() {
103 let buf = [0x00u8, 0x00, 0x80];
104 let result = BbframePayload::parse(&buf).unwrap();
105 assert!(result.intl_frame_start);
106
107 let buf2 = [0x00u8, 0x00, 0x00];
108 let result2 = BbframePayload::parse(&buf2).unwrap();
109 assert!(!result2.intl_frame_start);
110 }
111
112 #[test]
113 fn parse_rejects_nonzero_rfu_bits() {
114 let buf = [0x00u8, 0x00, 0x40];
115 let result = BbframePayload::parse(&buf);
116 assert!(result.is_err());
117 assert!(matches!(
118 result.unwrap_err(),
119 crate::Error::ReservedBitsViolation { .. }
120 ));
121 }
122
123 #[test]
124 fn parse_bbframe_body_slices_from_offset_3() {
125 let body: Vec<u8> = (0..100).collect();
126 let mut buf = vec![0x0Fu8, 0x01, 0x00];
127 buf.extend_from_slice(&body);
128 let result = BbframePayload::parse(&buf).unwrap();
129 assert_eq!(result.bbframe, body.as_slice());
130 }
131
132 #[test]
133 fn parse_rejects_body_shorter_than_3_bytes() {
134 assert!(BbframePayload::parse(&[0x00, 0x00]).is_err());
135 assert!(BbframePayload::parse(&[]).is_err());
136 }
137
138 #[test]
139 fn serialize_round_trip_preserves_all_fields() {
140 let orig = BbframePayload {
141 frame_idx: 0xAB,
142 plp_id: 0x07,
143 intl_frame_start: true,
144 bbframe: &[0xCA, 0xFE, 0xBA, 0xBE],
145 };
146 let mut buf = vec![0u8; orig.serialized_len()];
147 orig.serialize_into(&mut buf).unwrap();
148 let parsed = BbframePayload::parse(&buf).unwrap();
149 assert_eq!(orig, parsed);
150 }
151
152 #[test]
153 fn serialize_zeros_rfu_bits() {
154 let payload = BbframePayload {
155 frame_idx: 0x00,
156 plp_id: 0x00,
157 intl_frame_start: false,
158 bbframe: &[],
159 };
160 let mut buf = [0xFFu8; 3];
161 payload.serialize_into(&mut buf).unwrap();
162 assert_eq!(buf[0], 0x00);
163 assert_eq!(buf[1], 0x00);
164 assert_eq!(buf[2] & 0x7F, 0x00);
165 }
166}