1use crate::error::{Error, Result};
13use crate::ext::HeaderExtension;
14
15pub const HET_EXT_FDT: u8 = 192;
17pub const HET_EXT_CENC: u8 = 193;
19
20pub const TOI_FDT: u32 = 0;
23
24pub const FLUTE_VERSION: u8 = 2;
26
27pub const FDT_INSTANCE_ID_MAX: u32 = (1 << 20) - 1;
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub struct ExtFdt {
36 pub version: u8,
38 pub instance_id: u32,
40}
41
42impl ExtFdt {
43 pub fn parse(content: &[u8]) -> Result<Self> {
46 if content.len() != 3 {
47 return Err(Error::BufferTooShort {
48 need: 3,
49 have: content.len(),
50 what: "EXT_FDT content",
51 });
52 }
53 let v = content[0] >> 4;
55 let instance_id =
56 ((content[0] as u32 & 0x0F) << 16) | ((content[1] as u32) << 8) | content[2] as u32;
57 Ok(ExtFdt {
58 version: v,
59 instance_id,
60 })
61 }
62
63 pub fn to_content(&self) -> Result<[u8; 3]> {
65 if self.version > 0x0F {
66 return Err(Error::FieldTooWide {
67 what: "EXT_FDT V",
68 value: self.version as u64,
69 bits: 4,
70 });
71 }
72 if self.instance_id > FDT_INSTANCE_ID_MAX {
73 return Err(Error::FieldTooWide {
74 what: "FDT Instance ID",
75 value: self.instance_id as u64,
76 bits: 20,
77 });
78 }
79 Ok([
80 (self.version << 4) | ((self.instance_id >> 16) as u8 & 0x0F),
81 (self.instance_id >> 8) as u8,
82 self.instance_id as u8,
83 ])
84 }
85
86 pub fn to_extension<'a>(&self, scratch: &'a mut [u8; 3]) -> Result<HeaderExtension<'a>> {
89 *scratch = self.to_content()?;
90 Ok(HeaderExtension::new(HET_EXT_FDT, &scratch[..]))
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
96#[cfg_attr(feature = "serde", derive(serde::Serialize))]
97#[non_exhaustive]
98pub enum CencAlgorithm {
99 Null,
101 Zlib,
103 Deflate,
105 Gzip,
107 Other(u8),
109}
110
111impl CencAlgorithm {
112 pub fn from_u8(v: u8) -> Self {
114 match v {
115 0 => CencAlgorithm::Null,
116 1 => CencAlgorithm::Zlib,
117 2 => CencAlgorithm::Deflate,
118 3 => CencAlgorithm::Gzip,
119 other => CencAlgorithm::Other(other),
120 }
121 }
122
123 pub fn to_u8(self) -> u8 {
125 match self {
126 CencAlgorithm::Null => 0,
127 CencAlgorithm::Zlib => 1,
128 CencAlgorithm::Deflate => 2,
129 CencAlgorithm::Gzip => 3,
130 CencAlgorithm::Other(v) => v,
131 }
132 }
133
134 pub fn name(&self) -> &'static str {
136 match self {
137 CencAlgorithm::Null => "null",
138 CencAlgorithm::Zlib => "ZLIB",
139 CencAlgorithm::Deflate => "DEFLATE",
140 CencAlgorithm::Gzip => "GZIP",
141 CencAlgorithm::Other(_) => "reserved",
142 }
143 }
144}
145
146broadcast_common::impl_spec_display!(CencAlgorithm, Other);
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153#[cfg_attr(feature = "serde", derive(serde::Serialize))]
154pub struct ExtCenc {
155 pub algorithm: CencAlgorithm,
157}
158
159impl ExtCenc {
160 pub fn parse(content: &[u8]) -> Result<Self> {
163 if content.len() != 3 {
164 return Err(Error::BufferTooShort {
165 need: 3,
166 have: content.len(),
167 what: "EXT_CENC content",
168 });
169 }
170 Ok(ExtCenc {
172 algorithm: CencAlgorithm::from_u8(content[0]),
173 })
174 }
175
176 pub fn to_content(&self) -> [u8; 3] {
178 [self.algorithm.to_u8(), 0, 0]
179 }
180
181 pub fn to_extension<'a>(&self, scratch: &'a mut [u8; 3]) -> HeaderExtension<'a> {
184 *scratch = self.to_content();
185 HeaderExtension::new(HET_EXT_CENC, &scratch[..])
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use alloc::string::ToString;
193
194 #[test]
195 fn ext_fdt_round_trip() {
196 let f = ExtFdt {
197 version: FLUTE_VERSION,
198 instance_id: 0x0_ABCD,
199 };
200 let c = f.to_content().unwrap();
201 assert_eq!(c, [0x20, 0xAB, 0xCD]);
203 assert_eq!(ExtFdt::parse(&c).unwrap(), f);
204
205 let mut scratch = [0u8; 3];
207 let ext = f.to_extension(&mut scratch).unwrap();
208 assert_eq!(ext.het, HET_EXT_FDT);
209 assert!(ext.is_fixed());
210 assert_eq!(ext.serialized_len(), 4);
211 }
212
213 #[test]
214 fn ext_fdt_max_instance_id() {
215 let f = ExtFdt {
216 version: 2,
217 instance_id: FDT_INSTANCE_ID_MAX,
218 };
219 let c = f.to_content().unwrap();
220 assert_eq!(c, [0x2F, 0xFF, 0xFF]);
221 assert_eq!(ExtFdt::parse(&c).unwrap(), f);
222 }
223
224 #[test]
225 fn ext_fdt_rejects_overwide_instance_id() {
226 let f = ExtFdt {
227 version: 2,
228 instance_id: FDT_INSTANCE_ID_MAX + 1,
229 };
230 assert!(matches!(f.to_content(), Err(Error::FieldTooWide { .. })));
231 }
232
233 #[test]
234 fn ext_cenc_round_trip() {
235 for algo in [
236 CencAlgorithm::Null,
237 CencAlgorithm::Zlib,
238 CencAlgorithm::Deflate,
239 CencAlgorithm::Gzip,
240 CencAlgorithm::Other(7),
241 ] {
242 let e = ExtCenc { algorithm: algo };
243 let c = e.to_content();
244 assert_eq!(c[1..], [0, 0]);
245 assert_eq!(ExtCenc::parse(&c).unwrap(), e);
246 }
247 assert_eq!(CencAlgorithm::Gzip.to_string(), "GZIP");
248 assert_eq!(CencAlgorithm::Other(7).to_string(), "reserved(0x07)");
249 }
250}