dvb_t2mi/payload/
bbframe.rs1use dvb_common::{Parse, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
16pub struct BbframePayload<'a> {
17 pub frame_idx: u8,
19 pub plp_id: u8,
21 pub intl_frame_start: bool,
23 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<'a> crate::traits::PayloadDef<'a> for BbframePayload<'a> {
63 const PACKET_TYPE: u8 = 0x00;
64 const NAME: &'static str = "BBFRAME";
65}
66
67impl Serialize for BbframePayload<'_> {
68 type Error = crate::error::Error;
69
70 fn serialized_len(&self) -> usize {
71 3 + self.bbframe.len()
72 }
73
74 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
75 if buf.len() < self.serialized_len() {
76 return Err(crate::Error::OutputBufferTooSmall {
77 need: self.serialized_len(),
78 have: buf.len(),
79 });
80 }
81
82 buf[0] = self.frame_idx;
83 buf[1] = self.plp_id;
84 buf[2] = if self.intl_frame_start { 0x80 } else { 0x00 };
85 if !self.bbframe.is_empty() {
86 buf[3..3 + self.bbframe.len()].copy_from_slice(self.bbframe);
87 }
88
89 Ok(self.serialized_len())
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn parse_extracts_frame_idx_and_plp_id() {
99 let buf = [0x42u8, 0x05, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
100 let result = BbframePayload::parse(&buf).unwrap();
101 assert_eq!(result.frame_idx, 0x42);
102 assert_eq!(result.plp_id, 0x05);
103 assert_eq!(result.bbframe, &[0xDE, 0xAD, 0xBE, 0xEF]);
104 }
105
106 #[test]
107 fn parse_extracts_intl_frame_start_flag() {
108 let buf = [0x00u8, 0x00, 0x80];
109 let result = BbframePayload::parse(&buf).unwrap();
110 assert!(result.intl_frame_start);
111
112 let buf2 = [0x00u8, 0x00, 0x00];
113 let result2 = BbframePayload::parse(&buf2).unwrap();
114 assert!(!result2.intl_frame_start);
115 }
116
117 #[test]
118 fn parse_rejects_nonzero_rfu_bits() {
119 let buf = [0x00u8, 0x00, 0x40];
120 let result = BbframePayload::parse(&buf);
121 assert!(result.is_err());
122 assert!(matches!(
123 result.unwrap_err(),
124 crate::Error::ReservedBitsViolation { .. }
125 ));
126 }
127
128 #[test]
129 fn parse_bbframe_body_slices_from_offset_3() {
130 let body: Vec<u8> = (0..100).collect();
131 let mut buf = vec![0x0Fu8, 0x01, 0x00];
132 buf.extend_from_slice(&body);
133 let result = BbframePayload::parse(&buf).unwrap();
134 assert_eq!(result.bbframe, body.as_slice());
135 }
136
137 #[test]
138 fn parse_rejects_body_shorter_than_3_bytes() {
139 assert!(BbframePayload::parse(&[0x00, 0x00]).is_err());
140 assert!(BbframePayload::parse(&[]).is_err());
141 }
142
143 #[test]
144 fn serialize_round_trip_preserves_all_fields() {
145 let orig = BbframePayload {
146 frame_idx: 0xAB,
147 plp_id: 0x07,
148 intl_frame_start: true,
149 bbframe: &[0xCA, 0xFE, 0xBA, 0xBE],
150 };
151 let mut buf = vec![0u8; orig.serialized_len()];
152 orig.serialize_into(&mut buf).unwrap();
153 let parsed = BbframePayload::parse(&buf).unwrap();
154 assert_eq!(orig, parsed);
155 }
156
157 #[test]
158 fn serialize_zeros_rfu_bits() {
159 let payload = BbframePayload {
160 frame_idx: 0x00,
161 plp_id: 0x00,
162 intl_frame_start: false,
163 bbframe: &[],
164 };
165 let mut buf = [0xFFu8; 3];
166 payload.serialize_into(&mut buf).unwrap();
167 assert_eq!(buf[0], 0x00);
168 assert_eq!(buf[1], 0x00);
169 assert_eq!(buf[2] & 0x7F, 0x00);
170 }
171}