Skip to main content

dvb_si/descriptors/
fta_content_management.rs

1//! FTA Content Management Descriptor — ETSI EN 300 468 §6.2.18.1 (tag 0x7E, Table 57, PDF p. 82).
2//!
3//! Carried in NIT/BAT/SDT/EIT. Fixed 1-byte body packing five fields
4//! (Table 57, MSB→LSB): `user_defined` (1), `reserved_future_use` (3),
5//! `do_not_scramble` (1), `control_remote_access_over_internet` (2),
6//! `do_not_apply_revocation` (1).
7//! `control_remote_access_over_internet` coding is Table 58.
8
9use crate::error::{Error, Result};
10use crate::traits::Descriptor;
11use dvb_common::{Parse, Serialize};
12
13/// Descriptor tag for FTA_content_management_descriptor.
14pub const TAG: u8 = 0x7E;
15/// Length of the header (tag byte + length byte).
16pub const HEADER_LEN: usize = 2;
17/// Fixed body length: one packed flag byte.
18pub 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;
26/// Max value of the 2-bit control_remote_access_over_internet field.
27pub const CONTROL_REMOTE_ACCESS_MAX: u8 = 0b11;
28
29/// FTA Content Management Descriptor.
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32pub struct FtaContentManagementDescriptor {
33    /// 1-bit user_defined flag.
34    pub user_defined: bool,
35    /// 1-bit do_not_scramble flag.
36    pub do_not_scramble: bool,
37    /// 2-bit control_remote_access_over_internet field (Table 58).
38    pub control_remote_access_over_internet: u8,
39    /// 1-bit do_not_apply_revocation flag.
40    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        // reserved_future_use (3 bits) ignored on parse (§5.1).
76        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        // reserved_future_use 3 bits emitted as 1s (§5.1).
107        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        // user_defined=1, reserved=000, do_not_scramble=1, cra=10, revocation=1
145        // = 1 000 1 10 1 = 0b1000_1101 = 0x8D
146        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        // reserved bits all set, everything else zero: 0 111 0 00 0 = 0x70.
157        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        // Reserved 3 bits emitted as 1s: 1 111 1 10 1 = 0xFD.
200        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, // 3 bits
225            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        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
245        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
246    }
247}