zrip_core/frame/
header.rs1#![forbid(unsafe_code)]
2
3use crate::error::DecompressError;
4use crate::frame::ZSTD_MAGIC;
5
6#[derive(Debug, Clone)]
7pub struct FrameHeader {
8 pub window_size: u64,
9 pub frame_content_size: Option<u64>,
10 pub dict_id: Option<u32>,
11 pub content_checksum: bool,
12 pub single_segment: bool,
13 pub header_size: usize,
14}
15
16pub fn parse_frame_header(data: &[u8]) -> Result<FrameHeader, DecompressError> {
17 if data.len() < 4 {
18 return Err(DecompressError::InputExhausted);
19 }
20
21 let magic = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
22 if magic != ZSTD_MAGIC {
23 return Err(DecompressError::BadMagic);
24 }
25
26 parse_frame_header_after_magic(&data[4..], 4)
27}
28
29pub fn parse_frame_header_after_magic(
30 data: &[u8],
31 magic_size: usize,
32) -> Result<FrameHeader, DecompressError> {
33 if data.is_empty() {
34 return Err(DecompressError::BadFrameHeader);
35 }
36
37 let descriptor = data[0];
38 let dict_id_flag = descriptor & 0x03;
39 let content_checksum = (descriptor & 0x04) != 0;
40 let single_segment = (descriptor & 0x20) != 0;
41 let fcs_field_size_flag = (descriptor >> 6) & 0x03;
42
43 let reserved = (descriptor & 0x08) != 0;
44 if reserved {
45 return Err(DecompressError::BadFrameHeader);
46 }
47 let mut offset = 1;
50
51 let window_size = if single_segment {
52 0
53 } else {
54 if data.len() <= offset {
55 return Err(DecompressError::BadFrameHeader);
56 }
57 let window_desc = data[offset];
58 offset += 1;
59 let exponent = (window_desc >> 3) as u64;
60 let mantissa = (window_desc & 0x07) as u64;
61 let window_base = 1u64 << (10 + exponent);
62 let window_add = (window_base >> 3) * mantissa;
63 window_base + window_add
64 };
65
66 let dict_id_size = match dict_id_flag {
67 0 => 0,
68 1 => 1,
69 2 => 2,
70 3 => 4,
71 _ => unreachable!(),
72 };
73
74 let dict_id = if dict_id_size > 0 {
75 if data.len() < offset + dict_id_size {
76 return Err(DecompressError::BadFrameHeader);
77 }
78 let id = match dict_id_size {
79 1 => data[offset] as u32,
80 2 => u16::from_le_bytes([data[offset], data[offset + 1]]) as u32,
81 4 => u32::from_le_bytes([
82 data[offset],
83 data[offset + 1],
84 data[offset + 2],
85 data[offset + 3],
86 ]),
87 _ => unreachable!(),
88 };
89 offset += dict_id_size;
90 (id != 0).then_some(id)
91 } else {
92 None
93 };
94
95 let fcs_field_size = match fcs_field_size_flag {
96 0 => {
97 if single_segment {
98 1
99 } else {
100 0
101 }
102 }
103 1 => 2,
104 2 => 4,
105 3 => 8,
106 _ => unreachable!(),
107 };
108
109 let frame_content_size = if fcs_field_size > 0 {
110 if data.len() < offset + fcs_field_size {
111 return Err(DecompressError::BadFrameHeader);
112 }
113 let fcs = match fcs_field_size {
114 1 => data[offset] as u64,
115 2 => u16::from_le_bytes([data[offset], data[offset + 1]]) as u64 + 256,
116 4 => u32::from_le_bytes([
117 data[offset],
118 data[offset + 1],
119 data[offset + 2],
120 data[offset + 3],
121 ]) as u64,
122 8 => u64::from_le_bytes([
123 data[offset],
124 data[offset + 1],
125 data[offset + 2],
126 data[offset + 3],
127 data[offset + 4],
128 data[offset + 5],
129 data[offset + 6],
130 data[offset + 7],
131 ]),
132 _ => unreachable!(),
133 };
134 offset += fcs_field_size;
135 Some(fcs)
136 } else {
137 None
138 };
139
140 let final_window_size = if single_segment {
141 frame_content_size.unwrap_or(0)
142 } else {
143 window_size
144 };
145
146 Ok(FrameHeader {
147 window_size: final_window_size,
148 frame_content_size,
149 dict_id,
150 content_checksum,
151 single_segment,
152 header_size: offset + magic_size,
153 })
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn parse_minimal_header() {
162 let data = [0x28, 0xB5, 0x2F, 0xFD, 0x20, 0x00];
163 let hdr = parse_frame_header(&data).unwrap();
164 assert!(hdr.single_segment);
165 assert!(!hdr.content_checksum);
166 assert_eq!(hdr.dict_id, None);
167 assert_eq!(hdr.frame_content_size, Some(0));
168 }
169
170 #[test]
171 fn bad_magic() {
172 let data = [0x00, 0x00, 0x00, 0x00, 0x00];
173 assert!(matches!(
174 parse_frame_header(&data),
175 Err(DecompressError::BadMagic)
176 ));
177 }
178
179 #[test]
180 fn parse_header_after_magic() {
181 let data = [0x20, 0x00];
182 let hdr = parse_frame_header_after_magic(&data, 0).unwrap();
183 assert!(hdr.single_segment);
184 assert_eq!(hdr.frame_content_size, Some(0));
185 assert_eq!(hdr.header_size, 2);
186 }
187}