Skip to main content

st377_1/
primer.rs

1//! Primer Pack — SMPTE ST 377-1:2019 §9.2, Tables 13-15 (`docs/st377-1.md`):
2//! the per-Partition lookup table mapping every 2-byte local tag used in
3//! this Partition's Header Metadata to its full UL/UUID.
4
5extern crate alloc;
6
7use alloc::vec::Vec;
8
9use broadcast_common::{Parse, Serialize};
10
11use crate::ber::{ber_length_size, decode_ber_length, encode_ber_length};
12use crate::error::{Error, Result};
13use crate::types::{UlBytes, ul_bytes_from_prefix};
14
15/// Fixed bytes 1-13 of the Primer Pack Key (Table 13), i.e. everything
16/// except byte 8 (registry version, wildcard on parse).
17const PRIMER_KEY_PREFIX: [u8; 7] = [0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01];
18const PRIMER_KEY_MID: [u8; 4] = [0x0D, 0x01, 0x02, 0x01];
19/// Byte 14 (Set/Pack Kind = Primer Pack) and byte 15 (Primer version).
20const PRIMER_KEY_TAIL: [u8; 2] = [0x05, 0x01];
21
22/// The size in bytes of one `LocalTagEntry` (Table 15): a 2-byte tag plus a
23/// 16-byte AUID.
24const LOCAL_TAG_ENTRY_LEN: u32 = 18;
25
26/// The Primer Pack — SMPTE ST 377-1:2019 §9.2, Tables 13-15: a Batch of
27/// `{local_tag: u16, uid: AUID}` entries, scoped to the single Partition
28/// that contains it (§9.2 — never accumulated across Partitions).
29#[derive(Debug, Clone, PartialEq, Eq, Default)]
30pub struct PrimerPack {
31    /// Every local-tag -> UL/UUID mapping in this Partition's Header
32    /// Metadata.
33    pub entries: Vec<(u16, UlBytes)>,
34}
35
36impl PrimerPack {
37    /// Build the 16-byte Primer Pack Key (Table 13).
38    #[must_use]
39    pub fn key() -> UlBytes {
40        let mut key = [0u8; 16];
41        key[0..7].copy_from_slice(&PRIMER_KEY_PREFIX);
42        key[7] = 0x01; // registry version
43        key[8..12].copy_from_slice(&PRIMER_KEY_MID);
44        key[12] = 0x01; // Structure Kind
45        key[13..15].copy_from_slice(&PRIMER_KEY_TAIL);
46        key[15] = 0x00; // reserved
47        key
48    }
49
50    /// True if `key` is the Primer Pack Key (Table 13), ignoring byte 8
51    /// (registry version, wildcard).
52    #[must_use]
53    pub fn is_primer_key(key: &UlBytes) -> bool {
54        key[0..7] == PRIMER_KEY_PREFIX
55            && key[8..12] == PRIMER_KEY_MID
56            && key[12] == 0x01
57            && key[13..15] == PRIMER_KEY_TAIL
58    }
59
60    fn check_key(key: &UlBytes) -> Result<()> {
61        if Self::is_primer_key(key) {
62            Ok(())
63        } else {
64            Err(Error::KeyPrefixMismatch {
65                what: "Primer Pack (Table 13)",
66            })
67        }
68    }
69
70    /// Resolve a UL/UUID to its local tag in this Primer Pack, if present.
71    /// Used to decode "dyn" (dynamically-allocated-tag) properties whose
72    /// static tag the spec deliberately does not fix (`docs/st377-1.md`'s
73    /// Annex A tables) — e.g. `Preface`'s `IsRIPPresent`.
74    #[must_use]
75    pub fn resolve_ul(&self, ul: &UlBytes) -> Option<u16> {
76        self.entries.iter().find(|(_, u)| u == ul).map(|(t, _)| *t)
77    }
78
79    /// Look up the UL/UUID for a local tag, if present.
80    #[must_use]
81    pub fn resolve_tag(&self, tag: u16) -> Option<UlBytes> {
82        self.entries
83            .iter()
84            .find(|(t, _)| *t == tag)
85            .map(|(_, u)| *u)
86    }
87
88    /// Parse a Primer Pack (Key + Length + Value) from `bytes`, validating
89    /// the Key and consuming exactly one KLV item's worth. Also usable in a
90    /// stream context via the returned consumed-byte count.
91    pub fn parse_prefix(bytes: &[u8]) -> Result<(Self, usize)> {
92        if bytes.len() < 16 {
93            return Err(Error::BufferTooShort {
94                need: 16,
95                have: bytes.len(),
96                what: "Primer Pack key",
97            });
98        }
99        let key: UlBytes = ul_bytes_from_prefix(bytes);
100        Self::check_key(&key)?;
101
102        let (len, len_size) = decode_ber_length(&bytes[16..])?;
103        let value_start = 16 + len_size;
104        let len = len as usize;
105        let value_end = value_start.checked_add(len).ok_or(Error::BufferTooShort {
106            need: usize::MAX,
107            have: bytes.len(),
108            what: "Primer Pack value (length overflow)",
109        })?;
110        if bytes.len() < value_end {
111            return Err(Error::BufferTooShort {
112                need: value_end,
113                have: bytes.len(),
114                what: "Primer Pack value",
115            });
116        }
117        let v = &bytes[value_start..value_end];
118        if v.len() < 8 {
119            return Err(Error::InvalidBatchHeader {
120                count: 0,
121                item_len: 0,
122                buffer_len: v.len(),
123            });
124        }
125        let count = u32::from_be_bytes([v[0], v[1], v[2], v[3]]);
126        let item_len = u32::from_be_bytes([v[4], v[5], v[6], v[7]]);
127        let body = &v[8..];
128        if item_len != LOCAL_TAG_ENTRY_LEN || body.len() != count as usize * 18 {
129            return Err(Error::InvalidBatchHeader {
130                count,
131                item_len,
132                buffer_len: body.len(),
133            });
134        }
135        let mut entries = Vec::with_capacity(count as usize);
136        for chunk in body.chunks_exact(18) {
137            let tag = u16::from_be_bytes([chunk[0], chunk[1]]);
138            let uid: UlBytes = ul_bytes_from_prefix(&chunk[2..]);
139            entries.push((tag, uid));
140        }
141        Ok((PrimerPack { entries }, value_end))
142    }
143}
144
145impl<'a> Parse<'a> for PrimerPack {
146    type Error = Error;
147
148    fn parse(bytes: &'a [u8]) -> Result<Self> {
149        let (pack, consumed) = Self::parse_prefix(bytes)?;
150        if consumed != bytes.len() {
151            return Err(Error::BufferTooShort {
152                need: consumed,
153                have: bytes.len(),
154                what: "Primer Pack (trailing bytes after exact-fit parse)",
155            });
156        }
157        Ok(pack)
158    }
159}
160
161impl Serialize for PrimerPack {
162    type Error = Error;
163
164    fn serialized_len(&self) -> usize {
165        let value_len = 8 + self.entries.len() * 18;
166        16 + ber_length_size(value_len as u64) + value_len
167    }
168
169    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
170        let total = self.serialized_len();
171        if buf.len() < total {
172            return Err(Error::BufferTooShort {
173                need: total,
174                have: buf.len(),
175                what: "Primer Pack",
176            });
177        }
178        buf[0..16].copy_from_slice(&Self::key());
179        let value_len = 8 + self.entries.len() * 18;
180        let len_size = encode_ber_length(value_len as u64, &mut buf[16..])?;
181        let mut pos = 16 + len_size;
182        buf[pos..pos + 4].copy_from_slice(&(self.entries.len() as u32).to_be_bytes());
183        pos += 4;
184        buf[pos..pos + 4].copy_from_slice(&LOCAL_TAG_ENTRY_LEN.to_be_bytes());
185        pos += 4;
186        for (tag, uid) in &self.entries {
187            buf[pos..pos + 2].copy_from_slice(&tag.to_be_bytes());
188            pos += 2;
189            buf[pos..pos + 16].copy_from_slice(uid);
190            pos += 16;
191        }
192        Ok(pos)
193    }
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn primer_pack_round_trip() {
202        let pack = PrimerPack {
203            entries: alloc::vec![(0x3B02, [0xAAu8; 16]), (0x3B05, [0xBBu8; 16])],
204        };
205        let mut buf = alloc::vec![0u8; pack.serialized_len()];
206        pack.serialize_into(&mut buf).unwrap();
207        let parsed = PrimerPack::parse(&buf).unwrap();
208        assert_eq!(parsed, pack);
209        assert_eq!(parsed.resolve_tag(0x3B02), Some([0xAAu8; 16]));
210        assert_eq!(parsed.resolve_ul(&[0xBBu8; 16]), Some(0x3B05));
211        assert_eq!(parsed.resolve_tag(0x9999), None);
212    }
213
214    #[test]
215    fn empty_primer_pack_round_trip() {
216        let pack = PrimerPack::default();
217        let mut buf = alloc::vec![0u8; pack.serialized_len()];
218        pack.serialize_into(&mut buf).unwrap();
219        assert_eq!(PrimerPack::parse(&buf).unwrap(), pack);
220    }
221
222    #[test]
223    fn wrong_key_rejected() {
224        let mut bytes = alloc::vec![0u8; 17];
225        bytes[16] = 0; // zero-length value
226        assert!(matches!(
227            PrimerPack::parse(&bytes),
228            Err(Error::KeyPrefixMismatch { .. })
229        ));
230    }
231}