1use crate::error::{Error, Result};
7use crate::traits::Descriptor;
8use dvb_common::{Parse, Serialize};
9
10pub const TAG: u8 = 0x09;
12const HEADER_LEN: usize = 2;
13const MIN_BODY_LEN: usize = 4; #[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct CaDescriptor<'a> {
23 pub ca_system_id: u16,
37
38 pub ca_pid: u16,
41
42 pub private_data: &'a [u8],
44}
45
46impl<'a> Parse<'a> for CaDescriptor<'a> {
47 type Error = crate::error::Error;
48
49 fn parse(bytes: &'a [u8]) -> Result<Self> {
50 if bytes.len() < HEADER_LEN {
51 return Err(Error::BufferTooShort {
52 need: HEADER_LEN,
53 have: bytes.len(),
54 what: "CaDescriptor header",
55 });
56 }
57 if bytes[0] != TAG {
58 return Err(Error::InvalidDescriptor {
59 tag: bytes[0],
60 reason: "unexpected tag for CA_descriptor",
61 });
62 }
63 let length = bytes[1] as usize;
64 if length < MIN_BODY_LEN {
65 return Err(Error::InvalidDescriptor {
66 tag: TAG,
67 reason: "CA_descriptor length too short for mandatory fields",
68 });
69 }
70 let total = HEADER_LEN + length;
71 if bytes.len() < total {
72 return Err(Error::BufferTooShort {
73 need: total,
74 have: bytes.len(),
75 what: "CaDescriptor body",
76 });
77 }
78 let body = &bytes[HEADER_LEN..total];
79 let ca_system_id = u16::from_be_bytes([body[0], body[1]]);
80 let ca_pid = ((u16::from(body[2]) & 0x1F) << 8) | u16::from(body[3]);
82 let private_data = if body.len() > MIN_BODY_LEN {
83 &body[MIN_BODY_LEN..]
84 } else {
85 &[]
86 };
87 Ok(Self {
88 ca_system_id,
89 ca_pid,
90 private_data,
91 })
92 }
93}
94
95impl Serialize for CaDescriptor<'_> {
96 type Error = crate::error::Error;
97
98 fn serialized_len(&self) -> usize {
99 HEADER_LEN + MIN_BODY_LEN + self.private_data.len()
100 }
101
102 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
103 let len = self.serialized_len();
104 if buf.len() < len {
105 return Err(Error::OutputBufferTooSmall {
106 need: len,
107 have: buf.len(),
108 });
109 }
110 buf[0] = TAG;
111 buf[1] = (len - HEADER_LEN) as u8;
112 buf[2] = (self.ca_system_id >> 8) as u8;
113 buf[3] = (self.ca_system_id & 0xFF) as u8;
114 buf[4] = 0xE0 | ((self.ca_pid >> 8) as u8);
116 buf[5] = (self.ca_pid & 0xFF) as u8;
117 if !self.private_data.is_empty() {
118 buf[HEADER_LEN + MIN_BODY_LEN..len].copy_from_slice(self.private_data);
119 }
120 Ok(len)
121 }
122}
123
124impl<'a> Descriptor<'a> for CaDescriptor<'a> {
125 const TAG: u8 = TAG;
126
127 fn descriptor_length(&self) -> u8 {
128 (self.serialized_len() - HEADER_LEN) as u8
129 }
130}
131
132impl<'a> crate::traits::DescriptorDef<'a> for CaDescriptor<'a> {
133 const TAG: u8 = TAG;
134 const NAME: &'static str = "CA";
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn parse_viaccess_ecm_pid() {
143 let bytes = [TAG, 4, 0x05, 0x00, 0xE1, 0x01];
145 let d = CaDescriptor::parse(&bytes).unwrap();
146 assert_eq!(d.ca_system_id, 0x0500);
147 assert_eq!(d.ca_pid, 0x0101);
148 assert!(d.private_data.is_empty());
149 }
150
151 #[test]
152 fn parse_with_private_data() {
153 let bytes = [TAG, 6, 0x05, 0x00, 0xE1, 0x01, 0xAA, 0xBB];
155 let d = CaDescriptor::parse(&bytes).unwrap();
156 assert_eq!(d.ca_system_id, 0x0500);
157 assert_eq!(d.ca_pid, 0x0101);
158 assert_eq!(d.private_data, &[0xAA, 0xBB]);
159 }
160
161 #[test]
162 fn parse_rejects_wrong_tag() {
163 let err = CaDescriptor::parse(&[0x0A, 4, 0x05, 0x00, 0xE1, 0x01]).unwrap_err();
164 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x0A, .. }));
165 }
166
167 #[test]
168 fn parse_rejects_short_header() {
169 let err = CaDescriptor::parse(&[TAG]).unwrap_err();
170 assert!(matches!(err, Error::BufferTooShort { .. }));
171 }
172
173 #[test]
174 fn parse_rejects_length_too_short() {
175 let bytes = [TAG, 3, 0x05, 0x00, 0xE1];
176 let err = CaDescriptor::parse(&bytes).unwrap_err();
177 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
178 }
179
180 #[test]
181 fn parse_rejects_length_overflow() {
182 let bytes = [TAG, 10, 0x05, 0x00, 0xE1, 0x01];
183 let err = CaDescriptor::parse(&bytes).unwrap_err();
184 assert!(matches!(err, Error::BufferTooShort { .. }));
185 }
186
187 #[test]
188 fn serialize_round_trip() {
189 let d = CaDescriptor {
190 ca_system_id: 0x1800,
191 ca_pid: 0x0200,
192 private_data: &[0xDE, 0xAD],
193 };
194 let mut buf = vec![0u8; d.serialized_len()];
195 d.serialize_into(&mut buf).unwrap();
196 let reparsed = CaDescriptor::parse(&buf).unwrap();
197 assert_eq!(d, reparsed);
198 }
199
200 #[test]
201 fn serialize_round_trip_no_private_data() {
202 let d = CaDescriptor {
203 ca_system_id: 0x0500,
204 ca_pid: 0x0101,
205 private_data: &[],
206 };
207 let mut buf = vec![0u8; d.serialized_len()];
208 d.serialize_into(&mut buf).unwrap();
209 let reparsed = CaDescriptor::parse(&buf).unwrap();
210 assert_eq!(d, reparsed);
211 }
212
213 #[test]
214 fn serialize_rejects_small_buffer() {
215 let d = CaDescriptor {
216 ca_system_id: 0x0500,
217 ca_pid: 0x0101,
218 private_data: &[],
219 };
220 let mut tiny = vec![0u8; 3];
221 let err = d.serialize_into(&mut tiny).unwrap_err();
222 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
223 }
224
225 #[test]
226 fn descriptor_length_matches_payload() {
227 let d = CaDescriptor {
228 ca_system_id: 0x0500,
229 ca_pid: 0x0101,
230 private_data: &[0xAA],
231 };
232 assert_eq!(d.descriptor_length(), 5);
233 }
234}