Skip to main content

dvb_si/descriptors/ait/
application_name.rs

1//! Application Name Descriptor — ETSI TS 102 809 §5.3.5.6.2, Table 24
2//! (AIT tag 0x01).
3//!
4//! Carried in the AIT per-application descriptor loop. A multilingual loop of
5//! (ISO 639 language code + name) pairs, following the same pattern as the
6//! SI multilingual descriptors.
7
8use crate::descriptors::descriptor_body;
9use crate::error::{Error, Result};
10use crate::text::{DvbText, LangCode};
11use alloc::vec::Vec;
12use dvb_common::{Parse, Serialize};
13
14/// Descriptor tag for application_name_descriptor (AIT namespace).
15pub const TAG: u8 = 0x01;
16const HEADER_LEN: usize = 2;
17const LANG_LEN: usize = 3;
18const NAME_LEN_FIELD: usize = 1;
19
20/// One localised application name.
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize))]
23#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
24pub struct ApplicationNameEntry<'a> {
25    /// ISO 639-2 language code.
26    pub language_code: LangCode,
27    /// DVB Annex-A encoded application name.
28    pub application_name: DvbText<'a>,
29}
30
31/// Application Name Descriptor (AIT tag 0x01).
32#[derive(Debug, Clone, PartialEq, Eq)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
35pub struct ApplicationNameDescriptor<'a> {
36    /// Localised names in wire order.
37    pub entries: Vec<ApplicationNameEntry<'a>>,
38}
39
40impl<'a> Parse<'a> for ApplicationNameDescriptor<'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            "ApplicationNameDescriptor",
47            "unexpected tag for application_name_descriptor",
48        )?;
49        let mut entries = Vec::new();
50        let mut pos = 0;
51        while pos < body.len() {
52            if pos + LANG_LEN + NAME_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 name_len = body[pos + LANG_LEN] as usize;
60            let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
61            let name_end = name_start + name_len;
62            if name_end > body.len() {
63                return Err(Error::InvalidDescriptor {
64                    tag: TAG,
65                    reason: "application_name_length runs past descriptor end",
66                });
67            }
68            entries.push(ApplicationNameEntry {
69                language_code,
70                application_name: DvbText::new(&body[name_start..name_end]),
71            });
72            pos = name_end;
73        }
74        Ok(Self { entries })
75    }
76}
77
78impl Serialize for ApplicationNameDescriptor<'_> {
79    type Error = crate::error::Error;
80    fn serialized_len(&self) -> usize {
81        HEADER_LEN
82            + self
83                .entries
84                .iter()
85                .map(|e| LANG_LEN + NAME_LEN_FIELD + e.application_name.len())
86                .sum::<usize>()
87    }
88
89    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
90        for e in &self.entries {
91            if e.application_name.len() > u8::MAX as usize {
92                return Err(Error::InvalidDescriptor {
93                    tag: TAG,
94                    reason: "application_name exceeds 255 bytes",
95                });
96            }
97        }
98        let len = self.serialized_len();
99        let body_len = len - HEADER_LEN;
100        if body_len > u8::MAX as usize {
101            return Err(Error::InvalidDescriptor {
102                tag: TAG,
103                reason: "application_name_descriptor body exceeds 255 bytes",
104            });
105        }
106        if buf.len() < len {
107            return Err(Error::OutputBufferTooSmall {
108                need: len,
109                have: buf.len(),
110            });
111        }
112        buf[0] = TAG;
113        buf[1] = body_len as u8;
114        let mut pos = HEADER_LEN;
115        for e in &self.entries {
116            buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
117            buf[pos + LANG_LEN] = e.application_name.len() as u8;
118            let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
119            buf[name_start..name_start + e.application_name.len()]
120                .copy_from_slice(e.application_name.raw());
121            pos = name_start + e.application_name.len();
122        }
123        Ok(len)
124    }
125}
126
127impl<'a> crate::traits::DescriptorDef<'a> for ApplicationNameDescriptor<'a> {
128    const TAG: u8 = TAG;
129    const NAME: &'static str = "APPLICATION_NAME";
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    /// Body: lang(3) + name_len(1) + "Foo"(3) = 7.
137    fn build_single_entry_foo() -> [u8; 9] {
138        [TAG, 7, b'e', b'n', b'g', 3, b'F', b'o', b'o']
139    }
140
141    /// Body: "eng"/"Foo"(7) + "fra"/"Bar"(7) = 14.
142    fn build_two_entries() -> [u8; 16] {
143        [
144            TAG, 14, b'e', b'n', b'g', 3, b'F', b'o', b'o', b'f', b'r', b'a', 3, b'B', b'a', b'r',
145        ]
146    }
147
148    #[test]
149    fn parse_single_entry() {
150        let bytes = build_single_entry_foo();
151        let d = ApplicationNameDescriptor::parse(&bytes).unwrap();
152        assert_eq!(d.entries.len(), 1);
153        assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
154        assert_eq!(d.entries[0].application_name.raw(), b"Foo");
155    }
156
157    #[test]
158    fn parse_multiple_entries() {
159        let bytes = build_two_entries();
160        let d = ApplicationNameDescriptor::parse(&bytes).unwrap();
161        assert_eq!(d.entries.len(), 2);
162        assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
163        assert_eq!(d.entries[1].language_code, LangCode(*b"fra"));
164    }
165
166    #[test]
167    fn serialize_round_trip() {
168        let d = ApplicationNameDescriptor {
169            entries: alloc::vec![
170                ApplicationNameEntry {
171                    language_code: LangCode(*b"eng"),
172                    application_name: DvbText::new(b"HbbTV"),
173                },
174                ApplicationNameEntry {
175                    language_code: LangCode(*b"deu"),
176                    application_name: DvbText::new(b"App"),
177                },
178            ],
179        };
180        let mut buf = vec![0u8; d.serialized_len()];
181        d.serialize_into(&mut buf).unwrap();
182        let re = ApplicationNameDescriptor::parse(&buf).unwrap();
183        assert_eq!(d, re);
184    }
185
186    #[test]
187    fn serialize_byte_identical_single() {
188        let bytes = build_single_entry_foo();
189        let d = ApplicationNameDescriptor::parse(&bytes).unwrap();
190        let mut buf = vec![0u8; d.serialized_len()];
191        d.serialize_into(&mut buf).unwrap();
192        assert_eq!(buf.as_slice(), &bytes[..]);
193    }
194
195    #[test]
196    fn serialize_byte_identical_two() {
197        let bytes = build_two_entries();
198        let d = ApplicationNameDescriptor::parse(&bytes).unwrap();
199        let mut buf = vec![0u8; d.serialized_len()];
200        d.serialize_into(&mut buf).unwrap();
201        assert_eq!(buf.as_slice(), &bytes[..]);
202    }
203}