1use crate::alloc::{Allocator, SliceWrapper, SliceWrapperMut};
2use core;
3use core::cmp::min;
4
5use fearless_simd::Simd;
6
7use super::{
8 AnyHasher, BrotliEncoderParams, CloneWithAlloc, H9Opts, HasherSearchResult, HowPrepared,
9 Struct1, fix_unbroken_len, kHashMul32,
10};
11use crate::enc::combined_alloc::allocate;
12use crate::enc::static_dict::{
13 BROTLI_UNALIGNED_LOAD32, BrotliDictionary, FindMatchLengthWithLimitSimd,
14};
15use crate::enc::util::floatX;
16use crate::enc::vectorization::detect_level;
17
18pub const kInfinity: floatX = 1.7e38;
19
20#[derive(Clone, Copy, Debug)]
21pub enum Union1 {
22 cost(floatX),
23 next(u32),
24 shortcut(u32),
25}
26
27#[derive(Clone, Copy, Debug)]
28pub struct ZopfliNode {
29 pub length: u32,
31 pub distance: u32,
33 pub dcode_insert_length: u32,
35 pub u: Union1,
36}
37impl Default for ZopfliNode {
38 fn default() -> Self {
39 ZopfliNode {
40 length: 1,
41 distance: 0,
42 dcode_insert_length: 0,
43 u: Union1::cost(kInfinity),
44 }
45 }
46}
47
48pub trait Allocable<T: Copy, AllocT: Allocator<T>> {
49 fn new(m: &mut AllocT, init: T) -> Self;
50 fn new_uninit(m: &mut AllocT) -> Self;
51 fn free(&mut self, m: &mut AllocT);
52}
53pub trait H10Params {
54 fn max_tree_search_depth() -> u32;
55 fn max_tree_comp_length() -> u32;
56}
57
58pub struct H10DefaultParams {}
59impl H10Params for H10DefaultParams {
60 #[inline(always)]
61 fn max_tree_search_depth() -> u32 {
62 64
63 }
64 #[inline(always)]
65 fn max_tree_comp_length() -> u32 {
66 128
67 }
68}
69
70const BUCKET_BITS: usize = 17;
71
72pub struct H10Buckets<AllocU32: Allocator<u32>>(AllocU32::AllocatedMemory);
73
74impl<AllocU32: Allocator<u32>> Allocable<u32, AllocU32> for H10Buckets<AllocU32> {
75 fn new(m: &mut AllocU32, initializer: u32) -> H10Buckets<AllocU32> {
76 let mut ret = m.alloc_cell(1 << BUCKET_BITS);
77 for item in ret.slice_mut().iter_mut() {
78 *item = initializer;
79 }
80 H10Buckets::<AllocU32>(ret)
81 }
82 fn new_uninit(m: &mut AllocU32) -> H10Buckets<AllocU32> {
83 H10Buckets::<AllocU32>(m.alloc_cell(1 << BUCKET_BITS))
84 }
85 fn free(&mut self, m: &mut AllocU32) {
86 m.free_cell(core::mem::take(&mut self.0));
87 }
88}
89
90impl<AllocU32: Allocator<u32>> PartialEq<H10Buckets<AllocU32>> for H10Buckets<AllocU32> {
91 fn eq(&self, other: &H10Buckets<AllocU32>) -> bool {
92 return self.0.slice() == other.0.slice();
93 }
94}
95
96impl<AllocU32: Allocator<u32>> SliceWrapper<u32> for H10Buckets<AllocU32> {
97 #[inline(always)]
98 fn slice(&self) -> &[u32] {
99 self.0.slice()
100 }
101}
102impl<AllocU32: Allocator<u32>> SliceWrapperMut<u32> for H10Buckets<AllocU32> {
103 #[inline(always)]
104 fn slice_mut(&mut self) -> &mut [u32] {
105 self.0.slice_mut()
106 }
107}
108
109pub struct H10<
110 AllocU32: Allocator<u32>,
111 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
112 Params: H10Params,
113> where
114 Buckets: PartialEq<Buckets>,
115{
116 pub window_mask_: usize,
117 pub ringbuffer_break: Option<core::num::NonZeroUsize>,
118 pub common: Struct1,
119 pub buckets_: Buckets,
120 pub invalid_pos_: u32,
121 pub forest: AllocU32::AllocatedMemory,
122 pub _params: core::marker::PhantomData<Params>,
123}
124
125impl<
126 AllocU32: Allocator<u32>,
127 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
128 Params: H10Params,
129> PartialEq<H10<AllocU32, Buckets, Params>> for H10<AllocU32, Buckets, Params>
130where
131 Buckets: PartialEq<Buckets>,
132{
133 fn eq(&self, other: &H10<AllocU32, Buckets, Params>) -> bool {
134 self.window_mask_ == other.window_mask_
135 && self.common == other.common
136 && self.buckets_ == other.buckets_
137 && self.invalid_pos_ == other.invalid_pos_
138 && self.forest.slice() == other.forest.slice()
139 && self._params == other._params
140 && self.ringbuffer_break == other.ringbuffer_break
141 }
142}
143
144pub fn InitializeH10<AllocU32: Allocator<u32>>(
145 m32: &mut AllocU32,
146 one_shot: bool,
147 params: &BrotliEncoderParams,
148 ringbuffer_break: Option<core::num::NonZeroUsize>,
149 input_size: usize,
150) -> H10<AllocU32, H10Buckets<AllocU32>, H10DefaultParams> {
151 initialize_h10::<AllocU32, H10Buckets<AllocU32>>(
152 m32,
153 one_shot,
154 params,
155 input_size,
156 ringbuffer_break,
157 )
158}
159fn initialize_h10<
160 AllocU32: Allocator<u32>,
161 Buckets: SliceWrapperMut<u32> + SliceWrapper<u32> + Allocable<u32, AllocU32>,
162>(
163 m32: &mut AllocU32,
164 one_shot: bool,
165 params: &BrotliEncoderParams,
166 input_size: usize,
167 ringbuffer_break: Option<core::num::NonZeroUsize>,
168) -> H10<AllocU32, Buckets, H10DefaultParams>
169where
170 Buckets: PartialEq<Buckets>,
171{
172 let mut num_nodes = 1 << params.lgwin;
173 if one_shot && input_size < num_nodes {
174 num_nodes = input_size;
175 }
176 let window_mask = (1 << params.lgwin) - 1;
177 let invalid_pos = 0u32.wrapping_sub(window_mask);
178 let buckets = <Buckets as Allocable<u32, AllocU32>>::new(m32, invalid_pos);
179 H10::<AllocU32, Buckets, H10DefaultParams> {
180 common: Struct1 {
181 params: params.hasher,
182 is_prepared_: 1,
183 dict_num_lookups: 0,
184 dict_num_matches: 0,
185 },
186 _params: core::marker::PhantomData::<H10DefaultParams>,
187 window_mask_: window_mask as usize,
188 invalid_pos_: invalid_pos,
189 buckets_: buckets,
190 forest: m32.alloc_cell(num_nodes * 2),
191 ringbuffer_break,
192 }
193}
194
195impl<
196 AllocU32: Allocator<u32>,
197 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
198 Params: H10Params,
199> H10<AllocU32, Buckets, Params>
200where
201 Buckets: PartialEq<Buckets>,
202{
203 pub fn free(&mut self, m32: &mut AllocU32) {
204 m32.free_cell(core::mem::take(&mut self.forest));
205 self.buckets_.free(m32);
206 }
207
208 #[inline(always)]
210 fn store_simd<S: Simd>(&mut self, simd: S, data: &[u8], mask: usize, ix: usize) {
211 let max_backward: usize = self.window_mask_.wrapping_sub(16).wrapping_add(1);
212 StoreAndFindMatchesH10Simd(
213 simd,
214 self,
215 data,
216 ix,
217 mask,
218 self.ringbuffer_break,
219 Params::max_tree_comp_length() as usize,
220 max_backward,
221 &mut 0,
222 &mut [],
223 );
224 }
225
226 #[inline(always)]
229 pub(crate) fn store_range_simd<S: Simd>(
230 &mut self,
231 simd: S,
232 data: &[u8],
233 mask: usize,
234 ix_start: usize,
235 ix_end: usize,
236 ) {
237 let mut i: usize = ix_start;
238 let mut j: usize = ix_start;
239 if ix_start.wrapping_add(63) <= ix_end {
240 i = ix_end.wrapping_sub(63);
241 }
242 if ix_start.wrapping_add(512) <= i {
243 while j < i {
244 {
245 self.store_simd(simd, data, mask, j);
246 }
247 j = j.wrapping_add(8);
248 }
249 }
250 while i < ix_end {
251 {
252 self.store_simd(simd, data, mask, i);
253 }
254 i = i.wrapping_add(1);
255 }
256 }
257
258 #[inline(always)]
260 fn bulk_store_range_simd<S: Simd>(
261 &mut self,
262 simd: S,
263 data: &[u8],
264 mask: usize,
265 ix_start: usize,
266 ix_end: usize,
267 ) {
268 for i in ix_start..ix_end {
269 self.store_simd(simd, data, mask, i);
270 }
271 }
272}
273impl<
274 Alloc: Allocator<u16> + Allocator<u32>,
275 Buckets: Allocable<u32, Alloc> + SliceWrapperMut<u32> + SliceWrapper<u32>,
276 Params: H10Params,
277> CloneWithAlloc<Alloc> for H10<Alloc, Buckets, Params>
278where
279 Buckets: PartialEq<Buckets>,
280{
281 fn clone_with_alloc(&self, m: &mut Alloc) -> Self {
282 let mut ret = H10::<Alloc, Buckets, Params> {
283 window_mask_: self.window_mask_,
284 common: self.common.clone(),
285 buckets_: Buckets::new_uninit(m),
286 invalid_pos_: self.invalid_pos_,
287 forest: allocate::<u32, _>(m, self.forest.len()),
288 _params: core::marker::PhantomData::<Params>,
289 ringbuffer_break: self.ringbuffer_break,
290 };
291 ret.buckets_
292 .slice_mut()
293 .copy_from_slice(self.buckets_.slice());
294 ret.forest.slice_mut().copy_from_slice(self.forest.slice());
295 ret
296 }
297}
298
299impl<
300 AllocU32: Allocator<u32>,
301 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
302 Params: H10Params,
303> AnyHasher for H10<AllocU32, Buckets, Params>
304where
305 Buckets: PartialEq<Buckets>,
306{
307 #[inline(always)]
311 fn Opts(&self) -> H9Opts {
312 H9Opts {
313 literal_byte_score: 340,
314 }
315 }
316 #[inline(always)]
317 fn PrepareDistanceCache(&self, _distance_cache: &mut [i32]) {}
318 #[inline(always)]
319 fn HashTypeLength(&self) -> usize {
320 4
321 }
322 #[inline(always)]
323 fn StoreLookahead(&self) -> usize {
324 Params::max_tree_comp_length() as usize
325 }
326 fn StitchToPreviousBlock(
327 &mut self,
328 num_bytes: usize,
329 position: usize,
330 ringbuffer: &[u8],
331 ringbuffer_mask: usize,
332 ) {
333 super::hq::StitchToPreviousBlockH10(
334 self,
335 num_bytes,
336 position,
337 ringbuffer,
338 ringbuffer_mask,
339 self.ringbuffer_break,
340 )
341 }
342 #[inline(always)]
343 fn GetHasherCommon(&mut self) -> &mut Struct1 {
344 &mut self.common
345 }
346 #[inline(always)]
347 fn HashBytes(&self, data: &[u8]) -> usize {
348 let h = BROTLI_UNALIGNED_LOAD32(data).wrapping_mul(kHashMul32);
349 (h >> (32i32 - BUCKET_BITS as i32)) as usize
350 }
351 #[inline(always)]
352 fn Store(&mut self, data: &[u8], mask: usize, ix: usize) {
353 dispatch!(detect_level(), simd => self.store_simd(simd, data, mask, ix))
354 }
355 fn StoreRange(&mut self, data: &[u8], mask: usize, ix_start: usize, ix_end: usize) {
356 dispatch!(detect_level(), simd => self.store_range_simd(simd, data, mask, ix_start, ix_end))
357 }
358 fn BulkStoreRange(&mut self, data: &[u8], mask: usize, ix_start: usize, ix_end: usize) {
359 dispatch!(detect_level(), simd => self.bulk_store_range_simd(simd, data, mask, ix_start, ix_end))
360 }
361 fn Prepare(&mut self, _one_shot: bool, _input_size: usize, _data: &[u8]) -> HowPrepared {
362 if self.common.is_prepared_ != 0 {
363 return HowPrepared::ALREADY_PREPARED;
364 }
365 let invalid_pos = self.invalid_pos_;
366 for bucket in self.buckets_.slice_mut().iter_mut() {
367 *bucket = invalid_pos;
368 }
369 self.common.is_prepared_ = 1;
370 HowPrepared::NEWLY_PREPARED
371 }
372
373 fn FindLongestMatch(
374 &mut self,
375 _dictionary: Option<&BrotliDictionary>,
376 _dictionary_hash: &[u16],
377 _data: &[u8],
378 _ring_buffer_mask: usize,
379 _ring_buffer_break: Option<core::num::NonZeroUsize>,
380 _distance_cache: &[i32],
381 _cur_ix: usize,
382 _max_length: usize,
383 _max_backward: usize,
384 _gap: usize,
385 _max_distance: usize,
386 _out: &mut HasherSearchResult,
387 ) -> bool {
388 unimplemented!();
389 }
390}
391
392pub struct BackwardMatch(pub u64);
393
394impl BackwardMatch {
397 #[inline(always)]
398 pub fn distance(&self) -> u32 {
399 self.0 as u32
400 }
401 #[inline(always)]
402 pub fn length_and_code(&self) -> u32 {
403 (self.0 >> 32) as u32
404 }
405}
406pub struct BackwardMatchMut<'a>(pub &'a mut u64);
407
408impl<'a> BackwardMatchMut<'a> {
411 #[inline(always)]
412 pub fn distance(&self) -> u32 {
413 *self.0 as u32
414 }
415 #[inline(always)]
416 pub fn length_and_code(&self) -> u32 {
417 (*self.0 >> 32) as u32
418 }
419 #[inline(always)]
420 pub fn set_distance(&mut self, data: u32) {
421 *self.0 &= 0xffffffff00000000;
422 *self.0 |= u64::from(data)
423 }
424 #[inline(always)]
425 pub fn set_length_and_code(&mut self, data: u32) {
426 *self.0 = u64::from((*self.0) as u32) | (u64::from(data) << 32);
427 }
428 #[inline(always)]
429 pub fn init(&mut self, dist: usize, len: usize) {
430 self.set_distance(dist as u32);
431 self.set_length_and_code((len << 5) as u32);
432 }
433 #[inline(always)]
434 pub(crate) fn init_dictionary(&mut self, dist: usize, len: usize, len_code: usize) {
435 self.set_distance(dist as u32);
436 self.set_length_and_code((len << 5 | if len == len_code { 0 } else { len_code }) as u32);
437 }
438}
439
440macro_rules! LeftChildIndexH10 {
441 ($xself: expr_2021, $pos: expr_2021) => {
442 (2usize).wrapping_mul($pos & (*$xself).window_mask_)
443 };
444}
445macro_rules! RightChildIndexH10 {
446 ($xself: expr_2021, $pos: expr_2021) => {
447 (2usize)
448 .wrapping_mul($pos & (*$xself).window_mask_)
449 .wrapping_add(1)
450 };
451}
452#[allow(clippy::too_many_arguments)]
478pub fn StoreAndFindMatchesH10<
479 AllocU32: Allocator<u32>,
480 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32> + PartialEq<Buckets>,
481 Params: H10Params,
482>(
483 xself: &mut H10<AllocU32, Buckets, Params>,
484 data: &[u8],
485 cur_ix: usize,
486 ring_buffer_mask: usize,
487 ringbuffer_break: Option<core::num::NonZeroUsize>,
488 max_length: usize,
489 max_backward: usize,
490 best_len: &mut usize,
491 matches: &mut [u64],
492) -> usize {
493 dispatch!(detect_level(), simd => StoreAndFindMatchesH10Simd(
494 simd,
495 xself,
496 data,
497 cur_ix,
498 ring_buffer_mask,
499 ringbuffer_break,
500 max_length,
501 max_backward,
502 best_len,
503 matches,
504 ))
505}
506
507#[inline(always)]
513#[allow(clippy::too_many_arguments)]
514pub fn StoreAndFindMatchesH10Simd<
515 S: Simd,
516 AllocU32: Allocator<u32>,
517 Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32> + PartialEq<Buckets>,
518 Params: H10Params,
519>(
520 simd: S,
521 xself: &mut H10<AllocU32, Buckets, Params>,
522 data: &[u8],
523 cur_ix: usize,
524 ring_buffer_mask: usize,
525 ringbuffer_break: Option<core::num::NonZeroUsize>,
526 max_length: usize,
527 max_backward: usize,
528 best_len: &mut usize,
529 matches: &mut [u64],
530) -> usize {
531 let mut matches_offset = 0_usize;
532 let cur_ix_masked = cur_ix & ring_buffer_mask;
533 let max_comp_len = min(max_length, 128);
534 let should_reroot_tree = max_length >= 128;
535 let key = xself.HashBytes(&data[cur_ix_masked..]);
536 let forest = xself.forest.slice_mut();
537 let mut prev_ix = xself.buckets_.slice()[key] as usize;
538 let mut node_left = LeftChildIndexH10!(xself, cur_ix);
539 let mut node_right = RightChildIndexH10!(xself, cur_ix);
540 let mut best_len_left = 0_usize;
541 let mut best_len_right = 0_usize;
542 let mut depth_remaining = 64_usize;
543
544 if should_reroot_tree {
545 xself.buckets_.slice_mut()[key] = cur_ix as u32;
546 }
547
548 loop {
549 let backward = cur_ix.wrapping_sub(prev_ix);
550 let prev_ix_masked = prev_ix & ring_buffer_mask;
551 if backward == 0 || backward > max_backward || depth_remaining == 0 {
552 if should_reroot_tree {
553 forest[node_left] = xself.invalid_pos_;
554 forest[node_right] = xself.invalid_pos_;
555 }
556 break;
557 }
558
559 let cur_len = min(best_len_left, best_len_right);
560
561 let len = fix_unbroken_len(
562 cur_len.wrapping_add(FindMatchLengthWithLimitSimd(
563 simd,
564 &data[cur_ix_masked.wrapping_add(cur_len)..],
565 &data[prev_ix_masked.wrapping_add(cur_len)..],
566 max_length.wrapping_sub(cur_len),
567 )),
568 prev_ix_masked,
569 cur_ix_masked,
570 ringbuffer_break,
571 );
572
573 if matches_offset != matches.len() && len > *best_len {
574 *best_len = len;
575 BackwardMatchMut(&mut matches[matches_offset]).init(backward, len);
576 matches_offset += 1;
577 }
578
579 if len >= max_comp_len {
580 if should_reroot_tree {
581 forest[node_left] = forest[LeftChildIndexH10!(xself, prev_ix)];
582 forest[node_right] = forest[RightChildIndexH10!(xself, prev_ix)];
583 }
584 break;
585 }
586
587 if data[cur_ix_masked.wrapping_add(len)] > data[prev_ix_masked.wrapping_add(len)] {
588 best_len_left = len;
589 if should_reroot_tree {
590 forest[node_left] = prev_ix as u32;
591 }
592 node_left = RightChildIndexH10!(xself, prev_ix);
593 prev_ix = forest[node_left] as usize;
594 } else {
595 best_len_right = len;
596 if should_reroot_tree {
597 forest[node_right] = prev_ix as u32;
598 }
599 node_right = LeftChildIndexH10!(xself, prev_ix);
600 prev_ix = forest[node_right] as usize;
601 }
602
603 depth_remaining = depth_remaining.wrapping_sub(1);
604 }
605
606 matches_offset
607}