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