1use crate::error::{Error, Result};
4use broadcast_common::len::{need_in, need_out};
5use broadcast_common::{Parse, Serialize};
6
7pub const SEGMENT_TYPE: u8 = 0x10;
9pub const HEADER_LEN: usize = 6;
11pub const FIXED_LEN: usize = 2;
13pub const REGION_ENTRY_LEN: usize = 6;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize))]
19#[repr(u8)]
20#[non_exhaustive]
21pub enum PageState {
22 Normal = 0x00,
24 Acquisition = 0x01,
26 ModeChange = 0x02,
28 Reserved(u8),
30}
31
32impl PageState {
33 #[must_use]
35 pub fn name(&self) -> &'static str {
36 match self {
37 Self::Normal => "normal",
38 Self::Acquisition => "acquisition",
39 Self::ModeChange => "mode_change",
40 Self::Reserved(_) => "reserved",
41 }
42 }
43
44 fn to_bits(self) -> u8 {
45 match self {
46 Self::Normal => 0x00,
47 Self::Acquisition => 0x01,
48 Self::ModeChange => 0x02,
49 Self::Reserved(v) => v,
50 }
51 }
52}
53
54broadcast_common::impl_spec_display!(PageState, Reserved);
55
56#[derive(Debug, Clone, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize))]
59pub struct PageRegionEntry {
60 pub region_id: u8,
62 pub reserved: u8,
64 pub region_horizontal_address: u16,
66 pub region_vertical_address: u16,
68}
69
70impl PageRegionEntry {
71 #[allow(dead_code)]
72 fn serialized_len() -> usize {
73 REGION_ENTRY_LEN
74 }
75
76 fn serialize_into(&self, buf: &mut [u8]) {
77 buf[0] = self.region_id;
78 buf[1] = self.reserved;
79 buf[2..4].copy_from_slice(&self.region_horizontal_address.to_be_bytes());
80 buf[4..6].copy_from_slice(&self.region_vertical_address.to_be_bytes());
81 }
82
83 fn parse(bytes: &[u8]) -> Result<Self> {
84 need_in(bytes, REGION_ENTRY_LEN, "page_region_entry")?;
85 let reserved = bytes[1];
86 Ok(PageRegionEntry {
87 region_id: bytes[0],
88 reserved,
89 region_horizontal_address: u16::from_be_bytes([bytes[2], bytes[3]]),
90 region_vertical_address: u16::from_be_bytes([bytes[4], bytes[5]]),
91 })
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
97#[cfg_attr(feature = "serde", derive(serde::Serialize))]
98pub struct PageCompositionSegment {
99 pub page_id: u16,
101 pub page_time_out: u8,
103 pub page_version_number: u8,
105 pub page_state: PageState,
107 pub reserved: u8,
109 pub regions: alloc::vec::Vec<PageRegionEntry>,
111}
112
113impl<'a> Parse<'a> for PageCompositionSegment {
114 type Error = Error;
115
116 fn parse(bytes: &'a [u8]) -> Result<Self> {
117 need_in(bytes, HEADER_LEN + FIXED_LEN, "page_composition_segment")?;
118 if bytes[0] != crate::pes_data_field::SYNC_BYTE {
119 return Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
120 what: "sync_byte",
121 expected: u32::from(crate::pes_data_field::SYNC_BYTE),
122 found: u32::from(bytes[0]),
123 }));
124 }
125 if bytes[1] != SEGMENT_TYPE {
126 return Err(Error::UnknownSegmentType(bytes[1]));
127 }
128 let page_id = u16::from_be_bytes([bytes[2], bytes[3]]);
129 let segment_length = u16::from_be_bytes([bytes[4], bytes[5]]) as usize;
130 let total = HEADER_LEN + segment_length;
131 need_in(bytes, total, "page_composition_segment data")?;
132 let body = &bytes[HEADER_LEN..HEADER_LEN + segment_length];
133 need_in(body, FIXED_LEN, "page_composition_segment body")?;
134 let page_time_out = body[0];
135 let page_version_number = body[1] >> 4;
136 let page_state_bits = (body[1] >> 2) & 0x03;
137 let reserved = body[1] & 0x03;
138 let page_state = match page_state_bits {
139 0x00 => PageState::Normal,
140 0x01 => PageState::Acquisition,
141 0x02 => PageState::ModeChange,
142 v => PageState::Reserved(v),
143 };
144
145 let region_data = &body[FIXED_LEN..];
146 let region_count = region_data.len() / REGION_ENTRY_LEN;
147 if !region_data.len().is_multiple_of(REGION_ENTRY_LEN) {
154 return Err(Error::BufferTooShort(
155 broadcast_common::error::BufferTooShort {
156 need: (region_count + 1) * REGION_ENTRY_LEN,
157 have: region_data.len(),
158 what: "page_composition_segment regions",
159 },
160 ));
161 }
162 let mut regions = alloc::vec::Vec::with_capacity(region_count);
163 for i in 0..region_count {
164 let entry_bytes = ®ion_data[i * REGION_ENTRY_LEN..][..REGION_ENTRY_LEN];
165 regions.push(PageRegionEntry::parse(entry_bytes)?);
166 }
167
168 Ok(PageCompositionSegment {
169 page_id,
170 page_time_out,
171 page_version_number,
172 page_state,
173 reserved,
174 regions,
175 })
176 }
177}
178
179impl Serialize for PageCompositionSegment {
180 type Error = Error;
181
182 fn serialized_len(&self) -> usize {
183 HEADER_LEN + FIXED_LEN + self.regions.len() * REGION_ENTRY_LEN
184 }
185
186 fn serialize_into(&self, buf: &mut [u8]) -> core::result::Result<usize, Self::Error> {
187 let len = self.serialized_len();
188 need_out(buf, len)?;
189 buf[0] = 0x0F;
190 buf[1] = SEGMENT_TYPE;
191 buf[2..4].copy_from_slice(&self.page_id.to_be_bytes());
192 let seg_len = crate::segments::check_segment_length(len - HEADER_LEN)?;
193 buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
194
195 buf[6] = self.page_time_out;
196 buf[7] = (broadcast_common::len::fit_mask(
197 self.page_version_number,
198 0x0F,
199 "page_version_number",
200 )? << 4)
201 | (broadcast_common::len::fit_mask(self.page_state.to_bits(), 0x03, "page_state")?
202 << 2)
203 | broadcast_common::len::fit_mask(self.reserved, 0x03, "reserved")?;
204
205 for (i, region) in self.regions.iter().enumerate() {
206 let off = HEADER_LEN + FIXED_LEN + i * REGION_ENTRY_LEN;
207 region.serialize_into(&mut buf[off..off + REGION_ENTRY_LEN]);
208 }
209 Ok(len)
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216 use broadcast_common::{Parse, Serialize};
217
218 #[test]
219 fn round_trip() {
220 let bytes = [
221 0x0F, 0x10, 0x00, 0x01, 0x00, 0x0E, 0x0A, 0x04, 0x01, 0x00, 0x00, 0x64, 0x00, 0x32,
222 0x02, 0x00, 0x00, 0xC8, 0x00, 0x96,
223 ];
224 let seg = PageCompositionSegment::parse(&bytes).unwrap();
225 assert_eq!(seg.page_time_out, 10);
226 assert_eq!(seg.page_version_number, 0);
227 assert_eq!(seg.page_state, PageState::Acquisition);
228 assert_eq!(seg.regions.len(), 2);
229 assert_eq!(seg.regions[0].region_id, 1);
230 assert_eq!(seg.regions[0].region_horizontal_address, 100);
231 assert_eq!(seg.regions[0].region_vertical_address, 50);
232 let out = seg.try_to_bytes().unwrap();
233 assert_eq!(out, bytes);
234
235 let mut seg2 = seg.clone();
237 seg2.page_time_out = 20;
238 let out2 = seg2.try_to_bytes().unwrap();
239 assert_ne!(out2, bytes);
240 let reparse = PageCompositionSegment::parse(&out2).unwrap();
241 assert_eq!(reparse.page_time_out, 20);
242 }
243
244 #[test]
246 fn rejects_bad_sync_byte() {
247 let bytes = [
248 0x00, 0x10, 0x00, 0x01, 0x00, 0x0E, 0x0A, 0x04, 0x01, 0x00, 0x00, 0x64, 0x00, 0x32,
249 0x02, 0x00, 0x00, 0xC8, 0x00, 0x96,
250 ];
251 assert!(matches!(
252 PageCompositionSegment::parse(&bytes),
253 Err(Error::InvalidSync(broadcast_common::error::InvalidSync {
254 found: 0x00,
255 ..
256 }))
257 ));
258 }
259}