use deku::prelude::*;
pub const SKIPPABLE_FRAME_MAGIC: &[u8] = b"\x2A\x4D\x18";
pub const ZSTANDARD_FRAME_MAGIC: &[u8] = b"\x28\xB5\x2F\xFD";
pub const SKIPPABLE_FRAME_OVERHEAD: usize = 8;
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct SkippableFrame {
#[deku(bytes = "4", assert = "SkippableFrame::valid_magic(magic)")]
magic: u32,
#[deku(bytes = "4")]
size: u32,
#[deku(count = "size")]
pub data: Vec<u8>,
}
impl SkippableFrame {
fn valid_magic(magic: &u32) -> bool {
let magic_bytes = magic.to_le_bytes();
magic_bytes[0] >= 0x50
&& magic_bytes[0] <= 0x5F
&& &magic_bytes[1..4] == SKIPPABLE_FRAME_MAGIC
}
pub fn new(nibble: u8, data: Vec<u8>) -> Self {
assert!(
nibble < 16,
"skippable frame nibble must be between 0 and 15"
);
Self {
magic: u32::from_le_bytes([0x50 + nibble, 0x2A, 0x4D, 0x18]),
size: data
.len()
.try_into()
.expect("skippable frame data is too long"),
data,
}
}
pub fn nibble(&self) -> u8 {
(self.magic.to_le_bytes()[0] - 0x50) & 0x0F
}
pub fn size(&self) -> usize {
self.size as usize
}
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "little")]
pub struct ZstandardFrame {
pub header: ZstandardFrameHeader,
#[deku(until = "|b: &ZstandardBlock| b.header.last")]
pub blocks: Vec<ZstandardBlock>,
#[deku(bytes = 4, cond = "header.frame_descriptor.checksum")]
pub checksum: Option<u32>,
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(
magic = b"\x28\xB5\x2F\xFD",
endian = "endian",
ctx = "endian: deku::ctx::Endian",
ctx_default = "deku::ctx::Endian::Little"
)]
pub struct ZstandardFrameHeader {
pub frame_descriptor: ZstandardFrameDescriptor,
#[deku(bytes = 1, cond = "!frame_descriptor.single_segment")]
pub window_descriptor: Option<u8>,
#[deku(count = "frame_descriptor.did_length()")]
pub did: Vec<u8>,
#[deku(count = "frame_descriptor.fcs_length()")]
pub frame_content_size: Vec<u8>,
}
impl ZstandardFrameHeader {
pub fn uncompressed_size(&self) -> u64 {
match self.frame_descriptor.fcs_length() {
0 => 0,
1 => u64::from(self.frame_content_size[0]),
2 => {
u64::from(u16::from_le_bytes([
self.frame_content_size[0],
self.frame_content_size[1],
])) + 256
}
4 => u64::from(u32::from_le_bytes([
self.frame_content_size[0],
self.frame_content_size[1],
self.frame_content_size[2],
self.frame_content_size[3],
])),
8 => u64::from_le_bytes([
self.frame_content_size[0],
self.frame_content_size[1],
self.frame_content_size[2],
self.frame_content_size[3],
self.frame_content_size[4],
self.frame_content_size[5],
self.frame_content_size[6],
self.frame_content_size[7],
]),
_ => unreachable!(),
}
}
pub fn dictionary_id(&self) -> u32 {
self.did.iter().fold(0, |acc, &x| acc << 8 | x as u32)
}
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(endian = "endian", ctx = "endian: deku::ctx::Endian")]
pub struct ZstandardFrameDescriptor {
#[deku(bits = 2)]
pub fcs_size: u8,
#[deku(bits = 1)]
pub single_segment: bool,
#[deku(bits = 1)]
pub unused_bit: bool,
#[deku(bits = 1)]
pub reserved_bit: bool,
#[deku(bits = 1)]
pub checksum: bool,
#[deku(bits = 2)]
pub did_size: u8,
}
impl ZstandardFrameDescriptor {
pub fn did_length(&self) -> usize {
match self.did_size {
0 => 0,
1 => 1,
2 => 2,
3 => 4,
_ => unreachable!(),
}
}
pub fn fcs_length(&self) -> usize {
match self.fcs_size {
0 if self.single_segment => 1,
0 => 0,
1 => 2,
2 => 4,
3 => 8,
_ => unreachable!(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(
endian = "endian",
ctx = "endian: deku::ctx::Endian",
ctx_default = "deku::ctx::Endian::Little"
)]
pub struct ZstandardBlock {
pub header: ZstandardBlockHeader,
#[deku(count = "header.actual_size()")]
pub data: Vec<u8>,
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(
endian = "endian",
ctx = "endian: deku::ctx::Endian",
ctx_default = "deku::ctx::Endian::Little"
)]
pub struct ZstandardBlockHeader {
#[deku(bits = "5")]
size_low: u8,
pub block_type: ZstandardBlockType,
#[deku(bits = "1")]
pub last: bool,
#[deku(bits = "16")]
size_high: u16,
}
impl ZstandardBlockHeader {
pub fn new(block_type: ZstandardBlockType, last: bool, size: u32) -> Self {
assert!(size < 2_u32.pow(24));
let [a, b, c, d] = u32::to_be_bytes(size << 3);
let size_high = u16::from_be_bytes([b, c]);
let size_low = d >> 3;
tracing::trace!(
field = %format!("{a:08b} {b:08b} {c:08b} {d:08b}"),
high = %format!("{size_high:016b}"),
low = %format!("{size_low:08b}"),
"block header size bit wrangling (write)"
);
Self {
size_low,
block_type,
last,
size_high,
}
}
fn size(&self) -> u32 {
let [a, b] = u16::to_be_bytes(self.size_high);
let c = self.size_low << 3;
let real_size = u32::from_be_bytes([0, a, b, c]) >> 3;
tracing::trace!(
high = %format!("{:016b}", self.size_high),
low = %format!("{:08b}", self.size_low),
real_dec = %real_size,
real_hex = %format!("{real_size:02x?}"),
"block header size bit wrangling (read)"
);
real_size
}
pub fn rle_count(&self) -> Option<u32> {
if self.block_type == ZstandardBlockType::Rle {
Some(self.size())
} else {
None
}
}
pub fn actual_size(&self) -> u32 {
match self.block_type {
ZstandardBlockType::Raw | ZstandardBlockType::Compressed => self.size(),
ZstandardBlockType::Rle => 1,
ZstandardBlockType::Reserved => panic!("corrupt zstd: reserved block type"),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, DekuRead, DekuWrite)]
#[deku(
endian = "endian",
ctx = "endian: deku::ctx::Endian",
type = "u8",
bits = "2"
)]
pub enum ZstandardBlockType {
#[deku(id = "0b00")] Raw,
#[deku(id = "0b01")] Rle,
#[deku(id = "0b10")] Compressed,
#[deku(id = "0b11")] Reserved,
}