dvb_si/descriptors/
fta_content_management.rs1use crate::error::{Error, Result};
10use crate::traits::Descriptor;
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x7E;
15pub const HEADER_LEN: usize = 2;
17pub const BODY_LEN: usize = 1;
19
20const USER_DEFINED_MASK: u8 = 0b1000_0000;
21const RESERVED_MASK: u8 = 0b0111_0000;
22const DO_NOT_SCRAMBLE_MASK: u8 = 0b0000_1000;
23const CONTROL_REMOTE_ACCESS_MASK: u8 = 0b0000_0110;
24const CONTROL_REMOTE_ACCESS_SHIFT: u8 = 1;
25const DO_NOT_APPLY_REVOCATION_MASK: u8 = 0b0000_0001;
26pub const CONTROL_REMOTE_ACCESS_MAX: u8 = 0b11;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32pub struct FtaContentManagementDescriptor {
33 pub user_defined: bool,
35 pub do_not_scramble: bool,
37 pub control_remote_access_over_internet: u8,
39 pub do_not_apply_revocation: bool,
41}
42
43impl<'a> Parse<'a> for FtaContentManagementDescriptor {
44 type Error = crate::error::Error;
45 fn parse(bytes: &'a [u8]) -> Result<Self> {
46 if bytes.len() < HEADER_LEN {
47 return Err(Error::BufferTooShort {
48 need: HEADER_LEN,
49 have: bytes.len(),
50 what: "FtaContentManagementDescriptor header",
51 });
52 }
53 if bytes[0] != TAG {
54 return Err(Error::InvalidDescriptor {
55 tag: bytes[0],
56 reason: "unexpected tag for FTA_content_management_descriptor",
57 });
58 }
59 let length = bytes[1] as usize;
60 if length != BODY_LEN {
61 return Err(Error::InvalidDescriptor {
62 tag: TAG,
63 reason: "FTA_content_management_descriptor length must be exactly 1",
64 });
65 }
66 let end = HEADER_LEN + length;
67 if bytes.len() < end {
68 return Err(Error::BufferTooShort {
69 need: end,
70 have: bytes.len(),
71 what: "FtaContentManagementDescriptor body",
72 });
73 }
74 let flags = bytes[HEADER_LEN];
75 Ok(Self {
77 user_defined: flags & USER_DEFINED_MASK != 0,
78 do_not_scramble: flags & DO_NOT_SCRAMBLE_MASK != 0,
79 control_remote_access_over_internet: (flags & CONTROL_REMOTE_ACCESS_MASK)
80 >> CONTROL_REMOTE_ACCESS_SHIFT,
81 do_not_apply_revocation: flags & DO_NOT_APPLY_REVOCATION_MASK != 0,
82 })
83 }
84}
85
86impl Serialize for FtaContentManagementDescriptor {
87 type Error = crate::error::Error;
88 fn serialized_len(&self) -> usize {
89 HEADER_LEN + BODY_LEN
90 }
91
92 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
93 if self.control_remote_access_over_internet > CONTROL_REMOTE_ACCESS_MAX {
94 return Err(Error::InvalidDescriptor {
95 tag: TAG,
96 reason: "control_remote_access_over_internet exceeds 2 bits",
97 });
98 }
99 let len = self.serialized_len();
100 if buf.len() < len {
101 return Err(Error::OutputBufferTooSmall {
102 need: len,
103 have: buf.len(),
104 });
105 }
106 let mut flags = RESERVED_MASK;
108 if self.user_defined {
109 flags |= USER_DEFINED_MASK;
110 }
111 if self.do_not_scramble {
112 flags |= DO_NOT_SCRAMBLE_MASK;
113 }
114 flags |= (self.control_remote_access_over_internet << CONTROL_REMOTE_ACCESS_SHIFT)
115 & CONTROL_REMOTE_ACCESS_MASK;
116 if self.do_not_apply_revocation {
117 flags |= DO_NOT_APPLY_REVOCATION_MASK;
118 }
119 buf[0] = TAG;
120 buf[1] = BODY_LEN as u8;
121 buf[HEADER_LEN] = flags;
122 Ok(len)
123 }
124}
125
126impl<'a> Descriptor<'a> for FtaContentManagementDescriptor {
127 const TAG: u8 = TAG;
128 fn descriptor_length(&self) -> u8 {
129 BODY_LEN as u8
130 }
131}
132
133impl<'a> crate::traits::DescriptorDef<'a> for FtaContentManagementDescriptor {
134 const TAG: u8 = TAG;
135 const NAME: &'static str = "FTA_CONTENT_MANAGEMENT";
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn parse_extracts_all_fields() {
144 let bytes = [TAG, 1, 0x8D];
147 let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
148 assert!(d.user_defined);
149 assert!(d.do_not_scramble);
150 assert_eq!(d.control_remote_access_over_internet, 0b10);
151 assert!(d.do_not_apply_revocation);
152 }
153
154 #[test]
155 fn parse_ignores_reserved_bits() {
156 let bytes = [TAG, 1, 0x70];
158 let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
159 assert!(!d.user_defined);
160 assert!(!d.do_not_scramble);
161 assert_eq!(d.control_remote_access_over_internet, 0);
162 assert!(!d.do_not_apply_revocation);
163 }
164
165 #[test]
166 fn parse_rejects_wrong_tag() {
167 assert!(matches!(
168 FtaContentManagementDescriptor::parse(&[0x7F, 1, 0]).unwrap_err(),
169 Error::InvalidDescriptor { tag: 0x7F, .. }
170 ));
171 }
172
173 #[test]
174 fn parse_rejects_wrong_length() {
175 assert!(matches!(
176 FtaContentManagementDescriptor::parse(&[TAG, 2, 0, 0]).unwrap_err(),
177 Error::InvalidDescriptor { tag: TAG, .. }
178 ));
179 }
180
181 #[test]
182 fn parse_rejects_short_body() {
183 assert!(matches!(
184 FtaContentManagementDescriptor::parse(&[TAG, 1]).unwrap_err(),
185 Error::BufferTooShort { .. }
186 ));
187 }
188
189 #[test]
190 fn serialize_round_trip() {
191 let d = FtaContentManagementDescriptor {
192 user_defined: true,
193 do_not_scramble: true,
194 control_remote_access_over_internet: 0b10,
195 do_not_apply_revocation: true,
196 };
197 let mut buf = vec![0u8; d.serialized_len()];
198 d.serialize_into(&mut buf).unwrap();
199 assert_eq!(buf, [TAG, 1, 0xFD]);
201 assert_eq!(FtaContentManagementDescriptor::parse(&buf).unwrap(), d);
202 }
203
204 #[test]
205 fn serialize_rejects_too_small_buffer() {
206 let d = FtaContentManagementDescriptor {
207 user_defined: false,
208 do_not_scramble: false,
209 control_remote_access_over_internet: 0,
210 do_not_apply_revocation: false,
211 };
212 let mut buf = vec![0u8; 2];
213 assert!(matches!(
214 d.serialize_into(&mut buf).unwrap_err(),
215 Error::OutputBufferTooSmall { .. }
216 ));
217 }
218
219 #[test]
220 fn serialize_rejects_over_range_cra() {
221 let d = FtaContentManagementDescriptor {
222 user_defined: false,
223 do_not_scramble: false,
224 control_remote_access_over_internet: 0b100, do_not_apply_revocation: false,
226 };
227 let mut buf = vec![0u8; d.serialized_len()];
228 assert!(matches!(
229 d.serialize_into(&mut buf).unwrap_err(),
230 Error::InvalidDescriptor { tag: TAG, .. }
231 ));
232 }
233
234 #[cfg(feature = "serde")]
235 #[test]
236 fn serde_round_trip() {
237 let d = FtaContentManagementDescriptor {
238 user_defined: true,
239 do_not_scramble: false,
240 control_remote_access_over_internet: 0b01,
241 do_not_apply_revocation: true,
242 };
243 let json = serde_json::to_string(&d).unwrap();
244 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
246 }
247}