use crate::error::{Error, Result};
use crate::ext::HeaderExtension;
pub const HET_EXT_FDT: u8 = 192;
pub const HET_EXT_CENC: u8 = 193;
pub const TOI_FDT: u32 = 0;
pub const FLUTE_VERSION: u8 = 2;
pub const FDT_INSTANCE_ID_MAX: u32 = (1 << 20) - 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ExtFdt {
pub version: u8,
pub instance_id: u32,
}
impl ExtFdt {
pub fn parse(content: &[u8]) -> Result<Self> {
if content.len() != 3 {
return Err(Error::BufferTooShort {
need: 3,
have: content.len(),
what: "EXT_FDT content",
});
}
let v = content[0] >> 4;
let instance_id =
((content[0] as u32 & 0x0F) << 16) | ((content[1] as u32) << 8) | content[2] as u32;
Ok(ExtFdt {
version: v,
instance_id,
})
}
pub fn to_content(&self) -> Result<[u8; 3]> {
if self.version > 0x0F {
return Err(Error::FieldTooWide {
what: "EXT_FDT V",
value: self.version as u64,
bits: 4,
});
}
if self.instance_id > FDT_INSTANCE_ID_MAX {
return Err(Error::FieldTooWide {
what: "FDT Instance ID",
value: self.instance_id as u64,
bits: 20,
});
}
Ok([
(self.version << 4) | ((self.instance_id >> 16) as u8 & 0x0F),
(self.instance_id >> 8) as u8,
self.instance_id as u8,
])
}
pub fn to_extension<'a>(&self, scratch: &'a mut [u8; 3]) -> Result<HeaderExtension<'a>> {
*scratch = self.to_content()?;
Ok(HeaderExtension::new(HET_EXT_FDT, &scratch[..]))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum CencAlgorithm {
Null,
Zlib,
Deflate,
Gzip,
Other(u8),
}
impl CencAlgorithm {
pub fn from_u8(v: u8) -> Self {
match v {
0 => CencAlgorithm::Null,
1 => CencAlgorithm::Zlib,
2 => CencAlgorithm::Deflate,
3 => CencAlgorithm::Gzip,
other => CencAlgorithm::Other(other),
}
}
pub fn to_u8(self) -> u8 {
match self {
CencAlgorithm::Null => 0,
CencAlgorithm::Zlib => 1,
CencAlgorithm::Deflate => 2,
CencAlgorithm::Gzip => 3,
CencAlgorithm::Other(v) => v,
}
}
pub fn name(&self) -> &'static str {
match self {
CencAlgorithm::Null => "null",
CencAlgorithm::Zlib => "ZLIB",
CencAlgorithm::Deflate => "DEFLATE",
CencAlgorithm::Gzip => "GZIP",
CencAlgorithm::Other(_) => "reserved",
}
}
}
broadcast_common::impl_spec_display!(CencAlgorithm, Other);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ExtCenc {
pub algorithm: CencAlgorithm,
}
impl ExtCenc {
pub fn parse(content: &[u8]) -> Result<Self> {
if content.len() != 3 {
return Err(Error::BufferTooShort {
need: 3,
have: content.len(),
what: "EXT_CENC content",
});
}
Ok(ExtCenc {
algorithm: CencAlgorithm::from_u8(content[0]),
})
}
pub fn to_content(&self) -> [u8; 3] {
[self.algorithm.to_u8(), 0, 0]
}
pub fn to_extension<'a>(&self, scratch: &'a mut [u8; 3]) -> HeaderExtension<'a> {
*scratch = self.to_content();
HeaderExtension::new(HET_EXT_CENC, &scratch[..])
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn ext_fdt_round_trip() {
let f = ExtFdt {
version: FLUTE_VERSION,
instance_id: 0x0_ABCD,
};
let c = f.to_content().unwrap();
assert_eq!(c, [0x20, 0xAB, 0xCD]);
assert_eq!(ExtFdt::parse(&c).unwrap(), f);
let mut scratch = [0u8; 3];
let ext = f.to_extension(&mut scratch).unwrap();
assert_eq!(ext.het, HET_EXT_FDT);
assert!(ext.is_fixed());
assert_eq!(ext.serialized_len(), 4);
}
#[test]
fn ext_fdt_max_instance_id() {
let f = ExtFdt {
version: 2,
instance_id: FDT_INSTANCE_ID_MAX,
};
let c = f.to_content().unwrap();
assert_eq!(c, [0x2F, 0xFF, 0xFF]);
assert_eq!(ExtFdt::parse(&c).unwrap(), f);
}
#[test]
fn ext_fdt_rejects_overwide_instance_id() {
let f = ExtFdt {
version: 2,
instance_id: FDT_INSTANCE_ID_MAX + 1,
};
assert!(matches!(f.to_content(), Err(Error::FieldTooWide { .. })));
}
#[test]
fn ext_cenc_round_trip() {
for algo in [
CencAlgorithm::Null,
CencAlgorithm::Zlib,
CencAlgorithm::Deflate,
CencAlgorithm::Gzip,
CencAlgorithm::Other(7),
] {
let e = ExtCenc { algorithm: algo };
let c = e.to_content();
assert_eq!(c[1..], [0, 0]);
assert_eq!(ExtCenc::parse(&c).unwrap(), e);
}
assert_eq!(CencAlgorithm::Gzip.to_string(), "GZIP");
assert_eq!(CencAlgorithm::Other(7).to_string(), "reserved(0x07)");
}
}