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