1use crate::base::RUMVec;
21pub use branches::{likely as cpu_likely_branch, prefetch_read_data, unlikely as cpu_unlikely_branch};
22pub use std::simd::prelude::*;
23use crate::rumtk_mem_quick_array_init;
24
25pub const CPU_L1_PREFETCH: i32 = 0;
26pub const CPU_L2_PREFETCH: i32 = 1;
27pub const CPU_L3_PREFETCH: i32 = 2;
28pub const CPU_NONTEMPORAL_PREFETCH: i32 = 3;
29pub const CPU_L1_CACHE_LINE_SIZE: usize = 64; pub const CPU_L1_CACHE_SIZE: usize = 32 * 1024; pub const CPU_PAGE_SIZE: usize = 4 * 1024; pub const CPU_SIMD_64_SIZE: usize = 64;
33pub const CPU_SIMD_32_SIZE: usize = 32;
34pub const CPU_SIMD_16_SIZE: usize = 16;
35pub const CPU_SIMD_8_SIZE: usize = 8;
36pub const CPU_SIMD_AVERAGE_ALU_COUNT: usize = 4;
37pub const CPU_SEARCH_WINDOW_1024_SIZE: usize = 1024;
38pub const CPU_SEARCH_WINDOW_512_SIZE: usize = 512;
39pub const CPU_SEARCH_WINDOW_256_SIZE: usize = 256;
40pub const CPU_SEARCH_WINDOW_128_SIZE: usize = 128;
41pub const CPU_SEARCH_WINDOW_64_SIZE: usize = 64;
42pub const CPU_SEARCH_WINDOW_32_SIZE: usize = 32;
43pub const CPU_SEARCH_WINDOW_16_SIZE: usize = 16;
44
45
46#[cfg(feature = "simd")]
47pub type u8xN<const SEARCH_WINDOW_SIZE: usize> = Simd<u8, SEARCH_WINDOW_SIZE>;
48
49#[inline]
51pub fn cpu_l3_prefetch(data: *const u8) {
52 prefetch_read_data::<u8, CPU_L3_PREFETCH>(data);
53}
54
55#[inline]
56pub fn cpu_l2_prefetch(data: *const u8) {
57 prefetch_read_data::<u8, CPU_L2_PREFETCH>(data);
58}
59
60#[inline]
61pub fn cpu_l1_prefetch(data: *const u8) {
62 prefetch_read_data::<u8, CPU_L1_PREFETCH>(data);
63}
64
65#[inline(always)]
66pub fn cpu_slice_to_array<const SLICE_SIZE: usize>(chunk: &[u8]) -> [u8; SLICE_SIZE] {
67 chunk.try_into().expect("length mismatch")
68}
69
70#[inline(always)]
71pub fn cpu_slice_to_array_padded<const SLICE_SIZE: usize, const PAD: u8>(chunk: &[u8]) -> [u8; SLICE_SIZE] {
72 let mut result = [PAD; SLICE_SIZE];
73 result[..chunk.len()].copy_from_slice(chunk);
74 result
75}
76
77#[cfg(feature = "simd")]
78#[inline(always)]
79pub fn cpu_slice_to_simd<const SLICE_SIZE: usize>(chunk: &[u8]) -> u8xN<SLICE_SIZE> {
80 u8xN::from_slice(chunk)
81}
82
83#[cfg(feature = "simd")]
84#[inline(always)]
85pub fn cpu_slice_to_simd_padded<const SLICE_SIZE: usize, const PAD: u8>(chunk: &[u8]) -> u8xN<SLICE_SIZE> {
86 u8xN::from_array(cpu_slice_to_array_padded::<SLICE_SIZE, 0>(chunk))
87}
88
89#[inline(always)]
90pub fn cpu_slice_splat<const SLICE_SIZE: usize>(input: &[u8]) -> [u8; SLICE_SIZE] {
91 let mut result = [0u8; SLICE_SIZE];
92
93 for chunk in result.chunks_mut(input.len()) {
94 chunk.copy_from_slice(&input[..chunk.len()]);
95 }
96
97 result
98}
99
100#[cfg(feature = "simd")]
102#[inline(always)]
103pub fn cpu_simd_shift_right_n<const LANE_SIZE: usize, const SHIFT: usize, const PAD: u8>(item: &u8xN<LANE_SIZE>) -> u8xN<LANE_SIZE> {
104 item.shift_elements_right::<SHIFT>(PAD)
105}
106
107#[cfg(feature = "simd")]
108#[inline(always)]
109pub fn cpu_simd_masks<const LANE_SIZE: usize>(pattern: &[u8]) -> RUMVec<u8xN<LANE_SIZE>> {
110 let mask = u8xN::<LANE_SIZE>::from_array(cpu_slice_splat(pattern));
111 let mut masks = RUMVec::<u8xN<LANE_SIZE>>::with_capacity(pattern.len());
112
113 masks.push(mask);
114
115 for i in 1..pattern.len() {
116 let shifted = cpu_simd_shift_right_n::<LANE_SIZE, 1, 0>(&mask);
117 masks.push(shifted);
118 }
119
120 masks
121}
122
123#[inline(always)]
126pub fn cpu_find_fallback(chunk: &[u8], byte: u8) -> Option<usize> {
127 chunk.iter().position(|c| *c==byte)
128}
129
130#[cfg(feature = "simd")]
131#[inline(always)]
132fn cpu_find_simd_avx2_n<const SEARCH_WINDOW_SIZE: usize>(data_vec: u8xN<SEARCH_WINDOW_SIZE>, target: u8xN<SEARCH_WINDOW_SIZE>) -> Option<usize> {
133 let mask = data_vec.simd_eq(target);
134
135 if mask.any() {
136 let bitmask = mask.to_bitmask();
137 let lane_i = bitmask.trailing_zeros() as usize;
138 return Some(lane_i);
139 }
140
141 None
142}
143
144#[cfg(feature = "simd")]
145#[inline(always)]
146fn cpu_find_simd_avx2_unpadded<const SEARCH_WINDOW_SIZE: usize>(chunk: &[u8], target: u8xN<SEARCH_WINDOW_SIZE>) -> Option<usize> {
147 let data_vec = cpu_slice_to_simd::<SEARCH_WINDOW_SIZE>(chunk);
148 cpu_find_simd_avx2_n(data_vec, target)
149}
150
151#[cfg(feature = "simd")]
152#[inline(always)]
153fn cpu_find_simd_avx2_padded<const SEARCH_WINDOW_SIZE: usize>(chunk: &[u8], target: u8xN<SEARCH_WINDOW_SIZE>) -> Option<usize> {
154 let data_vec = cpu_slice_to_simd_padded::<SEARCH_WINDOW_SIZE, 0>(chunk);
155 cpu_find_simd_avx2_n(data_vec, target)
156}
157
158#[cfg(feature = "simd")]
163#[inline(always)]
164pub fn cpu_find_simd_n<const LANE_SIZE: usize>
165(
166 chunk: &[u8],
167 byte: u8,
168) -> Option<usize>
169{
170 let mask = u8xN::<LANE_SIZE>::splat(byte);
171 let needs_padding = chunk.len() % LANE_SIZE;
172 let unpadded = chunk.len() - needs_padding; for (i,window) in chunk[..unpadded].chunks(LANE_SIZE).enumerate() {
176 cpu_l1_prefetch(window.as_ptr());
177 if let Some(lane_i) = cpu_find_simd_avx2_unpadded::<LANE_SIZE>(window, mask) {
178 return Some(i * LANE_SIZE + lane_i);
179 }
180 }
181
182 cpu_find_simd_avx2_padded::<LANE_SIZE>(&chunk[unpadded..], mask).map(|idx| unpadded + idx)
183}
184
185#[cfg(feature = "simd")]
186#[inline(always)]
187pub fn cpu_find(window: &[u8], byte: u8) -> Option<usize> {
188 cpu_find_simd_n::<CPU_SIMD_64_SIZE>(
189 window,
190 byte,
191 )
192}
193
194#[cfg(not(feature = "simd"))]
195#[inline]
196pub fn cpu_find(window: &[u8], byte: u8) -> Option<usize> {
197 cpu_find_fallback(
198 window,
199 byte,
200 )
201}
202
203#[inline(always)]
205pub fn cpu_continuous_count_fallback(chunk: &[u8], byte: u8) -> Option<usize> {
206 let mut count = 0;
207 for i in 0..chunk.len() {
208 if cpu_unlikely_branch(chunk[i] != byte) {
209 return Some(count);
210 }
211 count += 1;
212 }
213 Some(count)
214}
215
216#[cfg(feature = "simd")]
217#[inline]
218fn cpu_continuous_count_simd_avx2_n<const SEARCH_WINDOW_SIZE: usize>(data_vec: u8xN<SEARCH_WINDOW_SIZE>, target: u8xN<SEARCH_WINDOW_SIZE>) -> usize {
219 let mask = data_vec.simd_eq(target);
220
221 let bitmask = mask.to_bitmask();
222 bitmask.trailing_zeros() as usize
223}
224
225#[cfg(feature = "simd")]
226#[inline]
227fn cpu_continuous_count_simd_avx2_unpadded<const SEARCH_WINDOW_SIZE: usize>(chunk: &[u8], target: u8xN<SEARCH_WINDOW_SIZE>) -> usize {
228 let data_vec = cpu_slice_to_simd::<SEARCH_WINDOW_SIZE>(chunk);
229 cpu_continuous_count_simd_avx2_n(data_vec, target)
230}
231
232#[cfg(feature = "simd")]
233#[inline]
234fn cpu_continuous_count_simd_avx2_padded<const SEARCH_WINDOW_SIZE: usize>(chunk: &[u8], target: u8xN<SEARCH_WINDOW_SIZE>) -> usize {
235 let data_vec = cpu_slice_to_simd_padded::<SEARCH_WINDOW_SIZE, 0>(chunk);
236 cpu_continuous_count_simd_avx2_n(data_vec, target)
237}
238
239#[cfg(feature = "simd")]
252#[inline]
253pub fn cpu_continuous_count_simd_n<const LANE_SIZE: usize>
254(
255 chunk: &[u8],
256 byte: u8,
257) -> Option<usize>
258{
259 let mask = u8xN::<LANE_SIZE>::splat(byte);
260 let mut iter = chunk.chunks(LANE_SIZE);
261 let max_iter = iter.len();
262 let large_steps = max_iter / CPU_SIMD_AVERAGE_ALU_COUNT; let mut indx = 0;
264
265 for _ in 0..large_steps {
270 let mut sizes = rumtk_mem_quick_array_init!(usize, CPU_SIMD_AVERAGE_ALU_COUNT);
271 sizes[0] = cpu_continuous_count_simd_avx2_unpadded::<LANE_SIZE>(iter.next().unwrap_or_default(), mask);
272 sizes[1] = cpu_continuous_count_simd_avx2_unpadded::<LANE_SIZE>(iter.next().unwrap_or_default(), mask);
273 sizes[2] = cpu_continuous_count_simd_avx2_unpadded::<LANE_SIZE>(iter.next().unwrap_or_default(), mask);
274 sizes[3] = cpu_continuous_count_simd_avx2_unpadded::<LANE_SIZE>(iter.next().unwrap_or_default(), mask);
275
276 for s in sizes {
277 if s < LANE_SIZE {
278 return Some(indx + s);
279 }
280 indx += LANE_SIZE;
281 }
282 }
283
284 match iter.next() {
285 Some(window) => {
286 Some(indx + cpu_continuous_count_simd_avx2_padded::<LANE_SIZE>(window, mask))
287 },
288 None => Some(indx),
289 }
290}
291
292#[cfg(feature = "simd")]
293#[inline]
294pub fn cpu_continuous_count(window: &[u8], byte: u8) -> Option<usize> {
295 let start = cpu_find(window, byte)?;
296 cpu_continuous_count_simd_n::<CPU_SIMD_64_SIZE>(
297 &window[start..],
298 byte,
299 )
300}
301
302#[cfg(not(feature = "simd"))]
303#[inline]
304pub fn cpu_continuous_count(window: &[u8], byte: u8) -> Option<usize> {
305 let start = cpu_find(window, byte)?;
306 cpu_continuous_count_fallback(
307 &window[start..],
308 byte,
309 )
310}
311
312#[inline(always)]
314pub fn cpu_replace_fallback(data: &mut [u8], pattern: u8, replacement: u8) {
315 for i in 0..data.len() {
316 if data[i] == pattern {
317 data[i] = replacement;
318 }
319 }
320}
321
322#[cfg(feature = "simd")]
323#[inline(always)]
324pub fn cpu_find_replace_simd_n<const LANE_SIZE: usize>(chunk: &mut [u8], pattern: u8xN<LANE_SIZE>, replacement: u8xN<LANE_SIZE>) {
325 let simd_chunk = u8xN::<LANE_SIZE>::from_array(cpu_slice_to_array_padded::<LANE_SIZE, 0>(chunk));
326 let bitmask = simd_chunk.simd_eq(pattern);
327
328 if bitmask.any() {
329 replacement.store_select(chunk, bitmask);
330 }
331}
332
333#[cfg(feature = "simd")]
334#[inline(always)]
335pub fn cpu_replace_simd_n<const LANE_SIZE: usize>(data: &mut [u8], pattern: u8, replacement: u8) {
336 let mask = u8xN::<LANE_SIZE>::splat(pattern);
337 let simd_replacement = u8xN::<LANE_SIZE>::splat(replacement);
338
339 for chunk in data.chunks_mut(LANE_SIZE) {
340 cpu_find_replace_simd_n::<LANE_SIZE>(chunk, mask, simd_replacement);
341 }
342}
343
344#[cfg(feature = "simd")]
345#[inline(always)]
346pub fn cpu_replace_byte(data: &mut [u8], pattern: u8, replacement: u8) {
347 cpu_replace_simd_n::<CPU_SIMD_64_SIZE>(data, pattern, replacement)
348}
349
350#[cfg(not(feature = "simd"))]
351#[inline(always)]
352pub fn cpu_replace_byte(data: &mut [u8], pattern: u8, replacement: u8) {
353 cpu_replace_fallback(data, pattern, replacement)
354}
355
356pub type CPUTokenStackIndex<const LANE_SIZE: usize> = [u32; LANE_SIZE];
358pub type CPUTokenRelativeStackInfo<const LANE_SIZE: usize> = (usize, CPUTokenStackIndex<LANE_SIZE>);
359pub type CPUTokenIndexCollection = RUMVec<u32>;
360pub type CPUTokenIndexSet = (u8, RUMVec<u32>);
361pub type CPUTokenSet = (u8, u32);
362pub type CPUTokenSetCollection = RUMVec<CPUTokenSet>;
363
364#[inline(always)]
365pub fn cpu_collect_fallback(chunk: &[u8], byte: u8, offset: usize) -> CPUTokenIndexCollection {
366 let mut results: CPUTokenStackIndex<CPU_SIMD_64_SIZE> = [0; CPU_SIMD_64_SIZE];
367 let mut length = 0;
368
369 for i in 0..chunk.len() {
370 if chunk[i]==byte {
371 let pos = offset + i;
372 results[length] = pos as u32;
373 length += 1;
374 }
375 }
376
377 CPUTokenIndexCollection::from(&results[..length])
378}
379
380#[cfg(feature = "simd")]
381#[inline]
382fn cpu_collect_simd_avx2_n<const LANE_SIZE: usize>(data_vec: &u8xN<LANE_SIZE>, target: u8xN<LANE_SIZE>, offset: usize) -> Option<CPUTokenRelativeStackInfo<LANE_SIZE>> {
383 let mut results: CPUTokenStackIndex<LANE_SIZE> = [0; LANE_SIZE];
384 let mut length = 0;
385
386 let mask = data_vec.simd_eq(target);
387
388 if mask.any() {
389 let items = mask.to_array();
390
391 for i in 0..items.len() {
392 if items[i] {
393 let pos = offset + i;
394 results[length] = pos as u32;
395 length += 1;
396 }
397 }
398
399 return Some((length, results));
400 }
401
402 None
403}
404
405#[cfg(feature = "simd")]
406#[inline]
407pub fn cpu_collect_simd_n<const LANE_SIZE: usize>
408(
409 chunk: &[u8],
410 byte: u8,
411 offset: usize
412) -> CPUTokenIndexCollection
413{
414 let mask = u8xN::<LANE_SIZE>::splat(byte);
415 let (prefix, middle, postfix) = chunk.as_simd::<LANE_SIZE>();
416
417 let mut local_offset: usize = offset;
418 let data: CPUTokenIndexCollection = cpu_collect_fallback(prefix, byte, local_offset);
419 let mut positions: CPUTokenIndexCollection = CPUTokenIndexCollection::from(data);
420 local_offset += prefix.len();
421
422 for window in middle.into_iter() {
423 match cpu_collect_simd_avx2_n::<LANE_SIZE>(window, mask, local_offset) {
424 Some((len, data)) => {
425 positions.extend_from_slice(&data[..len]);
426 },
427 None => {},
428 };
429 local_offset += LANE_SIZE;
430 }
431
432 let data: CPUTokenIndexCollection = cpu_collect_fallback(postfix, byte, local_offset);
433 positions.extend_from_slice(&data);
434
435 positions
436}
437
438#[cfg(feature = "simd")]
439#[inline]
440pub fn cpu_collect(window: &[u8], byte: u8, offset: usize) -> CPUTokenIndexSet {
441 let indx = cpu_collect_simd_n::<CPU_SIMD_64_SIZE>(
442 window,
443 byte,
444 offset
445 );
446 (byte, indx)
447}
448
449#[cfg(not(feature = "simd"))]
450#[inline]
451pub fn cpu_collect(window: &[u8], byte: u8, offset: usize) -> CPUTokenIndexSet {
452 let indx = cpu_collect_fallback(
453 window,
454 byte,
455 offset
456 );
457 (byte, indx)
458}
459
460#[inline]
461pub fn cpu_tokenize<const WINDOW_SIZE: usize>(haystack: &[u8], bytes: &[u8]) -> CPUTokenSetCollection
462{
463 let mut results = CPUTokenSetCollection::with_capacity(haystack.len() * size_of::<CPUTokenSet>());
464 let mut offset = 0;
465
466 for window in haystack.chunks(WINDOW_SIZE) {
467 for byte in bytes {
468 let (b, indx) = cpu_collect(window, *byte, offset);
469
470 if !indx.is_empty() {
471 for tok_indx in indx {
472 results.push((b, tok_indx));
473 }
474 }
475 }
476 offset += window.len();
477 }
478
479 results.sort_unstable_by(|a,b| a.1.cmp(&b.1));
480
481 results
482}
483
484#[inline]
485pub fn cpu_tokenize_rev<const WINDOW_SIZE: usize>(haystack: &[u8], bytes: &[u8]) -> CPUTokenSetCollection
486{
487 let reversed: Vec<u8> = bytes.iter().rev().cloned().collect();
488 cpu_tokenize::<WINDOW_SIZE>(haystack, &reversed)
489}