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