1use crate::descriptors::descriptor_body;
9use crate::error::{Error, Result};
10use alloc::vec::Vec;
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x00;
15const HEADER_LEN: usize = 2;
16const PROFILE_ENTRY_LEN: usize = 5;
17const FLAGS_LEN: usize = 1;
18const PRIORITY_LEN: usize = 1;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23#[non_exhaustive]
24pub enum Visibility {
25 NotVisibleAll,
27 NotVisibleUsers,
29 ReservedFutureUse,
31 VisibleAll,
33 Other(u8),
35}
36
37impl Visibility {
38 #[must_use]
40 pub fn from_u8(v: u8) -> Self {
41 match v & 0x03 {
42 0 => Self::NotVisibleAll,
43 1 => Self::NotVisibleUsers,
44 2 => Self::ReservedFutureUse,
45 3 => Self::VisibleAll,
46 other => Self::Other(other),
47 }
48 }
49
50 #[must_use]
52 pub fn to_u8(self) -> u8 {
53 match self {
54 Self::NotVisibleAll => 0,
55 Self::NotVisibleUsers => 1,
56 Self::ReservedFutureUse => 2,
57 Self::VisibleAll => 3,
58 Self::Other(v) => v & 0x03,
59 }
60 }
61
62 #[must_use]
64 pub fn name(self) -> &'static str {
65 match self {
66 Self::NotVisibleAll => "NOT_VISIBLE_ALL",
67 Self::NotVisibleUsers => "NOT_VISIBLE_USERS",
68 Self::ReservedFutureUse => "reserved_future_use",
69 Self::VisibleAll => "VISIBLE_ALL",
70 Self::Other(_) => "reserved",
71 }
72 }
73}
74dvb_common::impl_spec_display!(Visibility, Other);
75
76#[derive(Debug, Clone, PartialEq, Eq)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79pub struct ApplicationProfile {
80 pub profile: u16,
82 pub version_major: u8,
84 pub version_minor: u8,
86 pub version_micro: u8,
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize))]
93pub struct ApplicationDescriptor {
94 pub profiles: Vec<ApplicationProfile>,
96 pub service_bound_flag: bool,
98 pub visibility: Visibility,
100 pub application_priority: u8,
102 pub transport_protocol_labels: Vec<u8>,
104}
105
106impl<'a> Parse<'a> for ApplicationDescriptor {
107 type Error = crate::error::Error;
108 fn parse(bytes: &'a [u8]) -> Result<Self> {
109 let body = descriptor_body(
110 bytes,
111 TAG,
112 "ApplicationDescriptor",
113 "unexpected tag for application_descriptor",
114 )?;
115 if body.is_empty() {
116 return Err(Error::InvalidDescriptor {
117 tag: TAG,
118 reason: "application_descriptor body is empty",
119 });
120 }
121 let profiles_length = body[0] as usize;
122 let profiles_end = 1 + profiles_length;
123 if profiles_end > body.len() {
124 return Err(Error::InvalidDescriptor {
125 tag: TAG,
126 reason: "application_profiles_length runs past descriptor end",
127 });
128 }
129 if profiles_length % PROFILE_ENTRY_LEN != 0 {
130 return Err(Error::InvalidDescriptor {
131 tag: TAG,
132 reason: "application_profiles_length not a multiple of 5",
133 });
134 }
135 let mut profiles = Vec::with_capacity(profiles_length / PROFILE_ENTRY_LEN);
136 let mut pos = 1;
137 while pos < profiles_end {
138 let profile = u16::from_be_bytes([body[pos], body[pos + 1]]);
139 let version_major = body[pos + 2];
140 let version_minor = body[pos + 3];
141 let version_micro = body[pos + 4];
142 profiles.push(ApplicationProfile {
143 profile,
144 version_major,
145 version_minor,
146 version_micro,
147 });
148 pos += PROFILE_ENTRY_LEN;
149 }
150 if profiles_end + FLAGS_LEN + PRIORITY_LEN > body.len() {
151 return Err(Error::InvalidDescriptor {
152 tag: TAG,
153 reason: "flags/priority bytes missing after profiles",
154 });
155 }
156 let flags_byte = body[profiles_end];
157 let service_bound_flag = (flags_byte & 0x80) != 0;
158 let visibility = Visibility::from_u8((flags_byte >> 5) & 0x03);
159 let application_priority = body[profiles_end + FLAGS_LEN];
160 let labels_start = profiles_end + FLAGS_LEN + PRIORITY_LEN;
161 let transport_protocol_labels = body[labels_start..].to_vec();
162 Ok(Self {
163 profiles,
164 service_bound_flag,
165 visibility,
166 application_priority,
167 transport_protocol_labels,
168 })
169 }
170}
171
172impl Serialize for ApplicationDescriptor {
173 type Error = crate::error::Error;
174 fn serialized_len(&self) -> usize {
175 HEADER_LEN
176 + 1
177 + self.profiles.len() * PROFILE_ENTRY_LEN
178 + FLAGS_LEN
179 + PRIORITY_LEN
180 + self.transport_protocol_labels.len()
181 }
182
183 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
184 let profiles_length = self.profiles.len() * PROFILE_ENTRY_LEN;
185 if profiles_length > u8::MAX as usize {
186 return Err(Error::InvalidDescriptor {
187 tag: TAG,
188 reason: "application_profiles_length exceeds 255 bytes",
189 });
190 }
191 let len = self.serialized_len();
192 let body_len = len - HEADER_LEN;
193 if body_len > u8::MAX as usize {
194 return Err(Error::InvalidDescriptor {
195 tag: TAG,
196 reason: "application_descriptor body exceeds 255 bytes",
197 });
198 }
199 if buf.len() < len {
200 return Err(Error::OutputBufferTooSmall {
201 need: len,
202 have: buf.len(),
203 });
204 }
205 buf[0] = TAG;
206 buf[1] = body_len as u8;
207 buf[2] = profiles_length as u8;
208 let mut pos = 3;
209 for p in &self.profiles {
210 buf[pos..pos + 2].copy_from_slice(&p.profile.to_be_bytes());
211 buf[pos + 2] = p.version_major;
212 buf[pos + 3] = p.version_minor;
213 buf[pos + 4] = p.version_micro;
214 pos += PROFILE_ENTRY_LEN;
215 }
216 let flags_byte = (u8::from(self.service_bound_flag) << 7)
217 | ((self.visibility.to_u8() & 0x03) << 5)
218 | 0x1F;
219 buf[pos] = flags_byte;
220 buf[pos + 1] = self.application_priority;
221 pos += FLAGS_LEN + PRIORITY_LEN;
222 for (i, &label) in self.transport_protocol_labels.iter().enumerate() {
223 buf[pos + i] = label;
224 }
225 Ok(len)
226 }
227}
228
229impl<'a> crate::traits::DescriptorDef<'a> for ApplicationDescriptor {
230 const TAG: u8 = TAG;
231 const NAME: &'static str = "APPLICATION";
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 fn build_single_profile_two_labels() -> [u8; 12] {
240 [
241 TAG, 10, 5, 0x00, 0x01, 2, 3, 4, 0x9F, 0x0A, 0x01, 0x02, ]
249 }
250
251 #[test]
252 fn parse_single_profile_with_labels() {
253 let bytes = build_single_profile_two_labels();
254 let d = ApplicationDescriptor::parse(&bytes).unwrap();
255 assert_eq!(d.profiles.len(), 1);
256 assert_eq!(d.profiles[0].profile, 1);
257 assert_eq!(d.profiles[0].version_major, 2);
258 assert_eq!(d.profiles[0].version_minor, 3);
259 assert_eq!(d.profiles[0].version_micro, 4);
260 assert!(d.service_bound_flag);
261 assert_eq!(d.visibility, Visibility::NotVisibleAll);
262 assert_eq!(d.application_priority, 0x0A);
263 assert_eq!(d.transport_protocol_labels, [0x01, 0x02]);
264 }
265
266 #[test]
267 fn parse_visible_all() {
268 let bytes = [
270 TAG, 9, 5, 0x00, 0x10, 1, 0, 0,
271 0x6F, 0xFF, 0x42,
274 ];
275 let d = ApplicationDescriptor::parse(&bytes).unwrap();
276 assert!(!d.service_bound_flag);
277 assert_eq!(d.visibility, Visibility::VisibleAll);
278 }
279
280 #[test]
281 fn parse_rejects_short_body() {
282 let err = ApplicationDescriptor::parse(&[TAG]).unwrap_err();
283 assert!(matches!(err, Error::BufferTooShort { .. }));
284 }
285
286 #[test]
287 fn serialize_round_trip() {
288 let d = ApplicationDescriptor {
289 profiles: alloc::vec![ApplicationProfile {
290 profile: 0x0010,
291 version_major: 1,
292 version_minor: 2,
293 version_micro: 3,
294 }],
295 service_bound_flag: true,
296 visibility: Visibility::VisibleAll,
297 application_priority: 5,
298 transport_protocol_labels: alloc::vec![0x01],
299 };
300 let mut buf = vec![0u8; d.serialized_len()];
301 d.serialize_into(&mut buf).unwrap();
302 let re = ApplicationDescriptor::parse(&buf).unwrap();
303 assert_eq!(d, re);
304 assert_eq!(buf[0], TAG);
305 }
306
307 #[test]
308 fn serialize_byte_identical() {
309 let bytes = build_single_profile_two_labels();
310 let d = ApplicationDescriptor::parse(&bytes).unwrap();
311 let mut buf = vec![0u8; d.serialized_len()];
312 d.serialize_into(&mut buf).unwrap();
313 assert_eq!(buf.as_slice(), &bytes[..]);
314 }
315
316 #[test]
317 fn visibility_round_trip() {
318 for v in [
319 Visibility::NotVisibleAll,
320 Visibility::NotVisibleUsers,
321 Visibility::ReservedFutureUse,
322 Visibility::VisibleAll,
323 ] {
324 assert_eq!(Visibility::from_u8(v.to_u8()), v);
325 }
326 }
327}