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