use serde::Serialize;
use crate::tools::error::{FluteError, Result};
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Debug, Serialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum Cenc {
Null = 0,
Zlib = 1,
Deflate = 2,
Gzip = 3,
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum Ext {
Fdt = 192,
Fti = 64,
Cenc = 193,
Time = 2,
}
pub const TOI_FDT: u128 = 0;
#[derive(Clone, Debug)]
pub struct LCTHeader {
pub len: usize,
pub cci: u128,
pub tsi: u64,
pub toi: u128,
pub cp: u8,
pub close_object: bool,
pub close_session: bool,
pub header_ext_offset: u32,
pub length: usize,
}
impl TryFrom<u8> for Cenc {
type Error = ();
fn try_from(v: u8) -> std::result::Result<Self, Self::Error> {
match v {
x if x == Cenc::Null as u8 => Ok(Cenc::Null),
x if x == Cenc::Zlib as u8 => Ok(Cenc::Zlib),
x if x == Cenc::Deflate as u8 => Ok(Cenc::Deflate),
x if x == Cenc::Gzip as u8 => Ok(Cenc::Gzip),
_ => Err(()),
}
}
}
impl TryFrom<&str> for Cenc {
type Error = ();
fn try_from(v: &str) -> std::result::Result<Self, Self::Error> {
match v {
"null" => Ok(Cenc::Null),
"zlib" => Ok(Cenc::Zlib),
"deflate" => Ok(Cenc::Deflate),
"gzip" => Ok(Cenc::Gzip),
_ => Err(()),
}
}
}
impl Cenc {
pub fn to_str(&self) -> &str {
match self {
Cenc::Null => "null",
Cenc::Zlib => "zlib",
Cenc::Deflate => "deflate",
Cenc::Gzip => "gzip",
}
}
}
fn nb_bytes_128(cci: &u128, min: u32) -> u32 {
if (cci & 0xFFFF0000000000000000000000000000) != 0x0 {
return 16;
}
if (cci & 0xFFFF000000000000000000000000) != 0x0 {
return 14;
}
if (cci & 0xFFFF00000000000000000000) != 0x0 {
return 12;
}
if (cci & 0xFFFF0000000000000000) != 0x0 {
return 10;
}
if (cci & 0xFFFF000000000000) != 0x0 {
return 8;
}
if (cci & 0xFFFF00000000) != 0x0 {
return 6;
}
if (cci & 0xFFFF0000) != 0x0 {
return 4;
}
if (cci & 0xFFFF) != 0x0 {
return 2;
}
min
}
fn nb_bytes_64(n: u64, min: u32) -> u32 {
if (n & 0xFFFF000000000000) != 0x0 {
return 8;
}
if (n & 0xFFFF00000000) != 0x0 {
return 6;
}
if (n & 0xFFFF0000) != 0x0 {
return 4;
}
if (n & 0xFFFF) != 0x0 {
return 2;
}
min
}
pub fn push_lct_header(
data: &mut Vec<u8>,
psi: u8,
cci: &u128,
tsi: u64,
toi: &u128,
codepoint: u8,
close_object: bool,
close_session: bool,
) {
let cci_size = nb_bytes_128(cci, 0);
let tsi_size = nb_bytes_64(tsi, 2);
let toi_size = nb_bytes_128(toi, 2);
let h_tsi = (tsi_size & 2) >> 1; let h_toi = (toi_size & 2) >> 1;
let h = h_tsi | h_toi; let b: u8 = match close_object {
true => 1,
false => 0,
};
let a: u8 = match close_session {
true => 1,
false => 0,
};
let o = (toi_size >> 2) & 0x3;
let s = (tsi_size >> 2) & 1;
let c = match cci_size {
size if size <= 4 => 0,
size if size <= 8 => 1,
size if size <= 12 => 2,
_ => 3,
};
let hdr_len: u8 = (2 + o + s + h + c) as u8;
let v = 1;
let lct_header: u32 = (codepoint as u32)
| ((hdr_len as u32) << 8)
| (b as u32) << 16
| (a as u32) << 17
| (h) << 20
| (o) << 21
| (s) << 23
| (psi as u32) << 24
| (c) << 26
| (v as u32) << 28;
data.extend(lct_header.to_be_bytes());
let cci_net = cci.to_be_bytes();
let cci_net_start: usize = cci_net.len() - ((c + 1) << 2) as usize;
data.extend(&cci_net[cci_net_start..]);
let tsi_net = tsi.to_be_bytes();
let tsi_net_start = tsi_net.len() - ((s << 2) + (h << 1)) as usize;
data.extend(&tsi_net[tsi_net_start..]);
let toi_net = toi.to_be_bytes();
let toi_net_start = toi_net.len() - ((o << 2) + (h << 1)) as usize;
data.extend(&toi_net[toi_net_start..]);
}
pub fn inc_hdr_len(data: &mut [u8], val: u8) {
data[2] += val;
}
pub fn parse_lct_header(data: &[u8]) -> Result<LCTHeader> {
let len = data.get(2).map_or_else(
|| Err(FluteError::new("Fail to read lct header size")),
|&v| Ok((v as usize) << 2),
)?;
if len > data.len() {
return Err(FluteError::new(format!(
"lct header size is {} whereas pkt size is {}",
len,
data.len()
)));
}
let cp = data[3];
let flags1 = data[0];
let flags2 = data[1];
let s = (flags2 >> 7) & 0x1;
let o = (flags2 >> 5) & 0x3;
let h = (flags2 >> 4) & 0x1;
let c = (flags1 >> 2) & 0x3;
let a = (flags2 >> 1) & 0x1;
let b = flags2 & 0x1;
let version = flags1 >> 4;
if version != 1 && version != 2 {
return Err(FluteError::new(format!(
"FLUTE version {} is not supported",
version
)));
}
let cci_len = ((c + 1) as u32) << 2;
let tsi_len = ((s as u32) << 2) + ((h as u32) << 1);
let toi_len = ((o as u32) << 2) + ((h as u32) << 1);
let cci_from: usize = 4;
let cci_to: usize = (4 + cci_len) as usize;
let tsi_to: usize = cci_to + tsi_len as usize;
let toi_to: usize = tsi_to + toi_len as usize;
let header_ext_offset = toi_to as u32;
if toi_to > data.len() || cci_len > 16 || tsi_len > 8 || toi_len > 16 {
return Err(FluteError::new(format!(
"toi ends to offset {} whereas pkt size is {}",
toi_to,
data.len()
)));
}
if header_ext_offset > len as u32 {
return Err(FluteError::new("EXT offset outside LCT header"));
}
let mut cci: [u8; 16] = [0; 16]; let mut tsi: [u8; 8] = [0; 8]; let mut toi: [u8; 16] = [0; 16];
let _ = &cci[(16 - cci_len) as usize..].copy_from_slice(&data[cci_from..cci_to]);
let _ = &tsi[(8 - tsi_len) as usize..].copy_from_slice(&data[cci_to..tsi_to]);
let _ = &toi[(16 - toi_len) as usize..].copy_from_slice(&data[tsi_to..toi_to]);
let cci = u128::from_be_bytes(cci);
let tsi = u64::from_be_bytes(tsi);
let toi = u128::from_be_bytes(toi);
Ok(LCTHeader {
len,
cci,
tsi,
toi,
cp,
close_object: b != 0,
close_session: a != 0,
header_ext_offset,
length: len,
})
}
pub fn get_ext<'a>(data: &'a [u8], lct: &LCTHeader, ext: u8) -> Result<Option<&'a [u8]>> {
let mut lct_ext_ext = &data[(lct.header_ext_offset as usize)..lct.len];
while lct_ext_ext.len() >= 4 {
let het = lct_ext_ext[0];
let hel = match het {
het if het >= 128 => 4_usize,
_ => (lct_ext_ext[1] << 2) as usize,
};
if hel == 0 || hel > lct_ext_ext.len() {
return Err(FluteError::new(format!(
"Fail, LCT EXT size is {}/{} het={} offset={}",
hel,
lct_ext_ext.len(),
het,
lct.header_ext_offset
)));
}
if het == ext {
return Ok(Some(&lct_ext_ext[..hel]));
}
lct_ext_ext = &lct_ext_ext[hel..];
}
Ok(None)
}
#[cfg(test)]
mod tests {
#[test]
pub fn test_lct() {
crate::tests::init();
let mut lct = Vec::new();
let psi: u8 = 0;
let cci: u128 = 0x1;
let tsi: u64 = 0;
let toi: u128 = 0;
let codepoint: u8 = 0;
super::push_lct_header(&mut lct, psi, &cci, tsi, &toi, codepoint, false, false)
}
}