Skip to main content

dvb_si/descriptors/
multilingual_service_name.rs

1//! Multilingual Service Name Descriptor — ETSI EN 300 468 §6.2.25 (tag 0x5D).
2//!
3//! Table 79 (PDF p. 95). Carried in the SDT. A loop of entries, each carrying
4//! an ISO 639-2 language code plus TWO length-prefixed strings: the service
5//! provider name and the service name.
6
7use super::descriptor_body;
8use crate::error::{Error, Result};
9use crate::text::{DvbText, LangCode};
10use dvb_common::{Parse, Serialize};
11
12/// Descriptor tag for multilingual_service_name_descriptor.
13pub const TAG: u8 = 0x5D;
14const HEADER_LEN: usize = 2;
15const LANG_LEN: usize = 3;
16const LEN_FIELD: usize = 1;
17
18/// One localised (provider name, service name) pair.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
22pub struct ServiceNameEntry<'a> {
23    /// ISO 639-2 language code.
24    pub language_code: LangCode,
25    /// DVB Annex-A encoded service provider name.
26    pub service_provider_name: DvbText<'a>,
27    /// DVB Annex-A encoded service name.
28    pub service_name: DvbText<'a>,
29}
30
31/// Multilingual Service Name Descriptor (tag 0x5D).
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
35pub struct MultilingualServiceNameDescriptor<'a> {
36    /// Localised name pairs in wire order.
37    pub entries: Vec<ServiceNameEntry<'a>>,
38}
39
40impl<'a> Parse<'a> for MultilingualServiceNameDescriptor<'a> {
41    type Error = crate::error::Error;
42    fn parse(bytes: &'a [u8]) -> Result<Self> {
43        let body = descriptor_body(
44            bytes,
45            TAG,
46            "MultilingualServiceNameDescriptor",
47            "unexpected tag for multilingual_service_name_descriptor",
48        )?;
49        let mut entries = Vec::new();
50        let mut pos = 0;
51        while pos < body.len() {
52            if pos + LANG_LEN + LEN_FIELD > body.len() {
53                return Err(Error::InvalidDescriptor {
54                    tag: TAG,
55                    reason: "entry header runs past descriptor end",
56                });
57            }
58            let language_code = LangCode([body[pos], body[pos + 1], body[pos + 2]]);
59            let provider_len_pos = pos + LANG_LEN;
60            let provider_len = body[provider_len_pos] as usize;
61            let provider_start = provider_len_pos + LEN_FIELD;
62            let provider_end = provider_start + provider_len;
63            if provider_end + LEN_FIELD > body.len() {
64                return Err(Error::InvalidDescriptor {
65                    tag: TAG,
66                    reason: "service_provider_name_length runs past descriptor end",
67                });
68            }
69            let service_provider_name = DvbText::new(&body[provider_start..provider_end]);
70            let service_len = body[provider_end] as usize;
71            let service_start = provider_end + LEN_FIELD;
72            let service_end = service_start + service_len;
73            if service_end > body.len() {
74                return Err(Error::InvalidDescriptor {
75                    tag: TAG,
76                    reason: "service_name_length runs past descriptor end",
77                });
78            }
79            let service_name = DvbText::new(&body[service_start..service_end]);
80            entries.push(ServiceNameEntry {
81                language_code,
82                service_provider_name,
83                service_name,
84            });
85            pos = service_end;
86        }
87        Ok(Self { entries })
88    }
89}
90
91impl Serialize for MultilingualServiceNameDescriptor<'_> {
92    type Error = crate::error::Error;
93    fn serialized_len(&self) -> usize {
94        HEADER_LEN
95            + self
96                .entries
97                .iter()
98                .map(|e| {
99                    LANG_LEN
100                        + LEN_FIELD
101                        + e.service_provider_name.len()
102                        + LEN_FIELD
103                        + e.service_name.len()
104                })
105                .sum::<usize>()
106    }
107
108    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
109        for e in &self.entries {
110            if e.service_provider_name.len() > u8::MAX as usize {
111                return Err(Error::InvalidDescriptor {
112                    tag: TAG,
113                    reason: "service_provider_name exceeds 255 bytes (length is 8-bit)",
114                });
115            }
116            if e.service_name.len() > u8::MAX as usize {
117                return Err(Error::InvalidDescriptor {
118                    tag: TAG,
119                    reason: "service_name exceeds 255 bytes (length is 8-bit)",
120                });
121            }
122        }
123        let len = self.serialized_len();
124        let body = len - HEADER_LEN;
125        if body > u8::MAX as usize {
126            return Err(Error::InvalidDescriptor {
127                tag: TAG,
128                reason: "multilingual_service_name_descriptor body exceeds 255 bytes",
129            });
130        }
131        if buf.len() < len {
132            return Err(Error::OutputBufferTooSmall {
133                need: len,
134                have: buf.len(),
135            });
136        }
137        buf[0] = TAG;
138        buf[1] = body as u8;
139        let mut pos = HEADER_LEN;
140        for e in &self.entries {
141            buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
142            pos += LANG_LEN;
143            buf[pos] = e.service_provider_name.len() as u8;
144            pos += LEN_FIELD;
145            buf[pos..pos + e.service_provider_name.len()]
146                .copy_from_slice(e.service_provider_name.raw());
147            pos += e.service_provider_name.len();
148            buf[pos] = e.service_name.len() as u8;
149            pos += LEN_FIELD;
150            buf[pos..pos + e.service_name.len()].copy_from_slice(e.service_name.raw());
151            pos += e.service_name.len();
152        }
153        Ok(len)
154    }
155}
156impl<'a> crate::traits::DescriptorDef<'a> for MultilingualServiceNameDescriptor<'a> {
157    const TAG: u8 = TAG;
158    const NAME: &'static str = "MULTILINGUAL_SERVICE_NAME";
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    fn build(entries: &[([u8; 3], &[u8], &[u8])]) -> Vec<u8> {
166        let body: usize = entries
167            .iter()
168            .map(|(_, p, s)| LANG_LEN + 1 + p.len() + 1 + s.len())
169            .sum();
170        let mut v = Vec::with_capacity(HEADER_LEN + body);
171        v.push(TAG);
172        v.push(body as u8);
173        for (lang, provider, service) in entries {
174            v.extend_from_slice(lang);
175            v.push(provider.len() as u8);
176            v.extend_from_slice(provider);
177            v.push(service.len() as u8);
178            v.extend_from_slice(service);
179        }
180        v
181    }
182
183    #[test]
184    fn parse_single_entry() {
185        let bytes = build(&[(*b"eng", b"BBC", b"One")]);
186        let d = MultilingualServiceNameDescriptor::parse(&bytes).unwrap();
187        assert_eq!(d.entries.len(), 1);
188        assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
189        assert_eq!(d.entries[0].service_provider_name.raw(), b"BBC");
190        assert_eq!(d.entries[0].service_name.raw(), b"One");
191    }
192
193    #[test]
194    fn parse_multiple_entries() {
195        let bytes = build(&[(*b"eng", b"Prov", b"Svc"), (*b"fra", b"Fourn", b"Chaine")]);
196        let d = MultilingualServiceNameDescriptor::parse(&bytes).unwrap();
197        assert_eq!(d.entries.len(), 2);
198        assert_eq!(d.entries[1].service_name.raw(), b"Chaine");
199    }
200
201    #[test]
202    fn parse_empty_names_valid() {
203        let bytes = build(&[(*b"deu", b"", b"")]);
204        let d = MultilingualServiceNameDescriptor::parse(&bytes).unwrap();
205        assert!(d.entries[0].service_provider_name.raw().is_empty());
206        assert!(d.entries[0].service_name.raw().is_empty());
207    }
208
209    #[test]
210    fn parse_rejects_wrong_tag() {
211        let err = MultilingualServiceNameDescriptor::parse(&[0x5E, 0]).unwrap_err();
212        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x5E, .. }));
213    }
214
215    #[test]
216    fn parse_rejects_short_buffer() {
217        let err = MultilingualServiceNameDescriptor::parse(&[TAG]).unwrap_err();
218        assert!(matches!(err, Error::BufferTooShort { .. }));
219    }
220
221    #[test]
222    fn parse_rejects_provider_length_overrun() {
223        // provider_len=100 but body tiny.
224        let bytes = [TAG, 5, b'e', b'n', b'g', 100, 0];
225        let err = MultilingualServiceNameDescriptor::parse(&bytes).unwrap_err();
226        assert!(matches!(err, Error::InvalidDescriptor { .. }));
227    }
228
229    #[test]
230    fn parse_rejects_service_length_overrun() {
231        // lang + provider_len=0 + service_len=100, no service bytes.
232        let bytes = [TAG, 5, b'e', b'n', b'g', 0, 100];
233        let err = MultilingualServiceNameDescriptor::parse(&bytes).unwrap_err();
234        assert!(matches!(err, Error::InvalidDescriptor { .. }));
235    }
236
237    #[test]
238    fn empty_descriptor_valid() {
239        let d = MultilingualServiceNameDescriptor::parse(&[TAG, 0]).unwrap();
240        assert_eq!(d.entries.len(), 0);
241    }
242
243    #[test]
244    fn serialize_round_trip() {
245        let bytes = build(&[
246            (*b"eng", b"Provider", b"Channel"),
247            (*b"deu", b"Anbieter", b"Sender"),
248        ]);
249        let parsed = MultilingualServiceNameDescriptor::parse(&bytes).unwrap();
250        let mut buf = vec![0u8; parsed.serialized_len()];
251        parsed.serialize_into(&mut buf).unwrap();
252        assert_eq!(buf, bytes);
253        let re = MultilingualServiceNameDescriptor::parse(&buf).unwrap();
254        assert_eq!(parsed, re);
255    }
256
257    #[test]
258    fn serialize_rejects_too_small_buffer() {
259        let d = MultilingualServiceNameDescriptor {
260            entries: vec![ServiceNameEntry {
261                language_code: LangCode(*b"eng"),
262                service_provider_name: DvbText::new(b"P"),
263                service_name: DvbText::new(b"S"),
264            }],
265        };
266        let mut tiny = [0u8; 3];
267        let err = d.serialize_into(&mut tiny).unwrap_err();
268        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
269    }
270
271    #[test]
272    fn serialize_rejects_over_range_provider_name() {
273        let provider = vec![0u8; 256];
274        let d = MultilingualServiceNameDescriptor {
275            entries: vec![ServiceNameEntry {
276                language_code: LangCode(*b"eng"),
277                service_provider_name: DvbText::new(&provider),
278                service_name: DvbText::new(b"S"),
279            }],
280        };
281        let mut buf = vec![0u8; d.serialized_len()];
282        let err = d.serialize_into(&mut buf).unwrap_err();
283        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
284    }
285
286    #[cfg(feature = "serde")]
287    #[test]
288    fn serde_serialize_is_stable() {
289        let d = MultilingualServiceNameDescriptor {
290            entries: vec![ServiceNameEntry {
291                language_code: LangCode(*b"eng"),
292                service_provider_name: DvbText::new(b"BBC"),
293                service_name: DvbText::new(b"One"),
294            }],
295        };
296        let json = serde_json::to_string(&d).unwrap();
297        assert!(json.contains("\"language_code\""));
298        assert!(json.contains("\"eng\""));
299        assert!(json.contains("\"One\""));
300        assert!(json.contains("\"BBC\""));
301    }
302}