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