Skip to main content

dvb_si/descriptors/
service.rs

1//! Service Descriptor — ETSI EN 300 468 §6.2.33 (tag 0x48).
2//!
3//! Carried inside SDT. Provides the provider and service name plus a
4//! service_type byte classifying the service (TV SD, TV HD, radio, data, …).
5
6use crate::error::{Error, Result};
7use crate::text::DvbText;
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for service_descriptor.
12pub const TAG: u8 = 0x48;
13const HEADER_LEN: usize = 2;
14
15/// Service Descriptor.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct ServiceDescriptor<'a> {
19    /// service_type byte (ETSI Table 87).
20    pub service_type: u8,
21    /// DVB Annex-A encoded provider name.
22    pub provider_name: DvbText<'a>,
23    /// DVB Annex-A encoded service name.
24    pub service_name: DvbText<'a>,
25}
26
27impl<'a> Parse<'a> for ServiceDescriptor<'a> {
28    type Error = crate::error::Error;
29    fn parse(bytes: &'a [u8]) -> Result<Self> {
30        if bytes.len() < HEADER_LEN {
31            return Err(Error::BufferTooShort {
32                need: HEADER_LEN,
33                have: bytes.len(),
34                what: "ServiceDescriptor header",
35            });
36        }
37        if bytes[0] != TAG {
38            return Err(Error::InvalidDescriptor {
39                tag: bytes[0],
40                reason: "unexpected tag for service_descriptor",
41            });
42        }
43        let length = bytes[1] as usize;
44        let end = HEADER_LEN + length;
45        if bytes.len() < end {
46            return Err(Error::BufferTooShort {
47                need: end,
48                have: bytes.len(),
49                what: "ServiceDescriptor body",
50            });
51        }
52        if length < 3 {
53            return Err(Error::InvalidDescriptor {
54                tag: TAG,
55                reason: "service_descriptor body too short for service_type + two length fields",
56            });
57        }
58        let service_type = bytes[HEADER_LEN];
59        let provider_len = bytes[HEADER_LEN + 1] as usize;
60        let provider_end = HEADER_LEN + 2 + provider_len;
61        if provider_end + 1 > end {
62            return Err(Error::InvalidDescriptor {
63                tag: TAG,
64                reason: "service_provider_name_length runs past descriptor end",
65            });
66        }
67        let provider_name = DvbText::new(&bytes[HEADER_LEN + 2..provider_end]);
68        let service_len = bytes[provider_end] as usize;
69        let service_end = provider_end + 1 + service_len;
70        if service_end > end {
71            return Err(Error::InvalidDescriptor {
72                tag: TAG,
73                reason: "service_name_length runs past descriptor end",
74            });
75        }
76        let service_name = DvbText::new(&bytes[provider_end + 1..service_end]);
77        Ok(Self {
78            service_type,
79            provider_name,
80            service_name,
81        })
82    }
83}
84
85impl Serialize for ServiceDescriptor<'_> {
86    type Error = crate::error::Error;
87    fn serialized_len(&self) -> usize {
88        HEADER_LEN + 1 + 1 + self.provider_name.len() + 1 + self.service_name.len()
89    }
90
91    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
92        let len = self.serialized_len();
93        if buf.len() < len {
94            return Err(Error::OutputBufferTooSmall {
95                need: len,
96                have: buf.len(),
97            });
98        }
99        buf[0] = TAG;
100        buf[1] = (len - HEADER_LEN) as u8;
101        buf[2] = self.service_type;
102        buf[3] = self.provider_name.len() as u8;
103        let p_start = 4;
104        let p_end = p_start + self.provider_name.len();
105        buf[p_start..p_end].copy_from_slice(self.provider_name.raw());
106        buf[p_end] = self.service_name.len() as u8;
107        let s_start = p_end + 1;
108        buf[s_start..s_start + self.service_name.len()].copy_from_slice(self.service_name.raw());
109        Ok(len)
110    }
111}
112
113impl<'a> Descriptor<'a> for ServiceDescriptor<'a> {
114    const TAG: u8 = TAG;
115    fn descriptor_length(&self) -> u8 {
116        (self.serialized_len() - HEADER_LEN) as u8
117    }
118}
119
120impl<'a> crate::traits::DescriptorDef<'a> for ServiceDescriptor<'a> {
121    const TAG: u8 = TAG;
122    const NAME: &'static str = "SERVICE";
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn parse_extracts_all_fields() {
131        // service_type=1, provider="EUTE", service="TF1"
132        let bytes = [
133            TAG, 10, 0x01, 4, b'E', b'U', b'T', b'E', 3, b'T', b'F', b'1',
134        ];
135        let d = ServiceDescriptor::parse(&bytes).unwrap();
136        assert_eq!(d.service_type, 1);
137        assert_eq!(d.provider_name.raw(), b"EUTE");
138        assert_eq!(d.service_name.raw(), b"TF1");
139    }
140
141    #[test]
142    fn parse_rejects_wrong_tag() {
143        let err = ServiceDescriptor::parse(&[0x49, 0]).unwrap_err();
144        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x49, .. }));
145    }
146
147    #[test]
148    fn parse_rejects_short_header() {
149        let err = ServiceDescriptor::parse(&[TAG]).unwrap_err();
150        assert!(matches!(err, Error::BufferTooShort { .. }));
151    }
152
153    #[test]
154    fn parse_rejects_truncated_body() {
155        let err = ServiceDescriptor::parse(&[TAG, 5, 0x01, 0xFF]).unwrap_err();
156        assert!(matches!(err, Error::BufferTooShort { .. }));
157    }
158
159    #[test]
160    fn parse_rejects_provider_length_overrun() {
161        // provider_len says 100 but descriptor body only 5 bytes.
162        let bytes = [TAG, 5, 0x01, 100, b'A', b'B', b'C'];
163        let err = ServiceDescriptor::parse(&bytes).unwrap_err();
164        assert!(matches!(err, Error::InvalidDescriptor { .. }));
165    }
166
167    #[test]
168    fn empty_provider_and_service_names_valid() {
169        let bytes = [TAG, 3, 0x01, 0, 0];
170        let d = ServiceDescriptor::parse(&bytes).unwrap();
171        assert!(d.provider_name.raw().is_empty());
172        assert!(d.service_name.raw().is_empty());
173    }
174
175    #[test]
176    fn serialize_round_trip() {
177        let d = ServiceDescriptor {
178            service_type: 0x19,
179            provider_name: DvbText::new(b"BBC"),
180            service_name: DvbText::new(b"BBC ONE HD"),
181        };
182        let mut buf = vec![0u8; d.serialized_len()];
183        d.serialize_into(&mut buf).unwrap();
184        let re = ServiceDescriptor::parse(&buf).unwrap();
185        assert_eq!(d, re);
186    }
187
188    #[test]
189    fn descriptor_length_matches_payload() {
190        let d = ServiceDescriptor {
191            service_type: 1,
192            provider_name: DvbText::new(b"AA"),
193            service_name: DvbText::new(b"BBB"),
194        };
195        // 1 (type) + 1 (p_len) + 2 (p) + 1 (s_len) + 3 (s) = 8
196        assert_eq!(d.descriptor_length(), 8);
197    }
198}