Skip to main content

dvb_si/descriptors/
service_list.rs

1//! Service List Descriptor — ETSI EN 300 468 §6.2.36 (tag 0x41).
2//!
3//! Carried inside NIT/BAT transport_stream_loop second descriptor loop.
4//! Enumerates the service_id/service_type pairs available on the TS.
5
6use super::descriptor_body;
7use crate::error::{Error, Result};
8use dvb_common::{Parse, Serialize};
9
10/// Descriptor tag for service_list_descriptor.
11pub const TAG: u8 = 0x41;
12const HEADER_LEN: usize = 2;
13const ENTRY_LEN: usize = 3;
14
15/// One (service_id, service_type) pair.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18pub struct ServiceListEntry {
19    /// DVB service_id (matches program_number in PAT).
20    pub service_id: u16,
21    /// service_type byte (ETSI Table 87).
22    pub service_type: u8,
23}
24
25/// Service List Descriptor.
26#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize))]
28pub struct ServiceListDescriptor {
29    /// Entries in wire order.
30    pub entries: Vec<ServiceListEntry>,
31}
32
33impl<'a> Parse<'a> for ServiceListDescriptor {
34    type Error = crate::error::Error;
35    fn parse(bytes: &'a [u8]) -> Result<Self> {
36        let body = descriptor_body(
37            bytes,
38            TAG,
39            "ServiceListDescriptor",
40            "unexpected tag for service_list_descriptor",
41        )?;
42        if body.len() % 3 != 0 {
43            return Err(Error::InvalidDescriptor {
44                tag: TAG,
45                reason: "descriptor_length must be a multiple of 3",
46            });
47        }
48        let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
49        let mut offset = 0;
50        while offset < body.len() {
51            let service_id = u16::from_be_bytes([body[offset], body[offset + 1]]);
52            let service_type = body[offset + 2];
53            entries.push(ServiceListEntry {
54                service_id,
55                service_type,
56            });
57            offset += ENTRY_LEN;
58        }
59        Ok(Self { entries })
60    }
61}
62
63impl Serialize for ServiceListDescriptor {
64    type Error = crate::error::Error;
65    fn serialized_len(&self) -> usize {
66        HEADER_LEN + ENTRY_LEN * self.entries.len()
67    }
68
69    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
70        let len = self.serialized_len();
71        if buf.len() < len {
72            return Err(Error::OutputBufferTooSmall {
73                need: len,
74                have: buf.len(),
75            });
76        }
77        buf[0] = TAG;
78        buf[1] = (len - HEADER_LEN) as u8;
79        let mut offset = HEADER_LEN;
80        for entry in &self.entries {
81            buf[offset..offset + 2].copy_from_slice(&entry.service_id.to_be_bytes());
82            buf[offset + 2] = entry.service_type;
83            offset += ENTRY_LEN;
84        }
85        Ok(len)
86    }
87}
88impl<'a> crate::traits::DescriptorDef<'a> for ServiceListDescriptor {
89    const TAG: u8 = TAG;
90    const NAME: &'static str = "SERVICE_LIST";
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn parse_single_entry() {
99        let bytes = [TAG, 3, 0x00, 0x01, 0x01];
100        let d = ServiceListDescriptor::parse(&bytes).unwrap();
101        assert_eq!(d.entries.len(), 1);
102        assert_eq!(d.entries[0].service_id, 1);
103        assert_eq!(d.entries[0].service_type, 0x01);
104    }
105
106    #[test]
107    fn parse_multiple_entries_preserves_order() {
108        let bytes = [TAG, 9, 0x00, 0x01, 0x01, 0x00, 0x02, 0x02, 0x00, 0x03, 0x03];
109        let d = ServiceListDescriptor::parse(&bytes).unwrap();
110        assert_eq!(d.entries.len(), 3);
111        assert_eq!(d.entries[0].service_id, 1);
112        assert_eq!(d.entries[0].service_type, 0x01);
113        assert_eq!(d.entries[1].service_id, 2);
114        assert_eq!(d.entries[1].service_type, 0x02);
115        assert_eq!(d.entries[2].service_id, 3);
116        assert_eq!(d.entries[2].service_type, 0x03);
117    }
118
119    #[test]
120    fn parse_rejects_wrong_tag() {
121        let err = ServiceListDescriptor::parse(&[0x42, 3, 0x00, 0x01, 0x01]).unwrap_err();
122        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x42, .. }));
123    }
124
125    #[test]
126    fn parse_rejects_length_not_multiple_of_3() {
127        let bytes = [TAG, 4, 0x00, 0x01, 0x01, 0xFF];
128        let err = ServiceListDescriptor::parse(&bytes).unwrap_err();
129        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
130    }
131
132    #[test]
133    fn parse_rejects_truncated_body() {
134        let bytes = [TAG, 6, 0x00, 0x01];
135        let err = ServiceListDescriptor::parse(&bytes).unwrap_err();
136        assert!(matches!(err, Error::BufferTooShort { .. }));
137    }
138
139    #[test]
140    fn empty_descriptor_valid() {
141        let bytes = [TAG, 0];
142        let d = ServiceListDescriptor::parse(&bytes).unwrap();
143        assert!(d.entries.is_empty());
144    }
145
146    #[test]
147    fn serialize_round_trip() {
148        let d = ServiceListDescriptor {
149            entries: vec![
150                ServiceListEntry {
151                    service_id: 1,
152                    service_type: 0x01,
153                },
154                ServiceListEntry {
155                    service_id: 0x0102,
156                    service_type: 0x04,
157                },
158            ],
159        };
160        let mut buf = vec![0u8; d.serialized_len()];
161        d.serialize_into(&mut buf).unwrap();
162        let re = ServiceListDescriptor::parse(&buf).unwrap();
163        assert_eq!(d, re);
164    }
165}