dvb_si/descriptors/
pdc.rs1use super::descriptor_body;
9use crate::error::{Error, Result};
10use dvb_common::{Parse, Serialize};
11
12pub const TAG: u8 = 0x69;
14pub const HEADER_LEN: usize = 2;
16pub const BODY_LEN: usize = 3;
18pub const PIL_MASK: u32 = 0x000F_FFFF;
20pub const RESERVED_BITS: u32 = 0x00F0_0000;
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27pub struct PdcDescriptor {
28 pub programme_identification_label: u32,
30}
31
32impl PdcDescriptor {
33 #[must_use]
36 pub fn pil_day(&self) -> u8 {
37 ((self.programme_identification_label >> 15) & 0x1F) as u8
38 }
39
40 #[must_use]
42 pub fn pil_month(&self) -> u8 {
43 ((self.programme_identification_label >> 11) & 0x0F) as u8
44 }
45
46 #[must_use]
48 pub fn pil_hour(&self) -> u8 {
49 ((self.programme_identification_label >> 6) & 0x1F) as u8
50 }
51
52 #[must_use]
54 pub fn pil_minute(&self) -> u8 {
55 (self.programme_identification_label & 0x3F) as u8
56 }
57
58 pub fn set_pil(&mut self, day: u8, month: u8, hour: u8, minute: u8) -> crate::Result<()> {
66 if day > 0x1F || month > 0x0F || hour > 0x1F || minute > 0x3F {
67 return Err(crate::Error::ValueOutOfRange {
68 field: "PdcDescriptor::programme_identification_label",
69 reason: "a day/month/hour/minute component exceeds its bit field",
70 });
71 }
72 self.programme_identification_label = (u32::from(day) << 15)
73 | (u32::from(month) << 11)
74 | (u32::from(hour) << 6)
75 | u32::from(minute);
76 Ok(())
77 }
78}
79
80impl<'a> Parse<'a> for PdcDescriptor {
81 type Error = crate::error::Error;
82 fn parse(bytes: &'a [u8]) -> Result<Self> {
83 let body = descriptor_body(
84 bytes,
85 TAG,
86 "PdcDescriptor",
87 "unexpected tag for PDC_descriptor",
88 )?;
89 if body.len() != BODY_LEN {
90 return Err(Error::InvalidDescriptor {
91 tag: TAG,
92 reason: "PDC_descriptor length must be exactly 3",
93 });
94 }
95 let raw = (u32::from(body[0]) << 16) | (u32::from(body[1]) << 8) | u32::from(body[2]);
96 Ok(Self {
97 programme_identification_label: raw & PIL_MASK,
98 })
99 }
100}
101
102impl Serialize for PdcDescriptor {
103 type Error = crate::error::Error;
104 fn serialized_len(&self) -> usize {
105 HEADER_LEN + BODY_LEN
106 }
107
108 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
109 if self.programme_identification_label > PIL_MASK {
110 return Err(Error::InvalidDescriptor {
111 tag: TAG,
112 reason: "programme_identification_label exceeds 20 bits",
113 });
114 }
115 let len = self.serialized_len();
116 if buf.len() < len {
117 return Err(Error::OutputBufferTooSmall {
118 need: len,
119 have: buf.len(),
120 });
121 }
122 buf[0] = TAG;
123 buf[1] = BODY_LEN as u8;
124 let raw = RESERVED_BITS | (self.programme_identification_label & PIL_MASK);
126 buf[HEADER_LEN] = (raw >> 16) as u8;
127 buf[HEADER_LEN + 1] = (raw >> 8) as u8;
128 buf[HEADER_LEN + 2] = raw as u8;
129 Ok(len)
130 }
131}
132impl<'a> crate::traits::DescriptorDef<'a> for PdcDescriptor {
133 const TAG: u8 = TAG;
134 const NAME: &'static str = "PDC";
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn parse_extracts_pil() {
143 let bytes = [TAG, 3, 0x0A, 0xBC, 0xDE];
145 let d = PdcDescriptor::parse(&bytes).unwrap();
146 assert_eq!(d.programme_identification_label, 0x0A_BCDE);
147 }
148
149 #[test]
150 fn parse_ignores_reserved_bits() {
151 let bytes = [TAG, 3, 0xFA, 0xBC, 0xDE];
153 let d = PdcDescriptor::parse(&bytes).unwrap();
154 assert_eq!(d.programme_identification_label, 0x0A_BCDE);
155 }
156
157 #[test]
158 fn parse_rejects_wrong_tag() {
159 assert!(matches!(
160 PdcDescriptor::parse(&[0x6A, 3, 0, 0, 0]).unwrap_err(),
161 Error::InvalidDescriptor { tag: 0x6A, .. }
162 ));
163 }
164
165 #[test]
166 fn parse_rejects_wrong_length() {
167 assert!(matches!(
168 PdcDescriptor::parse(&[TAG, 2, 0, 0]).unwrap_err(),
169 Error::InvalidDescriptor { tag: TAG, .. }
170 ));
171 }
172
173 #[test]
174 fn parse_rejects_short_body() {
175 assert!(matches!(
176 PdcDescriptor::parse(&[TAG, 3, 0, 0]).unwrap_err(),
177 Error::BufferTooShort { .. }
178 ));
179 }
180
181 #[test]
182 fn serialize_round_trip() {
183 let d = PdcDescriptor {
184 programme_identification_label: 0x0A_BCDE,
185 };
186 let mut buf = vec![0u8; d.serialized_len()];
187 d.serialize_into(&mut buf).unwrap();
188 assert_eq!(buf, [TAG, 3, 0xFA, 0xBC, 0xDE]);
190 assert_eq!(PdcDescriptor::parse(&buf).unwrap(), d);
191 }
192
193 #[test]
194 fn serialize_rejects_too_small_buffer() {
195 let d = PdcDescriptor {
196 programme_identification_label: 0,
197 };
198 let mut buf = vec![0u8; 2];
199 assert!(matches!(
200 d.serialize_into(&mut buf).unwrap_err(),
201 Error::OutputBufferTooSmall { .. }
202 ));
203 }
204
205 #[test]
206 fn serialize_rejects_over_range_pil() {
207 let d = PdcDescriptor {
208 programme_identification_label: 0x10_0000, };
210 let mut buf = vec![0u8; d.serialized_len()];
211 assert!(matches!(
212 d.serialize_into(&mut buf).unwrap_err(),
213 Error::InvalidDescriptor { tag: TAG, .. }
214 ));
215 }
216
217 #[cfg(feature = "serde")]
218 #[test]
219 fn serde_round_trip() {
220 let d = PdcDescriptor {
221 programme_identification_label: 0x0A_BCDE,
222 };
223 let json = serde_json::to_string(&d).unwrap();
224 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
226 }
227}