Skip to main content

dvb_si/descriptors/
multilingual_network_name.rs

1//! Multilingual Network Name Descriptor — ETSI EN 300 468 §6.2.24 (tag 0x5B).
2//!
3//! Table 78 (PDF p. 94). Carried in the NIT. A loop of (ISO 639-2 language
4//! code, network name) pairs, each name length-prefixed by an 8-bit field.
5
6use crate::error::{Error, Result};
7use crate::text::{DvbText, LangCode};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for multilingual_network_name_descriptor.
12pub const TAG: u8 = 0x5B;
13const HEADER_LEN: usize = 2;
14const LANG_LEN: usize = 3;
15const NAME_LEN_FIELD: usize = 1;
16
17/// One localised network name.
18#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct NetworkNameEntry<'a> {
21    /// ISO 639-2 language code.
22    pub language_code: LangCode,
23    /// DVB Annex-A encoded network name.
24    pub network_name: DvbText<'a>,
25}
26
27/// Multilingual Network Name Descriptor (tag 0x5B).
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize))]
30pub struct MultilingualNetworkNameDescriptor<'a> {
31    /// Localised names in wire order.
32    pub entries: Vec<NetworkNameEntry<'a>>,
33}
34
35impl<'a> Parse<'a> for MultilingualNetworkNameDescriptor<'a> {
36    type Error = crate::error::Error;
37    fn parse(bytes: &'a [u8]) -> Result<Self> {
38        if bytes.len() < HEADER_LEN {
39            return Err(Error::BufferTooShort {
40                need: HEADER_LEN,
41                have: bytes.len(),
42                what: "MultilingualNetworkNameDescriptor header",
43            });
44        }
45        if bytes[0] != TAG {
46            return Err(Error::InvalidDescriptor {
47                tag: bytes[0],
48                reason: "unexpected tag for multilingual_network_name_descriptor",
49            });
50        }
51        let length = bytes[1] as usize;
52        let end = HEADER_LEN + length;
53        if bytes.len() < end {
54            return Err(Error::BufferTooShort {
55                need: end,
56                have: bytes.len(),
57                what: "MultilingualNetworkNameDescriptor body",
58            });
59        }
60        let mut entries = Vec::new();
61        let mut pos = HEADER_LEN;
62        while pos < end {
63            if pos + LANG_LEN + NAME_LEN_FIELD > end {
64                return Err(Error::InvalidDescriptor {
65                    tag: TAG,
66                    reason: "entry header runs past descriptor end",
67                });
68            }
69            let language_code = LangCode([bytes[pos], bytes[pos + 1], bytes[pos + 2]]);
70            let name_len = bytes[pos + LANG_LEN] as usize;
71            let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
72            let name_end = name_start + name_len;
73            if name_end > end {
74                return Err(Error::InvalidDescriptor {
75                    tag: TAG,
76                    reason: "name_length runs past descriptor end",
77                });
78            }
79            entries.push(NetworkNameEntry {
80                language_code,
81                network_name: DvbText::new(&bytes[name_start..name_end]),
82            });
83            pos = name_end;
84        }
85        Ok(Self { entries })
86    }
87}
88
89impl Serialize for MultilingualNetworkNameDescriptor<'_> {
90    type Error = crate::error::Error;
91    fn serialized_len(&self) -> usize {
92        HEADER_LEN
93            + self
94                .entries
95                .iter()
96                .map(|e| LANG_LEN + NAME_LEN_FIELD + e.network_name.len())
97                .sum::<usize>()
98    }
99
100    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
101        for e in &self.entries {
102            if e.network_name.len() > u8::MAX as usize {
103                return Err(Error::InvalidDescriptor {
104                    tag: TAG,
105                    reason: "network_name exceeds 255 bytes (name_length is 8-bit)",
106                });
107            }
108        }
109        let len = self.serialized_len();
110        let body = len - HEADER_LEN;
111        if body > u8::MAX as usize {
112            return Err(Error::InvalidDescriptor {
113                tag: TAG,
114                reason: "multilingual_network_name_descriptor body exceeds 255 bytes",
115            });
116        }
117        if buf.len() < len {
118            return Err(Error::OutputBufferTooSmall {
119                need: len,
120                have: buf.len(),
121            });
122        }
123        buf[0] = TAG;
124        buf[1] = body as u8;
125        let mut pos = HEADER_LEN;
126        for e in &self.entries {
127            buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
128            buf[pos + LANG_LEN] = e.network_name.len() as u8;
129            let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
130            buf[name_start..name_start + e.network_name.len()]
131                .copy_from_slice(e.network_name.raw());
132            pos = name_start + e.network_name.len();
133        }
134        Ok(len)
135    }
136}
137
138impl<'a> Descriptor<'a> for MultilingualNetworkNameDescriptor<'a> {
139    const TAG: u8 = TAG;
140    fn descriptor_length(&self) -> u8 {
141        (self.serialized_len() - HEADER_LEN) as u8
142    }
143}
144
145impl<'a> crate::traits::DescriptorDef<'a> for MultilingualNetworkNameDescriptor<'a> {
146    const TAG: u8 = TAG;
147    const NAME: &'static str = "MULTILINGUAL_NETWORK_NAME";
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    fn build(entries: &[([u8; 3], &[u8])]) -> Vec<u8> {
155        let body: usize = entries.iter().map(|(_, n)| LANG_LEN + 1 + n.len()).sum();
156        let mut v = Vec::with_capacity(HEADER_LEN + body);
157        v.push(TAG);
158        v.push(body as u8);
159        for (lang, name) in entries {
160            v.extend_from_slice(lang);
161            v.push(name.len() as u8);
162            v.extend_from_slice(name);
163        }
164        v
165    }
166
167    #[test]
168    fn parse_single_entry() {
169        let bytes = build(&[(*b"eng", b"BBC")]);
170        let d = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap();
171        assert_eq!(d.entries.len(), 1);
172        assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
173        assert_eq!(d.entries[0].network_name.raw(), b"BBC");
174    }
175
176    #[test]
177    fn parse_multiple_entries() {
178        let bytes = build(&[(*b"eng", b"Net"), (*b"fra", b"Reseau")]);
179        let d = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap();
180        assert_eq!(d.entries.len(), 2);
181        assert_eq!(d.entries[1].network_name.raw(), b"Reseau");
182    }
183
184    #[test]
185    fn parse_rejects_wrong_tag() {
186        let err = MultilingualNetworkNameDescriptor::parse(&[0x5C, 0]).unwrap_err();
187        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x5C, .. }));
188    }
189
190    #[test]
191    fn parse_rejects_short_buffer() {
192        let err = MultilingualNetworkNameDescriptor::parse(&[TAG]).unwrap_err();
193        assert!(matches!(err, Error::BufferTooShort { .. }));
194    }
195
196    #[test]
197    fn parse_rejects_name_length_overrun() {
198        // name_len=100 but body only carries a few bytes.
199        let bytes = [TAG, 5, b'e', b'n', b'g', 100, 0];
200        let err = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap_err();
201        assert!(matches!(err, Error::InvalidDescriptor { .. }));
202    }
203
204    #[test]
205    fn parse_rejects_truncated_entry_header() {
206        // Only 2 bytes of a language code present in the body.
207        let bytes = [TAG, 2, b'e', b'n'];
208        let err = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap_err();
209        assert!(matches!(err, Error::InvalidDescriptor { .. }));
210    }
211
212    #[test]
213    fn empty_descriptor_valid() {
214        let d = MultilingualNetworkNameDescriptor::parse(&[TAG, 0]).unwrap();
215        assert_eq!(d.entries.len(), 0);
216    }
217
218    #[test]
219    fn serialize_round_trip() {
220        let bytes = build(&[(*b"eng", b"Network"), (*b"deu", b"Netz")]);
221        let parsed = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap();
222        let mut buf = vec![0u8; parsed.serialized_len()];
223        parsed.serialize_into(&mut buf).unwrap();
224        assert_eq!(buf, bytes);
225        let re = MultilingualNetworkNameDescriptor::parse(&buf).unwrap();
226        assert_eq!(parsed, re);
227    }
228
229    #[test]
230    fn serialize_rejects_too_small_buffer() {
231        let d = MultilingualNetworkNameDescriptor {
232            entries: vec![NetworkNameEntry {
233                language_code: LangCode(*b"eng"),
234                network_name: DvbText::new(b"X"),
235            }],
236        };
237        let mut tiny = [0u8; 3];
238        let err = d.serialize_into(&mut tiny).unwrap_err();
239        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
240    }
241
242    #[test]
243    fn serialize_rejects_over_range_name() {
244        let name = vec![0u8; 256];
245        let d = MultilingualNetworkNameDescriptor {
246            entries: vec![NetworkNameEntry {
247                language_code: LangCode(*b"eng"),
248                network_name: DvbText::new(&name),
249            }],
250        };
251        let mut buf = vec![0u8; d.serialized_len()];
252        let err = d.serialize_into(&mut buf).unwrap_err();
253        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
254    }
255
256    #[cfg(feature = "serde")]
257    #[test]
258    fn serde_serialize_is_stable() {
259        // Borrowed `&[u8]` cannot be deserialized from a JSON array by
260        // serde_json; matching the borrowed-bytes descriptors in this crate we
261        // exercise the serialize path and assert it is deterministic.
262        let d = MultilingualNetworkNameDescriptor {
263            entries: vec![NetworkNameEntry {
264                language_code: LangCode(*b"eng"),
265                network_name: DvbText::new(b"BBC"),
266            }],
267        };
268        let json = serde_json::to_string(&d).unwrap();
269        assert_eq!(json, serde_json::to_string(&d.clone()).unwrap());
270    }
271}