Skip to main content

dvb_si/descriptors/
ca_identifier.rs

1//! CA Identifier Descriptor — ETSI EN 300 468 §6.2.6 (tag 0x53).
2//!
3//! Table 22 (PDF p. 57). Carried in SDT/BAT/EIT descriptor loops; lists the
4//! `CA_system_id`s whose conditional-access systems are available for the
5//! associated service/bouquet/event. Body is simply `N` × 16-bit CAIDs.
6
7pub use super::ca::ca_system_name;
8use super::descriptor_body;
9use crate::error::{Error, Result};
10use alloc::vec::Vec;
11use dvb_common::{Parse, Serialize};
12
13/// Descriptor tag for CA_identifier_descriptor.
14pub const TAG: u8 = 0x53;
15const HEADER_LEN: usize = 2;
16const ENTRY_LEN: usize = 2;
17/// Maximum body length expressible in the 8-bit `descriptor_length` field.
18const MAX_BODY_LEN: usize = u8::MAX as usize;
19
20/// CA Identifier Descriptor.
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23pub struct CaIdentifierDescriptor {
24    /// CA_system_id values in wire order.
25    pub ca_system_ids: Vec<u16>,
26}
27
28impl<'a> Parse<'a> for CaIdentifierDescriptor {
29    type Error = crate::error::Error;
30    fn parse(bytes: &'a [u8]) -> Result<Self> {
31        let body = descriptor_body(
32            bytes,
33            TAG,
34            "CaIdentifierDescriptor",
35            "unexpected tag for CA_identifier_descriptor",
36        )?;
37        if body.len() % ENTRY_LEN != 0 {
38            return Err(Error::InvalidDescriptor {
39                tag: TAG,
40                reason: "descriptor_length must be a multiple of 2",
41            });
42        }
43        let mut ca_system_ids = Vec::with_capacity(body.len() / ENTRY_LEN);
44        for chunk in body.chunks_exact(ENTRY_LEN) {
45            // chunks_exact(2) guarantees 2 bytes; unwrap is safe.
46            ca_system_ids.push(u16::from_be_bytes(*chunk.first_chunk::<2>().unwrap()));
47        }
48        Ok(Self { ca_system_ids })
49    }
50}
51
52impl Serialize for CaIdentifierDescriptor {
53    type Error = crate::error::Error;
54    fn serialized_len(&self) -> usize {
55        HEADER_LEN + ENTRY_LEN * self.ca_system_ids.len()
56    }
57
58    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
59        let len = self.serialized_len();
60        if buf.len() < len {
61            return Err(Error::OutputBufferTooSmall {
62                need: len,
63                have: buf.len(),
64            });
65        }
66        let body_len = ENTRY_LEN * self.ca_system_ids.len();
67        // 8-bit descriptor_length field: error rather than silently truncate.
68        if body_len > MAX_BODY_LEN {
69            return Err(Error::InvalidDescriptor {
70                tag: TAG,
71                reason: "CA_identifier_descriptor body exceeds 255 bytes",
72            });
73        }
74        buf[0] = TAG;
75        buf[1] = body_len as u8;
76        let mut pos = HEADER_LEN;
77        for caid in &self.ca_system_ids {
78            buf[pos..pos + ENTRY_LEN].copy_from_slice(&caid.to_be_bytes());
79            pos += ENTRY_LEN;
80        }
81        Ok(len)
82    }
83}
84impl<'a> crate::traits::DescriptorDef<'a> for CaIdentifierDescriptor {
85    const TAG: u8 = TAG;
86    const NAME: &'static str = "CA_IDENTIFIER";
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn parse_single_caid() {
95        let bytes = [TAG, 2, 0x06, 0x50];
96        let d = CaIdentifierDescriptor::parse(&bytes).unwrap();
97        assert_eq!(d.ca_system_ids, vec![0x0650]);
98    }
99
100    #[test]
101    fn parse_multiple_caids_preserves_order() {
102        let bytes = [TAG, 6, 0x05, 0x00, 0x06, 0x50, 0x0B, 0x00];
103        let d = CaIdentifierDescriptor::parse(&bytes).unwrap();
104        assert_eq!(d.ca_system_ids, vec![0x0500, 0x0650, 0x0B00]);
105    }
106
107    #[test]
108    fn parse_rejects_wrong_tag() {
109        assert!(matches!(
110            CaIdentifierDescriptor::parse(&[0x54, 2, 0x06, 0x50]).unwrap_err(),
111            Error::InvalidDescriptor { tag: 0x54, .. }
112        ));
113    }
114
115    #[test]
116    fn parse_rejects_short_buffer() {
117        // declares 4 body bytes, only 2 present
118        let bytes = [TAG, 4, 0x06, 0x50];
119        assert!(matches!(
120            CaIdentifierDescriptor::parse(&bytes).unwrap_err(),
121            Error::BufferTooShort { .. }
122        ));
123    }
124
125    #[test]
126    fn parse_rejects_odd_length() {
127        let bytes = [TAG, 3, 0x06, 0x50, 0x00];
128        assert!(matches!(
129            CaIdentifierDescriptor::parse(&bytes).unwrap_err(),
130            Error::InvalidDescriptor { tag: TAG, .. }
131        ));
132    }
133
134    #[test]
135    fn empty_descriptor_valid() {
136        let d = CaIdentifierDescriptor::parse(&[TAG, 0]).unwrap();
137        assert!(d.ca_system_ids.is_empty());
138    }
139
140    #[test]
141    fn serialize_round_trip() {
142        let d = CaIdentifierDescriptor {
143            ca_system_ids: vec![0x0100, 0x1800, 0x2600],
144        };
145        let mut buf = vec![0u8; d.serialized_len()];
146        d.serialize_into(&mut buf).unwrap();
147        assert_eq!(CaIdentifierDescriptor::parse(&buf).unwrap(), d);
148    }
149
150    #[test]
151    fn serialize_rejects_over_range_body() {
152        // 128 CAIDs = 256 body bytes, one past the u8 length field.
153        let d = CaIdentifierDescriptor {
154            ca_system_ids: vec![0x0500; 128],
155        };
156        let mut buf = vec![0u8; d.serialized_len()];
157        assert!(matches!(
158            d.serialize_into(&mut buf).unwrap_err(),
159            Error::InvalidDescriptor { tag: TAG, .. }
160        ));
161    }
162
163    #[cfg(feature = "serde")]
164    #[test]
165    fn serde_round_trip() {
166        let d = CaIdentifierDescriptor {
167            ca_system_ids: vec![0x0500, 0x0650],
168        };
169        let json = serde_json::to_string(&d).unwrap();
170        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
171        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
172    }
173}