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 super::descriptor_body;
10use crate::error::{Error, Result};
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        let body = descriptor_body(
47            bytes,
48            TAG,
49            "FtaContentManagementDescriptor",
50            "unexpected tag for FTA_content_management_descriptor",
51        )?;
52        if body.len() != BODY_LEN {
53            return Err(Error::InvalidDescriptor {
54                tag: TAG,
55                reason: "FTA_content_management_descriptor length must be exactly 1",
56            });
57        }
58        let flags = body[0];
59        // reserved_future_use (3 bits) ignored on parse (§5.1).
60        Ok(Self {
61            user_defined: flags & USER_DEFINED_MASK != 0,
62            do_not_scramble: flags & DO_NOT_SCRAMBLE_MASK != 0,
63            control_remote_access_over_internet: (flags & CONTROL_REMOTE_ACCESS_MASK)
64                >> CONTROL_REMOTE_ACCESS_SHIFT,
65            do_not_apply_revocation: flags & DO_NOT_APPLY_REVOCATION_MASK != 0,
66        })
67    }
68}
69
70impl Serialize for FtaContentManagementDescriptor {
71    type Error = crate::error::Error;
72    fn serialized_len(&self) -> usize {
73        HEADER_LEN + BODY_LEN
74    }
75
76    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
77        if self.control_remote_access_over_internet > CONTROL_REMOTE_ACCESS_MAX {
78            return Err(Error::InvalidDescriptor {
79                tag: TAG,
80                reason: "control_remote_access_over_internet exceeds 2 bits",
81            });
82        }
83        let len = self.serialized_len();
84        if buf.len() < len {
85            return Err(Error::OutputBufferTooSmall {
86                need: len,
87                have: buf.len(),
88            });
89        }
90        // reserved_future_use 3 bits emitted as 1s (§5.1).
91        let mut flags = RESERVED_MASK;
92        if self.user_defined {
93            flags |= USER_DEFINED_MASK;
94        }
95        if self.do_not_scramble {
96            flags |= DO_NOT_SCRAMBLE_MASK;
97        }
98        flags |= (self.control_remote_access_over_internet << CONTROL_REMOTE_ACCESS_SHIFT)
99            & CONTROL_REMOTE_ACCESS_MASK;
100        if self.do_not_apply_revocation {
101            flags |= DO_NOT_APPLY_REVOCATION_MASK;
102        }
103        buf[0] = TAG;
104        buf[1] = BODY_LEN as u8;
105        buf[HEADER_LEN] = flags;
106        Ok(len)
107    }
108}
109impl<'a> crate::traits::DescriptorDef<'a> for FtaContentManagementDescriptor {
110    const TAG: u8 = TAG;
111    const NAME: &'static str = "FTA_CONTENT_MANAGEMENT";
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn parse_extracts_all_fields() {
120        // user_defined=1, reserved=000, do_not_scramble=1, cra=10, revocation=1
121        // = 1 000 1 10 1 = 0b1000_1101 = 0x8D
122        let bytes = [TAG, 1, 0x8D];
123        let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
124        assert!(d.user_defined);
125        assert!(d.do_not_scramble);
126        assert_eq!(d.control_remote_access_over_internet, 0b10);
127        assert!(d.do_not_apply_revocation);
128    }
129
130    #[test]
131    fn parse_ignores_reserved_bits() {
132        // reserved bits all set, everything else zero: 0 111 0 00 0 = 0x70.
133        let bytes = [TAG, 1, 0x70];
134        let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
135        assert!(!d.user_defined);
136        assert!(!d.do_not_scramble);
137        assert_eq!(d.control_remote_access_over_internet, 0);
138        assert!(!d.do_not_apply_revocation);
139    }
140
141    #[test]
142    fn parse_rejects_wrong_tag() {
143        assert!(matches!(
144            FtaContentManagementDescriptor::parse(&[0x7F, 1, 0]).unwrap_err(),
145            Error::InvalidDescriptor { tag: 0x7F, .. }
146        ));
147    }
148
149    #[test]
150    fn parse_rejects_wrong_length() {
151        assert!(matches!(
152            FtaContentManagementDescriptor::parse(&[TAG, 2, 0, 0]).unwrap_err(),
153            Error::InvalidDescriptor { tag: TAG, .. }
154        ));
155    }
156
157    #[test]
158    fn parse_rejects_short_body() {
159        assert!(matches!(
160            FtaContentManagementDescriptor::parse(&[TAG, 1]).unwrap_err(),
161            Error::BufferTooShort { .. }
162        ));
163    }
164
165    #[test]
166    fn serialize_round_trip() {
167        let d = FtaContentManagementDescriptor {
168            user_defined: true,
169            do_not_scramble: true,
170            control_remote_access_over_internet: 0b10,
171            do_not_apply_revocation: true,
172        };
173        let mut buf = vec![0u8; d.serialized_len()];
174        d.serialize_into(&mut buf).unwrap();
175        // Reserved 3 bits emitted as 1s: 1 111 1 10 1 = 0xFD.
176        assert_eq!(buf, [TAG, 1, 0xFD]);
177        assert_eq!(FtaContentManagementDescriptor::parse(&buf).unwrap(), d);
178    }
179
180    #[test]
181    fn serialize_rejects_too_small_buffer() {
182        let d = FtaContentManagementDescriptor {
183            user_defined: false,
184            do_not_scramble: false,
185            control_remote_access_over_internet: 0,
186            do_not_apply_revocation: false,
187        };
188        let mut buf = vec![0u8; 2];
189        assert!(matches!(
190            d.serialize_into(&mut buf).unwrap_err(),
191            Error::OutputBufferTooSmall { .. }
192        ));
193    }
194
195    #[test]
196    fn serialize_rejects_over_range_cra() {
197        let d = FtaContentManagementDescriptor {
198            user_defined: false,
199            do_not_scramble: false,
200            control_remote_access_over_internet: 0b100, // 3 bits
201            do_not_apply_revocation: false,
202        };
203        let mut buf = vec![0u8; d.serialized_len()];
204        assert!(matches!(
205            d.serialize_into(&mut buf).unwrap_err(),
206            Error::InvalidDescriptor { tag: TAG, .. }
207        ));
208    }
209
210    #[cfg(feature = "serde")]
211    #[test]
212    fn serde_round_trip() {
213        let d = FtaContentManagementDescriptor {
214            user_defined: true,
215            do_not_scramble: false,
216            control_remote_access_over_internet: 0b01,
217            do_not_apply_revocation: true,
218        };
219        let json = serde_json::to_string(&d).unwrap();
220        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
221        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
222    }
223}