1use crate::error::{Error, Result};
3use alloc::vec::Vec;
4
5pub const MAGIC: u32 = 0xFD2F_B528;
6pub const SKIPPABLE_MASK: u32 = 0xFFFF_FFF0;
7pub const SKIPPABLE_BASE: u32 = 0x184D_2A50;
8pub const BLOCK_MAX: usize = 128 * 1024;
10pub const WINDOW_LOG_MIN: u32 = 10;
11pub const WINDOW_LOG_MAX: u32 = 31;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum FrameFormat {
19 Standard,
21 Magicless,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct FrameHeader {
28 pub window_size: u64,
30 pub content_size: Option<u64>,
32 pub dict_id: u32,
34 pub checksum: bool,
36 pub single_segment: bool,
38 pub header_len: usize,
40}
41
42fn le(src: &[u8], at: usize, n: usize) -> Result<u64> {
43 let b = src.get(at..at + n).ok_or(Error::Truncated)?;
44 Ok(b.iter().rev().fold(0u64, |acc, &x| (acc << 8) | x as u64))
45}
46
47impl FrameHeader {
48 pub fn parse(src: &[u8]) -> Result<FrameHeader> {
50 Self::parse_format(src, FrameFormat::Standard)
51 }
52
53 pub fn parse_format(src: &[u8], format: FrameFormat) -> Result<FrameHeader> {
55 let start = match format {
56 FrameFormat::Standard => {
57 if le(src, 0, 4)? as u32 != MAGIC {
58 return Err(Error::BadMagic);
59 }
60 4
61 }
62 FrameFormat::Magicless => 0,
63 };
64 let fhd = *src.get(start).ok_or(Error::Truncated)?;
65 let fcs_flag = fhd >> 6;
66 let single_segment = fhd & 0x20 != 0;
67 if fhd & 0x08 != 0 {
68 return Err(Error::Reserved);
69 }
70 let checksum = fhd & 0x04 != 0;
71 let did_len = [0usize, 1, 2, 4][(fhd & 3) as usize];
72 let fcs_len = match fcs_flag {
73 0 if single_segment => 1,
74 0 => 0,
75 1 => 2,
76 2 => 4,
77 _ => 8,
78 };
79 let mut at = start + 1;
80 let mut window_size = 0u64;
81 if !single_segment {
82 let wd = *src.get(at).ok_or(Error::Truncated)?;
83 let log = WINDOW_LOG_MIN + (wd >> 3) as u32;
84 let base = 1u64 << log;
85 window_size = base + (base / 8) * (wd & 7) as u64;
86 at += 1;
87 }
88 let dict_id = le(src, at, did_len)? as u32;
89 at += did_len;
90 let content_size = match fcs_len {
91 0 => None,
92 2 => Some(le(src, at, 2)? + 256),
93 n => Some(le(src, at, n)?),
94 };
95 at += fcs_len;
96 if single_segment {
97 window_size = content_size.ok_or(Error::Corrupt("single segment without size"))?;
98 }
99 Ok(FrameHeader { window_size, content_size, dict_id, checksum, single_segment, header_len: at })
100 }
101
102 pub fn write(&self, window_log: u32, out: &mut Vec<u8>) {
104 self.write_format(FrameFormat::Standard, window_log, out)
105 }
106
107 pub fn write_format(&self, format: FrameFormat, window_log: u32, out: &mut Vec<u8>) {
109 if format == FrameFormat::Standard {
110 out.extend_from_slice(&MAGIC.to_le_bytes());
111 }
112 let (fcs_flag, fcs_len) = match self.content_size {
113 None => (0u8, 0usize),
114 Some(n) if self.single_segment && n < 256 => (0, 1),
115 Some(n) if (256..65536 + 256).contains(&n) => (1, 2),
116 Some(n) if n <= u32::MAX as u64 => (2, 4),
117 Some(_) => (3, 8),
118 };
119 let (did_flag, did_len) = match self.dict_id {
120 0 => (0u8, 0usize),
121 1..=255 => (1, 1),
122 256..=65535 => (2, 2),
123 _ => (3, 4),
124 };
125 let fhd = (fcs_flag << 6) | ((self.single_segment as u8) << 5) | ((self.checksum as u8) << 2) | did_flag;
126 out.push(fhd);
127 if !self.single_segment {
128 out.push(((window_log - WINDOW_LOG_MIN) << 3) as u8);
129 }
130 out.extend_from_slice(&self.dict_id.to_le_bytes()[..did_len]);
131 if let Some(n) = self.content_size {
132 let v = if fcs_len == 2 { n - 256 } else { n };
133 out.extend_from_slice(&v.to_le_bytes()[..fcs_len]);
134 }
135 }
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum BlockType {
141 Raw,
142 Rle,
143 Compressed,
144}
145
146pub fn parse_block_header(src: &[u8]) -> Result<(bool, BlockType, usize)> {
148 let v = le(src, 0, 3)? as u32;
149 let ty = match (v >> 1) & 3 {
150 0 => BlockType::Raw,
151 1 => BlockType::Rle,
152 2 => BlockType::Compressed,
153 _ => return Err(Error::Reserved),
154 };
155 Ok((v & 1 == 1, ty, (v >> 3) as usize))
156}
157
158pub fn write_block_header(last: bool, ty: BlockType, size: usize, out: &mut Vec<u8>) {
159 let t = match ty {
160 BlockType::Raw => 0,
161 BlockType::Rle => 1,
162 BlockType::Compressed => 2,
163 };
164 let v = (last as u32) | (t << 1) | ((size as u32) << 3);
165 out.extend_from_slice(&v.to_le_bytes()[..3]);
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn header_roundtrip() {
174 for (cs, ss, dict, ck) in
175 [(Some(0u64), true, 0u32, false), (Some(300), true, 7, true), (Some(70000), false, 70000, false), (None, false, 1 << 30, true)]
176 {
177 let h = FrameHeader { window_size: 0, content_size: cs, dict_id: dict, checksum: ck, single_segment: ss, header_len: 0 };
178 let mut out = Vec::new();
179 h.write(20, &mut out);
180 let p = FrameHeader::parse(&out).unwrap();
181 assert_eq!((p.content_size, p.dict_id, p.checksum, p.single_segment, p.header_len), (cs, dict, ck, ss, out.len()));
182 if !ss {
183 assert_eq!(p.window_size, 1 << 20);
184 }
185 }
186 }
187}