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