Skip to main content

simd_brotli/enc/backward_references/
hash_to_binary_tree.rs

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    //highest 7 bit is used to reconstruct the length code
30    pub length: u32,
31    // distance associated with the length
32    pub distance: u32,
33    // number of literal inserts before the copy; highest 5 bits contain distance short code + 1 (or zero if no short code)
34    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    /// `AnyHasher::Store` on an already-detected instruction set.
209    #[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    /// `AnyHasher::StoreRange` on an already-detected instruction set, so the walk over
227    /// `ix_start..ix_end` pays for detection once rather than once per position.
228    #[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    /// `AnyHasher::BulkStoreRange` on an already-detected instruction set.
259    #[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    /*  fn GetH10Tree(&mut self) -> Option<&mut H10<AllocU32, Buckets, H10Params>> {
308      Some(self)
309    }*/
310    #[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
394//    pub distance : u32,
395//    pub length_and_code : u32,
396impl 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
408//    pub distance : u32,
409//    pub length_and_code : u32,
410impl<'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/*
453fn LeftChildIndexH10<AllocU32: Allocator<u32>,
454     Buckets: Allocable<u32, AllocU32>+SliceWrapperMut<u32>+SliceWrapper<u32>,
455     Params:H10Params>(
456    mut xself : &mut H10<AllocU32, Buckets, Params>, pos : usize
457) -> usize {
458    (2usize).wrapping_mul(pos & xself.window_mask_)
459}
460
461fn RightChildIndexH10<AllocU32: Allocator<u32>,
462     Buckets: Allocable<u32, AllocU32>+SliceWrapperMut<u32>+SliceWrapper<u32>,
463     Params:H10Params>(
464    mut xself : &mut H10<AllocU32, Buckets, Params>, pos : usize
465) -> usize {
466    (2usize).wrapping_mul(
467        pos & xself.window_mask_
468    ).wrapping_add(
469        1
470    )
471}
472*/
473
474/// Detects the instruction set per call, then runs [`StoreAndFindMatchesH10Simd`].
475///
476/// Callers walking a range of positions should detect once and call that directly.
477#[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/// Walks the binary tree rooted at `cur_ix`'s hash bucket, recording matches and
508/// re-rooting the tree at the current position.
509///
510/// The tree walk measures a match length per node, up to 64 of them, which is why this
511/// takes an already-detected instruction set rather than probing per comparison.
512#[inline(always)]
513#[allow(clippy::too_many_arguments)]
514#[cfg_attr(feature = "hotpath", hotpath::measure)]
515pub fn StoreAndFindMatchesH10Simd<
516    S: Simd,
517    AllocU32: Allocator<u32>,
518    Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32> + PartialEq<Buckets>,
519    Params: H10Params,
520>(
521    simd: S,
522    xself: &mut H10<AllocU32, Buckets, Params>,
523    data: &[u8],
524    cur_ix: usize,
525    ring_buffer_mask: usize,
526    ringbuffer_break: Option<core::num::NonZeroUsize>,
527    max_length: usize,
528    max_backward: usize,
529    best_len: &mut usize,
530    matches: &mut [u64],
531) -> usize {
532    let mut matches_offset = 0_usize;
533    let cur_ix_masked = cur_ix & ring_buffer_mask;
534    let max_comp_len = min(max_length, 128);
535    let should_reroot_tree = max_length >= 128;
536    let key = xself.HashBytes(&data[cur_ix_masked..]);
537    let forest = xself.forest.slice_mut();
538    let mut prev_ix = xself.buckets_.slice()[key] as usize;
539    let mut node_left = LeftChildIndexH10!(xself, cur_ix);
540    let mut node_right = RightChildIndexH10!(xself, cur_ix);
541    let mut best_len_left = 0_usize;
542    let mut best_len_right = 0_usize;
543    let mut depth_remaining = 64_usize;
544
545    if should_reroot_tree {
546        xself.buckets_.slice_mut()[key] = cur_ix as u32;
547    }
548
549    loop {
550        let backward = cur_ix.wrapping_sub(prev_ix);
551        let prev_ix_masked = prev_ix & ring_buffer_mask;
552        if backward == 0 || backward > max_backward || depth_remaining == 0 {
553            if should_reroot_tree {
554                forest[node_left] = xself.invalid_pos_;
555                forest[node_right] = xself.invalid_pos_;
556            }
557            break;
558        }
559
560        let cur_len = min(best_len_left, best_len_right);
561
562        let len = fix_unbroken_len(
563            cur_len.wrapping_add(FindMatchLengthWithLimitSimd(
564                simd,
565                &data[cur_ix_masked.wrapping_add(cur_len)..],
566                &data[prev_ix_masked.wrapping_add(cur_len)..],
567                max_length.wrapping_sub(cur_len),
568            )),
569            prev_ix_masked,
570            cur_ix_masked,
571            ringbuffer_break,
572        );
573
574        if matches_offset != matches.len() && len > *best_len {
575            *best_len = len;
576            BackwardMatchMut(&mut matches[matches_offset]).init(backward, len);
577            matches_offset += 1;
578        }
579
580        if len >= max_comp_len {
581            if should_reroot_tree {
582                forest[node_left] = forest[LeftChildIndexH10!(xself, prev_ix)];
583                forest[node_right] = forest[RightChildIndexH10!(xself, prev_ix)];
584            }
585            break;
586        }
587
588        if data[cur_ix_masked.wrapping_add(len)] > data[prev_ix_masked.wrapping_add(len)] {
589            best_len_left = len;
590            if should_reroot_tree {
591                forest[node_left] = prev_ix as u32;
592            }
593            node_left = RightChildIndexH10!(xself, prev_ix);
594            prev_ix = forest[node_left] as usize;
595        } else {
596            best_len_right = len;
597            if should_reroot_tree {
598                forest[node_right] = prev_ix as u32;
599            }
600            node_right = LeftChildIndexH10!(xself, prev_ix);
601            prev_ix = forest[node_right] as usize;
602        }
603
604        depth_remaining = depth_remaining.wrapping_sub(1);
605    }
606
607    matches_offset
608}