lzip_parallel/
deflate_scan.rs1use rayon::prelude::*;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct FlushBoundary {
25 pub start: usize,
26}
27
28pub fn find_next_flush(buf: &[u8], from: usize) -> Option<FlushBoundary> {
33 if buf.len() < 4 || from + 4 > buf.len() {
34 return None;
35 }
36
37 #[cfg(target_arch = "x86_64")]
38 {
39 if std::arch::is_x86_feature_detected!("avx512bw") {
40 return unsafe { find_next_flush_avx512(buf, from) };
41 }
42 if std::arch::is_x86_feature_detected!("avx2") {
43 return unsafe { find_next_flush_avx2(buf, from) };
44 }
45 }
46
47 find_next_flush_scalar(buf, from)
48}
49
50fn find_next_flush_scalar(buf: &[u8], from: usize) -> Option<FlushBoundary> {
52 if buf.len() < 4 {
53 return None;
54 }
55 let end = buf.len() - 3;
56 let mut i = from;
57 while i < end {
58 if buf[i + 2] != 0xFF {
60 i += 1;
61 continue;
62 }
63 if buf[i] == 0x00 && buf[i + 1] == 0x00 && buf[i + 3] == 0xFF {
64 return Some(FlushBoundary { start: i + 4 });
65 }
66 i += 1;
67 }
68 None
69}
70
71#[cfg(target_arch = "x86_64")]
79#[target_feature(enable = "avx512bw")]
80unsafe fn find_next_flush_avx512(buf: &[u8], from: usize) -> Option<FlushBoundary> {
81 use core::arch::x86_64::*;
82
83 unsafe {
84 let zero_vec = _mm512_setzero_si512();
85 let ff_vec = _mm512_set1_epi8(-1i8);
86
87 let ptr = buf.as_ptr();
88 let len = buf.len();
89 let mut i = from;
90
91 while i + 67 <= len {
93 let chunk = _mm512_loadu_si512(ptr.add(i) as *const __m512i);
94
95 let zero_mask = _mm512_cmpeq_epi8_mask(chunk, zero_vec);
97 let ff_mask = _mm512_cmpeq_epi8_mask(chunk, ff_vec);
98
99 let candidates = (zero_mask & (zero_mask >> 1)) & ((ff_mask >> 2) & (ff_mask >> 3));
101
102 if candidates != 0 {
103 let bit_pos = candidates.trailing_zeros() as usize;
104 let pos = i + bit_pos;
105 if pos + 4 <= len
106 && buf[pos] == 0x00
107 && buf[pos + 1] == 0x00
108 && buf[pos + 2] == 0xFF
109 && buf[pos + 3] == 0xFF
110 {
111 return Some(FlushBoundary { start: pos + 4 });
112 }
113 i += bit_pos + 1;
114 continue;
115 }
116
117 i += 64;
118 }
119
120 find_next_flush_scalar(buf, i)
122 }
123}
124
125#[cfg(target_arch = "x86_64")]
131#[target_feature(enable = "avx2")]
132unsafe fn find_next_flush_avx2(buf: &[u8], from: usize) -> Option<FlushBoundary> {
133 use core::arch::x86_64::*;
134
135 unsafe {
136 let zero_vec = _mm256_setzero_si256();
137 let ff_vec = _mm256_set1_epi8(-1); let ptr = buf.as_ptr();
140 let len = buf.len();
141
142 let mut i = from;
143
144 while i + 35 <= len {
147 let chunk = _mm256_loadu_si256(ptr.add(i) as *const __m256i);
148
149 let eq_zero = _mm256_cmpeq_epi8(chunk, zero_vec);
151 let eq_ff = _mm256_cmpeq_epi8(chunk, ff_vec);
152
153 let zero_mask = _mm256_movemask_epi8(eq_zero) as u32;
154 let ff_mask = _mm256_movemask_epi8(eq_ff) as u32;
155
156 let candidates = (zero_mask & (zero_mask >> 1)) & ((ff_mask >> 2) & (ff_mask >> 3));
160
161 if candidates != 0 {
162 let bit_pos = candidates.trailing_zeros() as usize;
163 let pos = i + bit_pos;
164 if pos + 4 <= len
165 && buf[pos] == 0x00
166 && buf[pos + 1] == 0x00
167 && buf[pos + 2] == 0xFF
168 && buf[pos + 3] == 0xFF
169 {
170 return Some(FlushBoundary { start: pos + 4 });
171 }
172 i += bit_pos + 1;
173 continue;
174 }
175
176 i += 32;
177 }
178
179 find_next_flush_scalar(buf, i)
181 }
182}
183
184pub fn find_all_flushes(buf: &[u8]) -> Vec<FlushBoundary> {
186 let mut result = Vec::new();
187 let mut pos = 0;
188 while let Some(b) = find_next_flush(buf, pos) {
189 result.push(b);
190 pos = b.start;
191 }
192 result
193}
194
195pub fn split_boundaries_parallel(buf: &[u8], n_splits: usize) -> Vec<FlushBoundary> {
201 if n_splits <= 1 || buf.len() < 8 {
202 return Vec::new();
203 }
204
205 let mut found: Vec<Option<FlushBoundary>> = crate::thread_pool().install(|| {
206 (1..n_splits)
207 .into_par_iter()
208 .map(|i| {
209 let nominal = buf.len() * i / n_splits;
210 find_next_flush(buf, nominal)
211 })
212 .collect()
213 });
214
215 let mut result: Vec<FlushBoundary> = found.drain(..).flatten().collect();
216 result.sort_by_key(|b| b.start);
217 result.dedup_by_key(|b| b.start);
218 result
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 fn make_flush_marker() -> Vec<u8> {
226 vec![0x00, 0x00, 0x00, 0xFF, 0xFF]
227 }
228
229 #[test]
230 fn find_flush_at_start() {
231 let mut buf = make_flush_marker();
232 buf.extend_from_slice(&[0xAA, 0xBB]);
233 let b = find_next_flush(&buf, 0).unwrap();
235 assert_eq!(b.start, 5); }
237
238 #[test]
239 fn find_flush_mid_buffer() {
240 let mut buf = vec![0xDE, 0xAD, 0xBE, 0xEF];
241 buf.extend_from_slice(&make_flush_marker());
242 buf.extend_from_slice(&[0x01, 0x02]);
243 let b = find_next_flush(&buf, 0).unwrap();
244 assert_eq!(b.start, 9);
245 }
246
247 #[test]
248 fn no_flush_in_random_data() {
249 let buf: Vec<u8> = (0..64).map(|i| (i * 37 + 1) as u8).collect();
251 let _ = find_next_flush(&buf, 0);
253 }
254
255 #[test]
256 fn find_all_flushes_two_markers() {
257 let mut buf = Vec::new();
258 buf.extend_from_slice(&make_flush_marker()); buf.extend_from_slice(&[0xAA; 10]);
260 buf.extend_from_slice(&make_flush_marker()); let all = find_all_flushes(&buf);
262 assert_eq!(all.len(), 2);
263 assert_eq!(all[0].start, 5);
264 assert_eq!(all[1].start, 20);
265 }
266}