dvb_t2mi/payload/
fef_composite.rs1use std::fmt;
7
8use dvb_common::{Parse, Serialize};
9
10use super::fef_null::S1Field;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct FefCompositePayload {
24 pub fef_idx: u8,
26 pub s1_field: S1Field,
28 pub s2_field: u8,
30 pub num_subparts: u16,
32}
33
34const FEF_COMPOSITE_LEN: usize = 8;
35
36impl<'a> Parse<'a> for FefCompositePayload {
37 type Error = crate::error::Error;
38
39 fn parse(bytes: &'a [u8]) -> Result<Self, crate::error::Error> {
40 if bytes.len() < FEF_COMPOSITE_LEN {
41 return Err(crate::Error::BufferTooShort {
42 need: FEF_COMPOSITE_LEN,
43 have: bytes.len(),
44 what: "FefCompositePayload header",
45 });
46 }
47
48 if bytes[1] & 0x80 != 0 {
50 return Err(crate::Error::ReservedBitsViolation {
51 field: "byte 1 [7] rfu1",
52 reason: "Must be zero (ETSI TS 102 773 §5.2.11)",
53 });
54 }
55 if bytes[2] != 0 || bytes[3] != 0 || bytes[4] != 0 || bytes[5] != 0 {
57 return Err(crate::Error::ReservedBitsViolation {
58 field: "32-bit RFU (rfu2)",
59 reason: "Must be zero (ETSI TS 102 773 §5.2.11)",
60 });
61 }
62
63 Ok(FefCompositePayload {
64 fef_idx: bytes[0],
65 s1_field: S1Field::try_from((bytes[1] >> 4) & 0x07)?,
66 s2_field: bytes[1] & 0x0F,
67 num_subparts: u16::from_be_bytes([bytes[6], bytes[7]]),
68 })
69 }
70}
71
72impl<'a> crate::traits::PayloadDef<'a> for FefCompositePayload {
73 const PACKET_TYPE: u8 = 0x32;
74 const NAME: &'static str = "FEF_COMPOSITE";
75}
76
77impl Serialize for FefCompositePayload {
78 type Error = crate::error::Error;
79
80 fn serialized_len(&self) -> usize {
81 FEF_COMPOSITE_LEN
82 }
83
84 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, crate::error::Error> {
85 if buf.len() < self.serialized_len() {
86 return Err(crate::Error::OutputBufferTooSmall {
87 need: self.serialized_len(),
88 have: buf.len(),
89 });
90 }
91
92 if self.s2_field > 0x0F {
93 return Err(crate::Error::ReservedBitsViolation {
94 field: "s2_field",
95 reason: "Must fit in 4 bits",
96 });
97 }
98
99 buf[0] = self.fef_idx;
100 buf[1] = ((u8::from(self.s1_field) << 4) & 0x70) | (self.s2_field & 0x0F);
101 buf[2] = 0;
103 buf[3] = 0;
104 buf[4] = 0;
105 buf[5] = 0;
106 let ns = self.num_subparts.to_be_bytes();
107 buf[6] = ns[0];
108 buf[7] = ns[1];
109
110 Ok(self.serialized_len())
111 }
112}
113
114impl fmt::Display for FefCompositePayload {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(
117 f,
118 "FEF Composite {{ fef_idx: {}, s1: {:?}, s2: {:04b}, subparts: {} }}",
119 self.fef_idx, self.s1_field, self.s2_field, self.num_subparts
120 )
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn parse_extracts_fields() {
130 let buf = [0x03u8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05];
131 let result = FefCompositePayload::parse(&buf).unwrap();
132 assert_eq!(result.fef_idx, 3);
133 assert_eq!(result.s1_field, S1Field::V1);
134 assert_eq!(result.s2_field, 0);
135 assert_eq!(result.num_subparts, 5);
136 }
137
138 #[test]
139 fn parse_rejects_nonzero_rfu1() {
140 let buf = [0x00u8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
141 assert!(FefCompositePayload::parse(&buf).is_err());
142 }
143
144 #[test]
145 fn parse_rejects_nonzero_32bit_rfu2() {
146 let buf = [0x00u8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
147 assert!(FefCompositePayload::parse(&buf).is_err());
148 }
149
150 #[test]
151 fn parse_rejects_short_buffer() {
152 let buf = [0x00u8; 7];
153 assert!(FefCompositePayload::parse(&buf).is_err());
154 }
155
156 #[test]
157 fn serialize_round_trip() {
158 let orig = FefCompositePayload {
159 fef_idx: 10,
160 s1_field: S1Field::V7,
161 s2_field: 0x0F,
162 num_subparts: 100,
163 };
164 let mut buf = [0u8; FEF_COMPOSITE_LEN];
165 orig.serialize_into(&mut buf).unwrap();
166 let parsed = FefCompositePayload::parse(&buf).unwrap();
167 assert_eq!(orig, parsed);
168 }
169
170 #[test]
171 fn serialize_rejects_too_small_buffer() {
172 let payload = FefCompositePayload {
173 fef_idx: 0,
174 s1_field: S1Field::V0,
175 s2_field: 0,
176 num_subparts: 0,
177 };
178 let mut buf = [0u8; 7];
179 assert!(payload.serialize_into(&mut buf).is_err());
180 }
181
182 #[test]
183 fn serialize_zeros_rfu_bits() {
184 let payload = FefCompositePayload {
185 fef_idx: 1,
186 s1_field: S1Field::V0,
187 s2_field: 0,
188 num_subparts: 1,
189 };
190 let mut buf = [0xFFu8; FEF_COMPOSITE_LEN];
191 payload.serialize_into(&mut buf).unwrap();
192 assert_eq!(buf[1] & 0x80, 0); assert_eq!(&buf[2..6], &[0u8; 4]); }
195}