1use rayon::prelude::*;
16
17use crate::deflate_scan::{self, FlushBoundary};
18use crate::entry::ZipError;
19
20pub struct ChunkSplit {
22 pub segment_starts: Vec<usize>,
24 pub decode_segments: usize,
26 pub consumed: usize,
28}
29
30pub fn split_chunk(data: &[u8], n_workers: usize, is_last: bool) -> Option<ChunkSplit> {
38 let boundaries: Vec<FlushBoundary> =
39 deflate_scan::split_boundaries_parallel(data, n_workers);
40
41 if boundaries.is_empty() {
42 return None;
43 }
44
45 let mut segment_starts = Vec::with_capacity(boundaries.len() + 1);
47 segment_starts.push(0usize);
48 for b in &boundaries {
49 if *segment_starts.last().unwrap() != b.start {
50 segment_starts.push(b.start);
51 }
52 }
53
54 let n = segment_starts.len();
55 let decode_segments = if is_last {
56 n
57 } else if n > 1 {
58 n - 1
59 } else {
60 return None;
61 };
62
63 let consumed = if decode_segments < n {
64 segment_starts[decode_segments]
65 } else {
66 data.len()
67 };
68
69 Some(ChunkSplit { segment_starts, decode_segments, consumed })
70}
71
72pub fn decode_segment_into(
79 compressed: &[u8],
80 output: &mut Vec<u8>,
81) -> Result<(), ZipError> {
82 let headroom = linflate::OVERWRITE_HEADROOM;
83 let out_start = output.len();
84 let mut buf_size = (compressed.len() * 4).max(4096) + headroom;
85
86 loop {
87 output.reserve(buf_size);
88 let spare_len = output.capacity() - out_start;
89 let out_buf = unsafe {
90 std::slice::from_raw_parts_mut(output.as_mut_ptr().add(out_start), spare_len)
91 };
92
93 match linflate::inflate_segment(compressed, out_buf) {
94 Ok(written) => {
95 unsafe { output.set_len(out_start + written) };
96 return Ok(());
97 }
98 Err(linflate::InflateError::OutputOverflow) => {
99 buf_size = buf_size.saturating_mul(2);
100 if buf_size > 4 * 1024 * 1024 * 1024 {
101 return Err(ZipError("DEFLATE segment too large"));
102 }
103 }
104 Err(_) => return Err(ZipError("DEFLATE segment decompression failed")),
105 }
106 }
107}
108
109pub fn decode_segment(compressed: &[u8]) -> Result<Vec<u8>, ZipError> {
111 let mut out = Vec::new();
112 decode_segment_into(compressed, &mut out)?;
113 Ok(out)
114}
115
116pub fn decode_chunk(
127 data: &[u8],
128 n_workers: usize,
129 is_last: bool,
130) -> Result<(Vec<Vec<u8>>, usize), ZipError> {
131 let split = split_chunk(data, n_workers, is_last)
132 .ok_or(ZipError("no full-flush boundaries — fallback to single-core"))?;
133
134 let segments: Vec<&[u8]> = (0..split.decode_segments)
135 .map(|i| {
136 let start = split.segment_starts[i];
137 let end = if i + 1 < split.segment_starts.len() {
138 split.segment_starts[i + 1]
139 } else {
140 split.consumed
141 };
142 &data[start..end]
143 })
144 .collect();
145
146 let outputs: Vec<Result<Vec<u8>, ZipError>> = crate::thread_pool().install(|| {
147 segments.into_par_iter().map(|seg| decode_segment(seg)).collect()
148 });
149
150 let mut result = Vec::with_capacity(outputs.len());
151 for r in outputs {
152 result.push(r?);
153 }
154
155 Ok((result, split.consumed))
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 fn stored_block(payload: &[u8], bfinal: bool) -> Vec<u8> {
166 let mut out = Vec::new();
168 let header: u8 = if bfinal { 0x01 } else { 0x00 };
169 out.push(header);
170 let len = payload.len() as u16;
171 out.extend_from_slice(&len.to_le_bytes());
172 out.extend_from_slice(&(!len).to_le_bytes());
173 out.extend_from_slice(payload);
174 out
175 }
176
177 fn flush_marker() -> Vec<u8> {
178 vec![0x00, 0x00, 0x00, 0xFF, 0xFF]
180 }
181
182 #[test]
185 fn split_chunk_no_boundaries_returns_none() {
186 let data: Vec<u8> = (0u8..=255).cycle().take(128).collect();
188 assert!(split_chunk(&data, 4, true).is_none());
189 }
190
191 #[test]
192 fn split_chunk_single_boundary_is_last() {
193 let payload_a = vec![0xAA_u8; 15];
199 let mut buf = stored_block(&payload_a, false); buf.extend_from_slice(&flush_marker()); buf.extend_from_slice(&stored_block(b"world", true)); let split = split_chunk(&buf, 2, true).expect("should find boundary");
204 assert!(split.decode_segments >= 1);
205 assert_eq!(split.consumed, buf.len());
206 }
207
208 #[test]
209 fn split_chunk_single_boundary_not_last() {
210 let mut buf = stored_block(b"hello", false);
214 buf.extend_from_slice(&flush_marker());
215 buf.extend_from_slice(&stored_block(b"world", false));
216
217 let result = split_chunk(&buf, 2, false);
218 if let Some(s) = result {
219 assert!(s.consumed <= buf.len());
220 assert!(s.decode_segments >= 1);
221 }
222 }
224
225 #[test]
228 fn decode_segment_stored_final() {
229 let data = stored_block(b"Hello, World!\n", true);
230 let out = decode_segment(&data).expect("decode stored block");
231 assert_eq!(out, b"Hello, World!\n");
232 }
233
234 #[test]
235 fn decode_segment_stored_non_final() {
236 let data = stored_block(b"partial segment", false);
238 let out = decode_segment(&data).expect("decode non-final stored block");
239 assert_eq!(out, b"partial segment");
240 }
241
242 #[test]
243 fn decode_segment_real_deflate() {
244 use miniz_oxide::deflate::compress_to_vec;
246 let original = b"The quick brown fox jumps over the lazy dog".repeat(50);
247 let compressed = compress_to_vec(&original, 6);
248 let out = decode_segment(&compressed).expect("decode real deflate");
249 assert_eq!(out, original.to_vec());
250 }
251
252 #[test]
255 fn decode_chunk_no_boundaries_returns_err() {
256 let data = stored_block(b"no flush here", true);
258 assert!(decode_chunk(&data, 4, true).is_err());
259 }
260
261 #[test]
262 fn decode_chunk_two_segments() {
263 let payload_a = vec![0xAA_u8; 22]; let payload_b = vec![0xBB_u8; 5]; let mut buf = stored_block(&payload_a, false);
270 buf.extend_from_slice(&flush_marker());
271 buf.extend_from_slice(&stored_block(&payload_b, true));
272
273 let (outputs, consumed) = decode_chunk(&buf, 2, true).expect("decode two segments");
274 assert_eq!(consumed, buf.len());
275 let combined: Vec<u8> = outputs.into_iter().flatten().collect();
276 let expected: Vec<u8> = payload_a.iter().chain(payload_b.iter()).copied().collect();
277 assert_eq!(combined, expected);
278 }
279}