Skip to main content

brotli/enc/backward_references/
hq.rs

1use alloc::{Allocator, SliceWrapper, SliceWrapperMut};
2use core;
3use core::cmp::{max, min};
4
5use super::hash_to_binary_tree::{
6    kInfinity, Allocable, BackwardMatch, BackwardMatchMut, H10Params, StoreAndFindMatchesH10,
7    Union1, ZopfliNode, H10,
8};
9use super::{
10    fix_unbroken_len, kDistanceCacheIndex, kDistanceCacheOffset, kInvalidMatch, AnyHasher,
11    BrotliEncoderParams,
12};
13use crate::enc::combined_alloc::{alloc_if, alloc_or_default};
14use crate::enc::command::{
15    combine_length_codes, BrotliDistanceParams, Command, GetCopyLengthCode, GetInsertLengthCode,
16    PrefixEncodeCopyDistance,
17};
18use crate::enc::constants::{kCopyExtra, kInsExtra};
19use crate::enc::encode;
20use crate::enc::literal_cost::BrotliEstimateBitCostsForLiterals;
21use crate::enc::static_dict::{
22    BrotliDictionary, BrotliFindAllStaticDictionaryMatches, FindMatchLengthWithLimit,
23};
24use crate::enc::util::{floatX, FastLog2, FastLog2f64};
25
26const BROTLI_WINDOW_GAP: usize = 16;
27const BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN: usize = 37;
28
29/*
30static kBrotliMinWindowBits: i32 = 10i32;
31
32static kBrotliMaxWindowBits: i32 = 24i32;
33
34static kInvalidMatch: u32 = 0xfffffffu32;
35
36static kCutoffTransformsCount: u32 = 10u32;
37
38static kCutoffTransforms: u64 = 0x71b520au64 << 32 | 0xda2d3200u32 as (u64);
39
40pub static kHashMul32: u32 = 0x1e35a7bdu32;
41
42pub static kHashMul64: u64 = 0x1e35a7bdu64 << 32 | 0x1e35a7bdu64;
43
44pub static kHashMul64Long: u64 = 0x1fe35a7bu32 as (u64) << 32 | 0xd3579bd3u32 as (u64);
45
46*/
47pub const BROTLI_MAX_EFFECTIVE_DISTANCE_ALPHABET_SIZE: usize = 544;
48pub const BROTLI_NUM_LITERAL_SYMBOLS: usize = 256;
49pub const BROTLI_NUM_COMMAND_SYMBOLS: usize = 704;
50
51pub const BROTLI_SIMPLE_DISTANCE_ALPHABET_SIZE: usize = encode::BROTLI_NUM_DISTANCE_SHORT_CODES
52    as usize
53    + (2 * encode::BROTLI_LARGE_MAX_DISTANCE_BITS as usize);
54
55const STORE_LOOKAHEAD_H_10: usize = 128;
56
57#[inline(always)]
58pub fn BrotliInitZopfliNodes(array: &mut [ZopfliNode], length: usize) {
59    let stub = ZopfliNode::default();
60    let mut i: usize;
61    i = 0usize;
62    while i < length {
63        array[i] = stub;
64        i = i.wrapping_add(1);
65    }
66}
67
68impl ZopfliNode {
69    #[inline(always)]
70    fn copy_length(&self) -> u32 {
71        self.length & 0x01ff_ffff
72    }
73
74    #[inline(always)]
75    fn copy_distance(&self) -> u32 {
76        self.distance
77    }
78
79    #[inline(always)]
80    fn length_code(&self) -> u32 {
81        self.copy_length()
82            .wrapping_add(9)
83            .wrapping_sub(self.length >> 25)
84    }
85
86    #[inline(always)]
87    fn distance_code(&self) -> u32 {
88        let short_code: u32 = self.dcode_insert_length >> 27;
89        if short_code == 0u32 {
90            self.copy_distance().wrapping_add(16).wrapping_sub(1)
91        } else {
92            short_code.wrapping_sub(1)
93        }
94    }
95}
96
97pub fn BrotliZopfliCreateCommands(
98    num_bytes: usize,
99    block_start: usize,
100    max_backward_limit: usize,
101    nodes: &[ZopfliNode],
102    dist_cache: &mut [i32],
103    last_insert_len: &mut usize,
104    params: &BrotliEncoderParams,
105    commands: &mut [Command],
106    num_literals: &mut usize,
107) {
108    let mut pos: usize = 0usize;
109    let mut offset: u32 = match (nodes[0]).u {
110        Union1::next(off) => off,
111        _ => 0,
112    };
113    let mut i: usize;
114    let gap: usize = 0usize;
115    i = 0usize;
116    while offset != !(0u32) {
117        {
118            let next: &ZopfliNode = &nodes[pos.wrapping_add(offset as usize)];
119            let copy_length = next.copy_length() as usize;
120            let mut insert_length: usize = (next.dcode_insert_length & 0x07ff_ffff) as usize;
121            pos = pos.wrapping_add(insert_length);
122            offset = match next.u {
123                Union1::next(off) => off,
124                _ => 0,
125            };
126            if i == 0usize {
127                insert_length = insert_length.wrapping_add(*last_insert_len);
128                *last_insert_len = 0usize;
129            }
130            {
131                let distance: usize = next.copy_distance() as usize;
132                let len_code: usize = next.length_code() as usize;
133                let max_distance: usize = min(block_start.wrapping_add(pos), max_backward_limit);
134                let is_dictionary = distance > max_distance.wrapping_add(gap);
135                let dist_code: usize = next.distance_code() as usize;
136                commands[i].init(
137                    &params.dist,
138                    insert_length,
139                    copy_length,
140                    len_code,
141                    dist_code,
142                );
143                if !is_dictionary && dist_code > 0 {
144                    dist_cache[3] = dist_cache[2];
145                    dist_cache[2] = dist_cache[1];
146                    dist_cache[1] = dist_cache[0];
147                    dist_cache[0] = distance as i32;
148                }
149            }
150            *num_literals = num_literals.wrapping_add(insert_length);
151            pos = pos.wrapping_add(copy_length);
152        }
153        i = i.wrapping_add(1);
154    }
155    *last_insert_len = last_insert_len.wrapping_add(num_bytes.wrapping_sub(pos));
156}
157
158#[inline(always)]
159fn MaxZopfliLen(params: &BrotliEncoderParams) -> usize {
160    (if params.quality <= 10i32 {
161        150i32
162    } else {
163        325i32
164    }) as usize
165}
166
167pub struct ZopfliCostModel<AllocF: Allocator<floatX>> {
168    pub cost_cmd_: [floatX; BROTLI_NUM_COMMAND_SYMBOLS],
169    pub cost_dist_: AllocF::AllocatedMemory,
170    pub distance_histogram_size: u32,
171    pub literal_costs_: AllocF::AllocatedMemory,
172    pub min_cost_cmd_: floatX,
173    pub num_bytes_: usize,
174}
175
176#[derive(Copy, Clone, Debug)]
177pub struct PosData {
178    pub pos: usize,
179    pub distance_cache: [i32; 4],
180    pub costdiff: floatX,
181    pub cost: floatX,
182}
183
184#[derive(Copy, Clone, Debug)]
185pub struct StartPosQueue {
186    pub q_: [PosData; 8],
187    pub idx_: usize,
188}
189impl Default for StartPosQueue {
190    #[inline(always)]
191    fn default() -> Self {
192        StartPosQueue {
193            q_: [PosData {
194                pos: 0,
195                distance_cache: [0; 4],
196                costdiff: 0.0,
197                cost: 0.0,
198            }; 8],
199            idx_: 0,
200        }
201    }
202}
203
204impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
205    fn init(m: &mut AllocF, dist: &BrotliDistanceParams, num_bytes: usize) -> Self {
206        Self {
207            num_bytes_: num_bytes,
208            cost_cmd_: [0.0; 704],
209            min_cost_cmd_: 0.0,
210            // FIXME: makes little sense to test if N+2 > 0 -- always true unless wrapping. Perhaps use allocate() instead?
211            literal_costs_: alloc_or_default::<floatX, _>(m, num_bytes + 2),
212            // FIXME: possible bug because allocation size is different from the condition
213            cost_dist_: alloc_if::<floatX, _>(
214                dist.alphabet_size > 0,
215                m,
216                num_bytes + dist.alphabet_size as usize,
217            ),
218            distance_histogram_size: min(dist.alphabet_size, 544),
219        }
220    }
221
222    fn set_from_literal_costs(
223        &mut self,
224        position: usize,
225        ringbuffer: &[u8],
226        ringbuffer_mask: usize,
227    ) {
228        let literal_costs = self.literal_costs_.slice_mut();
229        let mut literal_carry: floatX = 0.0;
230        let cost_dist = self.cost_dist_.slice_mut();
231        let cost_cmd = &mut self.cost_cmd_[..];
232        let num_bytes: usize = self.num_bytes_;
233        BrotliEstimateBitCostsForLiterals(
234            position,
235            num_bytes,
236            ringbuffer_mask,
237            ringbuffer,
238            &mut literal_costs[1..],
239        );
240        literal_costs[0] = 0.0 as (floatX);
241        for i in 0usize..num_bytes {
242            literal_carry = literal_carry + literal_costs[i.wrapping_add(1)];
243            literal_costs[i.wrapping_add(1)] = literal_costs[i] + literal_carry;
244            literal_carry -= literal_costs[i.wrapping_add(1)] - literal_costs[i];
245        }
246        for i in 0..BROTLI_NUM_COMMAND_SYMBOLS {
247            cost_cmd[i] = FastLog2(11 + i as u64);
248        }
249        for i in 0usize..self.distance_histogram_size as usize {
250            cost_dist[i] = FastLog2((20u64).wrapping_add(i as (u64))) as (floatX);
251        }
252        self.min_cost_cmd_ = FastLog2(11) as (floatX);
253    }
254}
255
256pub fn StitchToPreviousBlockH10<
257    AllocU32: Allocator<u32>,
258    Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
259    Params: H10Params,
260>(
261    handle: &mut H10<AllocU32, Buckets, Params>,
262    num_bytes: usize,
263    position: usize,
264    ringbuffer: &[u8],
265    ringbuffer_mask: usize,
266    ringbuffer_break: Option<core::num::NonZeroUsize>,
267) where
268    Buckets: PartialEq<Buckets>,
269{
270    if (num_bytes >= handle.HashTypeLength() - 1
271        && position >= Params::max_tree_comp_length() as usize)
272    {
273        /* Store the last `MAX_TREE_COMP_LENGTH - 1` positions in the hasher.
274        These could not be calculated before, since they require knowledge
275        of both the previous and the current block. */
276        let i_start = position - Params::max_tree_comp_length() as usize;
277        let i_end = min(position, i_start.wrapping_add(num_bytes));
278        for i in i_start..i_end {
279            /* Maximum distance is window size - 16, see section 9.1. of the spec.
280            Furthermore, we have to make sure that we don't look further back
281            from the start of the next block than the window size, otherwise we
282            could access already overwritten areas of the ring-buffer. */
283            let max_backward = handle.window_mask_ - max(BROTLI_WINDOW_GAP - 1, position - i);
284            let mut _best_len = 0;
285            /* We know that i + MAX_TREE_COMP_LENGTH <= position + num_bytes, i.e. the
286            end of the current block and that we have at least
287            MAX_TREE_COMP_LENGTH tail in the ring-buffer. */
288            StoreAndFindMatchesH10(
289                handle,
290                ringbuffer,
291                i,
292                ringbuffer_mask,
293                ringbuffer_break,
294                <Params as H10Params>::max_tree_comp_length() as usize,
295                max_backward,
296                &mut _best_len,
297                &mut [],
298            );
299        }
300    }
301}
302fn FindAllMatchesH10<
303    AllocU32: Allocator<u32>,
304    Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
305    Params: H10Params,
306>(
307    handle: &mut H10<AllocU32, Buckets, Params>,
308    dictionary: Option<&BrotliDictionary>,
309    data: &[u8],
310    ring_buffer_mask: usize,
311    ring_buffer_break: Option<core::num::NonZeroUsize>,
312    cur_ix: usize,
313    max_length: usize,
314    max_backward: usize,
315    gap: usize,
316    params: &BrotliEncoderParams,
317    matches: &mut [u64],
318) -> usize
319where
320    Buckets: PartialEq<Buckets>,
321{
322    let mut matches_offset = 0usize;
323    let cur_ix_masked: usize = cur_ix & ring_buffer_mask;
324    let mut best_len: usize = 1usize;
325    let short_match_max_backward: usize = (if params.quality != 11i32 {
326        16i32
327    } else {
328        64i32
329    }) as usize;
330    let mut stop: usize = cur_ix.wrapping_sub(short_match_max_backward);
331    let mut dict_matches = [kInvalidMatch; BROTLI_MAX_STATIC_DICTIONARY_MATCH_LEN + 1];
332    let mut i: usize;
333    if cur_ix < short_match_max_backward {
334        stop = 0usize;
335    }
336    i = cur_ix.wrapping_sub(1);
337    while i > stop && best_len <= 2 {
338        let mut prev_ix: usize = i;
339        let backward: usize = cur_ix.wrapping_sub(prev_ix);
340        if backward > max_backward {
341            break;
342        }
343        prev_ix &= ring_buffer_mask;
344        if data[cur_ix_masked] == data[prev_ix]
345            && data[cur_ix_masked.wrapping_add(1)] == data[prev_ix.wrapping_add(1)]
346        {
347            let len =
348                FindMatchLengthWithLimit(&data[prev_ix..], &data[cur_ix_masked..], max_length);
349            if len > best_len {
350                best_len = len;
351                BackwardMatchMut(&mut matches[matches_offset]).init(backward, len);
352                matches_offset += 1;
353            }
354        }
355        i = i.wrapping_sub(1);
356    }
357    if best_len < max_length {
358        let loc_offset = StoreAndFindMatchesH10(
359            handle,
360            data,
361            cur_ix,
362            ring_buffer_mask,
363            ring_buffer_break,
364            max_length,
365            max_backward,
366            &mut best_len,
367            matches.split_at_mut(matches_offset).1,
368        );
369        matches_offset += loc_offset;
370    }
371    for i in 0..=37 {
372        dict_matches[i] = kInvalidMatch
373    }
374    {
375        let minlen = max(4, best_len.wrapping_add(1));
376        if dictionary.is_some()
377            && BrotliFindAllStaticDictionaryMatches(
378                dictionary.unwrap(),
379                &data[cur_ix_masked..],
380                minlen,
381                max_length,
382                &mut dict_matches[..],
383            ) != 0
384        {
385            assert!(params.use_dictionary);
386            let maxlen = min(37, max_length);
387            for l in minlen..=maxlen {
388                let dict_id: u32 = dict_matches[l];
389                if dict_id < kInvalidMatch {
390                    let distance: usize = max_backward
391                        .wrapping_add(gap)
392                        .wrapping_add((dict_id >> 5) as usize)
393                        .wrapping_add(1);
394                    if distance <= params.dist.max_distance {
395                        BackwardMatchMut(&mut matches[matches_offset]).init_dictionary(
396                            distance,
397                            l,
398                            (dict_id & 31u32) as usize,
399                        );
400                        matches_offset += 1;
401                    }
402                }
403            }
404        }
405    }
406    matches_offset
407}
408
409impl BackwardMatch {
410    #[inline(always)]
411    fn length(&self) -> usize {
412        (self.length_and_code() >> 5) as usize
413    }
414}
415
416#[inline(always)]
417fn MaxZopfliCandidates(params: &BrotliEncoderParams) -> usize {
418    (if params.quality <= 10i32 { 1i32 } else { 5i32 }) as usize
419}
420
421#[inline(always)]
422fn ComputeDistanceShortcut(
423    block_start: usize,
424    pos: usize,
425    max_backward: usize,
426    gap: usize,
427    nodes: &[ZopfliNode],
428) -> u32 {
429    let clen: usize = nodes[pos].copy_length() as usize;
430    let ilen: usize = ((nodes[pos]).dcode_insert_length) as usize & 0x07ff_ffff;
431    let dist: usize = nodes[pos].copy_distance() as usize;
432    if pos == 0usize {
433        0u32
434    } else if dist.wrapping_add(clen) <= block_start.wrapping_add(pos).wrapping_add(gap)
435        && dist <= max_backward.wrapping_add(gap)
436        && nodes[pos].distance_code() > 0
437    {
438        pos as u32
439    } else {
440        match (nodes[(pos.wrapping_sub(clen).wrapping_sub(ilen) as usize)]).u {
441            Union1::shortcut(shrt) => shrt,
442            _ => 0,
443        }
444    }
445}
446
447impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
448    #[inline(always)]
449    fn get_literal_costs(&self, from: usize, to: usize) -> floatX {
450        self.literal_costs_.slice()[to] - self.literal_costs_.slice()[from]
451    }
452}
453
454fn ComputeDistanceCache(
455    pos: usize,
456    mut starting_dist_cache: &[i32],
457    nodes: &[ZopfliNode],
458    dist_cache: &mut [i32],
459) {
460    let mut idx: i32 = 0i32;
461    let mut p: usize = match (nodes[pos]).u {
462        Union1::shortcut(shrt) => shrt,
463        _ => 0,
464    } as usize;
465    while idx < 4i32 && (p > 0usize) {
466        let ilen: usize = ((nodes[p]).dcode_insert_length) as usize & 0x07ff_ffff;
467        let clen = nodes[p].copy_length() as usize;
468        let dist = nodes[p].copy_distance() as usize;
469        dist_cache[({
470            let _old = idx;
471            idx += 1;
472            _old
473        } as usize)] = dist as i32;
474        p = match (nodes[(p.wrapping_sub(clen).wrapping_sub(ilen) as usize)]).u {
475            Union1::shortcut(shrt) => shrt,
476            _ => 0,
477        } as usize;
478    }
479    while idx < 4i32 {
480        {
481            dist_cache[(idx as usize)] = {
482                let (_old, _upper) = starting_dist_cache.split_at(1);
483                starting_dist_cache = _upper;
484                _old[0]
485            };
486        }
487        idx += 1;
488    }
489}
490
491impl StartPosQueue {
492    #[inline(always)]
493    fn size(&self) -> usize {
494        min(self.idx_, 8)
495    }
496
497    fn push(&mut self, posdata: &PosData) {
498        let mut offset: usize = !self.idx_ & 7usize;
499        self.idx_ = self.idx_.wrapping_add(1);
500        let len: usize = self.size();
501        let q: &mut [PosData; 8] = &mut self.q_;
502        q[offset] = *posdata;
503        for _i in 1..len {
504            if q[offset & 7].costdiff > q[(offset + 1) & 7].costdiff {
505                q.swap(offset & 7, (offset + 1) & 7);
506            }
507            offset = offset.wrapping_add(1);
508        }
509    }
510}
511
512fn EvaluateNode<AllocF: Allocator<floatX>>(
513    block_start: usize,
514    pos: usize,
515    max_backward_limit: usize,
516    gap: usize,
517    starting_dist_cache: &[i32],
518    model: &ZopfliCostModel<AllocF>,
519    queue: &mut StartPosQueue,
520    nodes: &mut [ZopfliNode],
521) {
522    let node_cost: floatX = match (nodes[pos]).u {
523        Union1::cost(cst) => cst,
524        _ => 0.0,
525    };
526    (nodes[pos]).u = Union1::shortcut(ComputeDistanceShortcut(
527        block_start,
528        pos,
529        max_backward_limit,
530        gap,
531        nodes,
532    ));
533    if node_cost <= model.get_literal_costs(0, pos) {
534        let mut posdata = PosData {
535            pos,
536            cost: node_cost,
537            costdiff: node_cost - model.get_literal_costs(0, pos),
538            distance_cache: [0; 4],
539        };
540        ComputeDistanceCache(
541            pos,
542            starting_dist_cache,
543            nodes,
544            &mut posdata.distance_cache[..],
545        );
546        queue.push(&mut posdata);
547    }
548}
549
550impl StartPosQueue {
551    #[inline(always)]
552    fn at(&self, k: usize) -> &PosData {
553        &self.q_[k.wrapping_sub(self.idx_) & 7usize]
554    }
555}
556
557impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
558    #[inline(always)]
559    fn get_min_cost_cmd(&self) -> floatX {
560        self.min_cost_cmd_
561    }
562}
563
564#[inline(always)]
565fn ComputeMinimumCopyLength(
566    start_cost: floatX,
567    nodes: &[ZopfliNode],
568    num_bytes: usize,
569    pos: usize,
570) -> usize {
571    let mut min_cost: floatX = start_cost;
572    let mut len: usize = 2usize;
573    let mut next_len_bucket: usize = 4usize;
574    let mut next_len_offset: usize = 10usize;
575    while pos.wrapping_add(len) <= num_bytes
576        && (match (nodes[pos.wrapping_add(len)]).u {
577            Union1::cost(cst) => cst,
578            _ => 0.0,
579        } <= min_cost)
580    {
581        len = len.wrapping_add(1);
582        if len == next_len_offset {
583            min_cost += 1.0;
584            next_len_offset = next_len_offset.wrapping_add(next_len_bucket);
585            next_len_bucket = next_len_bucket.wrapping_mul(2);
586        }
587    }
588    len
589}
590#[inline(always)]
591fn GetInsertExtra(inscode: u16) -> u32 {
592    kInsExtra[(inscode as usize)]
593}
594
595impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
596    #[inline(always)]
597    fn get_distance_cost(&self, distcode: usize) -> floatX {
598        self.cost_dist_.slice()[distcode]
599    }
600}
601
602#[inline(always)]
603fn GetCopyExtra(copycode: u16) -> u32 {
604    kCopyExtra[(copycode as usize)]
605}
606
607impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
608    #[inline(always)]
609    fn get_command_cost(&self, cmdcode: u16) -> floatX {
610        self.cost_cmd_[cmdcode as usize]
611    }
612}
613
614#[inline(always)]
615fn UpdateZopfliNode(
616    nodes: &mut [ZopfliNode],
617    pos: usize,
618    start_pos: usize,
619    len: usize,
620    len_code: usize,
621    dist: usize,
622    short_code: usize,
623    cost: floatX,
624) {
625    let next = &mut nodes[pos.wrapping_add(len)];
626    next.length = (len | len.wrapping_add(9u32 as usize).wrapping_sub(len_code) << 25) as u32;
627    next.distance = dist as u32;
628    next.dcode_insert_length = pos.wrapping_sub(start_pos) as u32 | (short_code << 27) as u32;
629    next.u = Union1::cost(cost);
630}
631
632impl BackwardMatch {
633    #[inline(always)]
634    fn length_code(&self) -> usize {
635        let code = (self.length_and_code() & 31u32) as usize;
636        if code != 0 {
637            code
638        } else {
639            self.length()
640        }
641    }
642}
643
644fn UpdateNodes<AllocF: Allocator<floatX>>(
645    num_bytes: usize,
646    block_start: usize,
647    pos: usize,
648    ringbuffer: &[u8],
649    ringbuffer_mask: usize,
650    ringbuffer_break: Option<core::num::NonZeroUsize>,
651    params: &BrotliEncoderParams,
652    max_backward_limit: usize,
653    starting_dist_cache: &[i32],
654    num_matches: usize,
655    matches: &[u64],
656    model: &ZopfliCostModel<AllocF>,
657    queue: &mut StartPosQueue,
658    nodes: &mut [ZopfliNode],
659) -> usize {
660    let cur_ix: usize = block_start.wrapping_add(pos);
661    let cur_ix_masked: usize = cur_ix & ringbuffer_mask;
662    let max_distance: usize = min(cur_ix, max_backward_limit);
663    let max_len: usize = num_bytes.wrapping_sub(pos);
664    let max_zopfli_len: usize = MaxZopfliLen(params);
665    let min_len: usize;
666    let mut result: usize = 0usize;
667    let gap: usize = 0usize;
668    EvaluateNode(
669        block_start,
670        pos,
671        max_backward_limit,
672        gap,
673        starting_dist_cache,
674        model,
675        queue,
676        nodes,
677    );
678    {
679        let posdata = queue.at(0);
680        let min_cost =
681            posdata.cost + model.get_min_cost_cmd() + model.get_literal_costs(posdata.pos, pos);
682        min_len = ComputeMinimumCopyLength(min_cost, nodes, num_bytes, pos);
683    }
684
685    for k in 0..min(MaxZopfliCandidates(params), queue.size()) {
686        let posdata = queue.at(k);
687        let start: usize = posdata.pos;
688        let inscode: u16 = GetInsertLengthCode(pos.wrapping_sub(start));
689        let start_costdiff: floatX = posdata.costdiff;
690        let base_cost: floatX =
691            start_costdiff + GetInsertExtra(inscode) as (floatX) + model.get_literal_costs(0, pos);
692        let mut best_len: usize = min_len.wrapping_sub(1);
693        for j in 0..16 {
694            if best_len >= max_len {
695                break;
696            }
697
698            let idx: usize = kDistanceCacheIndex[j] as usize;
699            let distance_cache_len_minus_1 = 3;
700            debug_assert_eq!(distance_cache_len_minus_1 + 1, posdata.distance_cache.len());
701            let backward: usize = (posdata.distance_cache[(idx & distance_cache_len_minus_1)]
702                + i32::from(kDistanceCacheOffset[j])) as usize;
703            let mut prev_ix: usize = cur_ix.wrapping_sub(backward);
704            let len: usize;
705            let continuation: u8 = ringbuffer[cur_ix_masked.wrapping_add(best_len)];
706            /*
707            if let Some(breakpoint) = ringbuffer_break {
708                if prev_ix < usize::from(breakpoint) &&
709                    cur_ix_masked.wrapping_add(best_len) > usize::from(breakpoint)
710                {
711                    break;
712                }
713                let tmp_ix = prev_ix & ringbuffer_mask;
714                if tmp_ix < usize::from(breakpoint) &&
715                    tmp_ix.wrapping_add(best_len) > breakpoint
716                {
717                    continue;
718                }
719            }*/
720            if cur_ix_masked.wrapping_add(best_len) > ringbuffer_mask {
721                break;
722            }
723            if backward > max_distance.wrapping_add(gap) {
724                continue;
725            }
726            if backward > max_distance {
727                continue;
728            }
729            if prev_ix >= cur_ix {
730                continue;
731            }
732            prev_ix &= ringbuffer_mask;
733            if prev_ix.wrapping_add(best_len) > ringbuffer_mask
734                || continuation != ringbuffer[prev_ix.wrapping_add(best_len)]
735            {
736                continue;
737            }
738            len = fix_unbroken_len(
739                FindMatchLengthWithLimit(
740                    &ringbuffer[prev_ix..],
741                    &ringbuffer[cur_ix_masked..],
742                    max_len,
743                ),
744                prev_ix,
745                cur_ix_masked,
746                ringbuffer_break,
747            );
748
749            let dist_cost = base_cost + model.get_distance_cost(j);
750            for l in best_len.wrapping_add(1)..=len {
751                let copycode: u16 = GetCopyLengthCode(l);
752                let cmdcode = combine_length_codes(inscode, copycode, j == 0);
753                let cost: floatX = (if cmdcode < 128 { base_cost } else { dist_cost })
754                    + (GetCopyExtra(copycode) as floatX)
755                    + model.get_command_cost(cmdcode);
756                if cost
757                    < match nodes[pos.wrapping_add(l)].u {
758                        Union1::cost(cost) => cost,
759                        _ => 0.0,
760                    }
761                {
762                    UpdateZopfliNode(nodes, pos, start, l, l, backward, j.wrapping_add(1), cost);
763                    result = max(result, l);
764                }
765                best_len = l;
766            }
767        }
768
769        if k >= 2 {
770            continue;
771        }
772
773        let mut len: usize = min_len;
774        for j in 0usize..num_matches {
775            let match_ = BackwardMatch(matches[j]);
776            let dist: usize = match_.distance() as usize;
777            let is_dictionary_match = dist > max_distance.wrapping_add(gap);
778            let dist_code: usize = dist.wrapping_add(16).wrapping_sub(1);
779            let mut dist_symbol: u16 = 0;
780            let mut distextra: u32 = 0;
781
782            PrefixEncodeCopyDistance(
783                dist_code,
784                params.dist.num_direct_distance_codes as usize,
785                u64::from(params.dist.distance_postfix_bits),
786                &mut dist_symbol,
787                &mut distextra,
788            );
789            let distnumextra: u32 = u32::from(dist_symbol) >> 10;
790            let dist_cost = base_cost
791                + (distnumextra as floatX)
792                + model.get_distance_cost((dist_symbol as i32 & 0x03ff) as usize);
793            let max_match_len = match_.length();
794            if len < max_match_len && (is_dictionary_match || max_match_len > max_zopfli_len) {
795                len = max_match_len;
796            }
797            while len <= max_match_len {
798                {
799                    let len_code: usize = if is_dictionary_match {
800                        match_.length_code()
801                    } else {
802                        len
803                    };
804                    let copycode: u16 = GetCopyLengthCode(len_code);
805                    let cmdcode = combine_length_codes(inscode, copycode, false);
806                    let cost: floatX = dist_cost
807                        + GetCopyExtra(copycode) as (floatX)
808                        + model.get_command_cost(cmdcode);
809                    if let Union1::cost(nodeCost) = (nodes[pos.wrapping_add(len)]).u {
810                        if cost < nodeCost {
811                            UpdateZopfliNode(nodes, pos, start, len, len_code, dist, 0usize, cost);
812                            result = max(result, len);
813                        }
814                    }
815                }
816                len = len.wrapping_add(1);
817            }
818        }
819    }
820    result
821}
822
823impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
824    #[inline(always)]
825    fn cleanup(&mut self, m: &mut AllocF) {
826        m.free_cell(core::mem::take(&mut self.literal_costs_));
827        m.free_cell(core::mem::take(&mut self.cost_dist_));
828    }
829}
830
831impl ZopfliNode {
832    #[inline(always)]
833    fn command_length(&self) -> u32 {
834        self.copy_length()
835            .wrapping_add(self.dcode_insert_length & 0x07ff_ffff)
836    }
837}
838
839#[inline(always)]
840fn ComputeShortestPathFromNodes(num_bytes: usize, nodes: &mut [ZopfliNode]) -> usize {
841    let mut index: usize = num_bytes;
842    let mut num_commands: usize = 0usize;
843    while (nodes[index].dcode_insert_length & 0x07ff_ffff) == 0 && nodes[index].length == 1 {
844        index = index.wrapping_sub(1);
845    }
846    nodes[index].u = Union1::next(!(0u32));
847    while index != 0 {
848        let len = nodes[index].command_length() as usize;
849        index = index.wrapping_sub(len);
850        (nodes[index]).u = Union1::next(len as u32);
851        num_commands = num_commands.wrapping_add(1);
852    }
853    num_commands
854}
855
856const MAX_NUM_MATCHES_H10: usize = 128;
857pub fn BrotliZopfliComputeShortestPath<
858    AllocU32: Allocator<u32>,
859    Buckets: Allocable<u32, AllocU32> + SliceWrapperMut<u32> + SliceWrapper<u32>,
860    Params: H10Params,
861    AllocF: Allocator<floatX>,
862>(
863    m: &mut AllocF,
864    dictionary: Option<&BrotliDictionary>,
865    num_bytes: usize,
866    position: usize,
867    ringbuffer: &[u8],
868    ringbuffer_mask: usize,
869    ringbuffer_break: Option<core::num::NonZeroUsize>,
870    params: &BrotliEncoderParams,
871    max_backward_limit: usize,
872    dist_cache: &[i32],
873    handle: &mut H10<AllocU32, Buckets, Params>,
874    nodes: &mut [ZopfliNode],
875) -> usize
876where
877    Buckets: PartialEq<Buckets>,
878{
879    let max_zopfli_len: usize = MaxZopfliLen(params);
880    let mut model: ZopfliCostModel<AllocF>;
881    let mut queue: StartPosQueue;
882    let mut matches = [0; MAX_NUM_MATCHES_H10];
883    let store_end: usize = if num_bytes >= STORE_LOOKAHEAD_H_10 {
884        position
885            .wrapping_add(num_bytes)
886            .wrapping_sub(STORE_LOOKAHEAD_H_10)
887            .wrapping_add(1)
888    } else {
889        position
890    };
891    let mut i: usize;
892    let gap: usize = 0usize;
893    let lz_matches_offset: usize = 0usize;
894    (nodes[0]).length = 0u32;
895    (nodes[0]).u = Union1::cost(0.0);
896    model = ZopfliCostModel::init(m, &params.dist, num_bytes);
897    if !(0i32 == 0) {
898        return 0usize;
899    }
900    model.set_from_literal_costs(position, ringbuffer, ringbuffer_mask);
901    queue = StartPosQueue::default();
902    i = 0usize;
903    while i.wrapping_add(handle.HashTypeLength()).wrapping_sub(1) < num_bytes {
904        {
905            let pos: usize = position.wrapping_add(i);
906            let max_distance: usize = min(pos, max_backward_limit);
907            let mut skip: usize;
908            let mut num_matches: usize = FindAllMatchesH10(
909                handle,
910                dictionary,
911                ringbuffer,
912                ringbuffer_mask,
913                ringbuffer_break,
914                pos,
915                num_bytes.wrapping_sub(i),
916                max_distance,
917                gap,
918                params,
919                &mut matches[lz_matches_offset..],
920            );
921            if num_matches > 0
922                && BackwardMatch(matches[num_matches.wrapping_sub(1)]).length() > max_zopfli_len
923            {
924                matches[0] = matches[num_matches.wrapping_sub(1)];
925                num_matches = 1usize;
926            }
927            skip = UpdateNodes(
928                num_bytes,
929                position,
930                i,
931                ringbuffer,
932                ringbuffer_mask,
933                ringbuffer_break,
934                params,
935                max_backward_limit,
936                dist_cache,
937                num_matches,
938                &matches[..],
939                &mut model,
940                &mut queue,
941                nodes,
942            );
943            if skip < 16384usize {
944                skip = 0usize;
945            }
946            if num_matches == 1 && BackwardMatch(matches[0]).length() > max_zopfli_len {
947                skip = max(BackwardMatch(matches[0]).length(), skip);
948            }
949            if skip > 1usize {
950                handle.StoreRange(
951                    ringbuffer,
952                    ringbuffer_mask,
953                    pos.wrapping_add(1),
954                    min(pos.wrapping_add(skip), store_end),
955                );
956                skip = skip.wrapping_sub(1);
957                while skip != 0 {
958                    i = i.wrapping_add(1);
959                    if i.wrapping_add(handle.HashTypeLength()).wrapping_sub(1) >= num_bytes {
960                        break;
961                    }
962                    EvaluateNode(
963                        position,
964                        i,
965                        max_backward_limit,
966                        gap,
967                        dist_cache,
968                        &mut model,
969                        &mut queue,
970                        nodes,
971                    );
972                    skip = skip.wrapping_sub(1);
973                }
974            }
975        }
976        i = i.wrapping_add(1);
977    }
978
979    model.cleanup(m);
980
981    ComputeShortestPathFromNodes(num_bytes, nodes)
982}
983
984pub fn BrotliCreateZopfliBackwardReferences<
985    Alloc: Allocator<u32> + Allocator<floatX> + Allocator<ZopfliNode>,
986    Buckets: Allocable<u32, Alloc> + SliceWrapperMut<u32> + SliceWrapper<u32>,
987    Params: H10Params,
988>(
989    alloc: &mut Alloc,
990    dictionary: Option<&BrotliDictionary>,
991    num_bytes: usize,
992    position: usize,
993    ringbuffer: &[u8],
994    ringbuffer_mask: usize,
995    ringbuffer_break: Option<core::num::NonZeroUsize>,
996    params: &BrotliEncoderParams,
997    hasher: &mut H10<Alloc, Buckets, Params>,
998    dist_cache: &mut [i32],
999    last_insert_len: &mut usize,
1000    commands: &mut [Command],
1001    num_commands: &mut usize,
1002    num_literals: &mut usize,
1003) where
1004    Buckets: PartialEq<Buckets>,
1005{
1006    let max_backward_limit: usize = (1usize << params.lgwin).wrapping_sub(16);
1007    // FIXME: makes little sense to test if N+1 > 0 -- always true unless wrapping. Perhaps use allocate() instead?
1008    let mut nodes = alloc_or_default::<ZopfliNode, _>(alloc, num_bytes + 1);
1009    if !(0i32 == 0) {
1010        return;
1011    }
1012    BrotliInitZopfliNodes(nodes.slice_mut(), num_bytes.wrapping_add(1));
1013    *num_commands = num_commands.wrapping_add(BrotliZopfliComputeShortestPath(
1014        alloc,
1015        dictionary,
1016        num_bytes,
1017        position,
1018        ringbuffer,
1019        ringbuffer_mask,
1020        ringbuffer_break,
1021        params,
1022        max_backward_limit,
1023        dist_cache,
1024        hasher,
1025        nodes.slice_mut(),
1026    ));
1027    if !(0i32 == 0) {
1028        return;
1029    }
1030    BrotliZopfliCreateCommands(
1031        num_bytes,
1032        position,
1033        max_backward_limit,
1034        nodes.slice(),
1035        dist_cache,
1036        last_insert_len,
1037        params,
1038        commands,
1039        num_literals,
1040    );
1041    {
1042        <Alloc as Allocator<ZopfliNode>>::free_cell(alloc, core::mem::take(&mut nodes));
1043    }
1044}
1045
1046fn SetCost(histogram: &[u32], histogram_size: usize, literal_histogram: bool, cost: &mut [floatX]) {
1047    let mut sum: u64 = 0;
1048    for i in 0..histogram_size {
1049        sum = sum.wrapping_add(u64::from(histogram[i]));
1050    }
1051    let log2sum = FastLog2(sum);
1052
1053    let mut missing_symbol_sum = sum;
1054    if !literal_histogram {
1055        for i in 0..histogram_size {
1056            if histogram[i] == 0 {
1057                missing_symbol_sum = missing_symbol_sum.wrapping_add(1);
1058            }
1059        }
1060    }
1061
1062    let missing_symbol_cost = FastLog2f64(missing_symbol_sum) + 2.0;
1063    for i in 0..histogram_size {
1064        if histogram[i] == 0 {
1065            cost[i] = missing_symbol_cost;
1066        } else {
1067            cost[i] = log2sum - FastLog2(u64::from(histogram[i]));
1068            if cost[i] < 1.0 {
1069                cost[i] = 1.0;
1070            }
1071        }
1072    }
1073}
1074
1075impl<AllocF: Allocator<floatX>> ZopfliCostModel<AllocF> {
1076    fn set_from_commands(
1077        &mut self,
1078        position: usize,
1079        ringbuffer: &[u8],
1080        ringbuffer_mask: usize,
1081        commands: &[Command],
1082        num_commands: usize,
1083        last_insert_len: usize,
1084    ) {
1085        let mut histogram_literal = [0u32; BROTLI_NUM_LITERAL_SYMBOLS];
1086        let mut histogram_cmd = [0u32; BROTLI_NUM_COMMAND_SYMBOLS];
1087        let mut histogram_dist = [0u32; BROTLI_SIMPLE_DISTANCE_ALPHABET_SIZE];
1088        let mut cost_literal = [0.0; BROTLI_NUM_LITERAL_SYMBOLS];
1089        let mut pos: usize = position.wrapping_sub(last_insert_len);
1090        let mut min_cost_cmd: floatX = kInfinity;
1091        let mut i: usize;
1092        let cost_cmd: &mut [floatX] = &mut self.cost_cmd_[..];
1093        i = 0usize;
1094        while i < num_commands {
1095            {
1096                let inslength: usize = (commands[i]).insert_len_ as usize;
1097                let copylength: usize = commands[i].copy_len() as usize;
1098                let distcode: usize = (commands[i].dist_prefix_ as i32 & 0x03ff) as usize;
1099                let cmdcode: usize = (commands[i]).cmd_prefix_ as usize;
1100                {
1101                    let _rhs = 1;
1102                    let _lhs = &mut histogram_cmd[cmdcode];
1103                    *_lhs = (*_lhs).wrapping_add(_rhs as u32);
1104                }
1105                if cmdcode >= 128usize {
1106                    let _rhs = 1;
1107                    let _lhs = &mut histogram_dist[distcode];
1108                    *_lhs = (*_lhs).wrapping_add(_rhs as u32);
1109                }
1110                for j in 0usize..inslength {
1111                    let _rhs = 1;
1112                    let _lhs = &mut histogram_literal
1113                        [(ringbuffer[(pos.wrapping_add(j) & ringbuffer_mask)] as usize)];
1114                    *_lhs = (*_lhs).wrapping_add(_rhs as u32);
1115                }
1116                pos = pos.wrapping_add(inslength.wrapping_add(copylength));
1117            }
1118            i = i.wrapping_add(1);
1119        }
1120        SetCost(
1121            &histogram_literal[..],
1122            BROTLI_NUM_LITERAL_SYMBOLS,
1123            true,
1124            &mut cost_literal,
1125        );
1126        SetCost(
1127            &histogram_cmd[..],
1128            BROTLI_NUM_COMMAND_SYMBOLS,
1129            false,
1130            &mut cost_cmd[..],
1131        );
1132        SetCost(
1133            &histogram_dist[..],
1134            self.distance_histogram_size as usize,
1135            false,
1136            self.cost_dist_.slice_mut(),
1137        );
1138        for i in 0usize..704usize {
1139            min_cost_cmd = min_cost_cmd.min(cost_cmd[i]);
1140        }
1141        self.min_cost_cmd_ = min_cost_cmd;
1142        {
1143            let literal_costs: &mut [floatX] = self.literal_costs_.slice_mut();
1144            let mut literal_carry: floatX = 0.0;
1145            let num_bytes: usize = self.num_bytes_;
1146            literal_costs[0] = 0.0 as (floatX);
1147            for i in 0usize..num_bytes {
1148                literal_carry +=
1149                    cost_literal[ringbuffer[position.wrapping_add(i) & ringbuffer_mask] as usize];
1150                literal_costs[i.wrapping_add(1)] = literal_costs[i] + literal_carry;
1151                literal_carry -= literal_costs[i.wrapping_add(1)] - literal_costs[i];
1152            }
1153        }
1154    }
1155}
1156
1157fn ZopfliIterate<AllocF: Allocator<floatX>>(
1158    num_bytes: usize,
1159    position: usize,
1160    ringbuffer: &[u8],
1161    ringbuffer_mask: usize,
1162    ringbuffer_break: Option<core::num::NonZeroUsize>,
1163    params: &BrotliEncoderParams,
1164    max_backward_limit: usize,
1165    gap: usize,
1166    dist_cache: &[i32],
1167    model: &ZopfliCostModel<AllocF>,
1168    num_matches: &[u32],
1169    matches: &[u64],
1170    nodes: &mut [ZopfliNode],
1171) -> usize {
1172    let max_zopfli_len: usize = MaxZopfliLen(params);
1173    let mut queue: StartPosQueue;
1174    let mut cur_match_pos: usize = 0usize;
1175    let mut i: usize;
1176    (nodes[0]).length = 0u32;
1177    (nodes[0]).u = Union1::cost(0.0);
1178    queue = StartPosQueue::default();
1179    i = 0usize;
1180    while i.wrapping_add(3) < num_bytes {
1181        {
1182            let mut skip: usize = UpdateNodes(
1183                num_bytes,
1184                position,
1185                i,
1186                ringbuffer,
1187                ringbuffer_mask,
1188                ringbuffer_break,
1189                params,
1190                max_backward_limit,
1191                dist_cache,
1192                num_matches[i] as usize,
1193                &matches[cur_match_pos..],
1194                model,
1195                &mut queue,
1196                nodes,
1197            );
1198            if skip < 16384usize {
1199                skip = 0usize;
1200            }
1201            cur_match_pos = cur_match_pos.wrapping_add(num_matches[i] as usize);
1202            if num_matches[i] == 1
1203                && BackwardMatch(matches[cur_match_pos.wrapping_sub(1)]).length() > max_zopfli_len
1204            {
1205                skip = max(
1206                    BackwardMatch(matches[cur_match_pos.wrapping_sub(1)]).length(),
1207                    skip,
1208                );
1209            }
1210            if skip > 1usize {
1211                skip = skip.wrapping_sub(1);
1212                while skip != 0 {
1213                    i = i.wrapping_add(1);
1214                    if i.wrapping_add(3) >= num_bytes {
1215                        break;
1216                    }
1217                    EvaluateNode(
1218                        position,
1219                        i,
1220                        max_backward_limit,
1221                        gap,
1222                        dist_cache,
1223                        model,
1224                        &mut queue,
1225                        nodes,
1226                    );
1227                    cur_match_pos = cur_match_pos.wrapping_add(num_matches[i] as usize);
1228                    skip = skip.wrapping_sub(1);
1229                }
1230            }
1231        }
1232        i = i.wrapping_add(1);
1233    }
1234    ComputeShortestPathFromNodes(num_bytes, nodes)
1235}
1236
1237pub fn BrotliCreateHqZopfliBackwardReferences<
1238    Alloc: Allocator<u32> + Allocator<u64> + Allocator<floatX> + Allocator<ZopfliNode>,
1239    Buckets: Allocable<u32, Alloc> + SliceWrapperMut<u32> + SliceWrapper<u32>,
1240    Params: H10Params,
1241>(
1242    alloc: &mut Alloc,
1243    dictionary: Option<&BrotliDictionary>,
1244    num_bytes: usize,
1245    position: usize,
1246    ringbuffer: &[u8],
1247    ringbuffer_mask: usize,
1248    ringbuffer_break: Option<core::num::NonZeroUsize>,
1249    params: &BrotliEncoderParams,
1250    hasher: &mut H10<Alloc, Buckets, Params>,
1251    dist_cache: &mut [i32],
1252    last_insert_len: &mut usize,
1253    commands: &mut [Command],
1254    num_commands: &mut usize,
1255    num_literals: &mut usize,
1256) where
1257    Buckets: PartialEq<Buckets>,
1258{
1259    let max_backward_limit: usize = (1usize << params.lgwin).wrapping_sub(16);
1260    let mut num_matches = alloc_or_default::<u32, _>(alloc, num_bytes);
1261    let mut matches_size: usize = (4usize).wrapping_mul(num_bytes);
1262    let store_end: usize = if num_bytes >= STORE_LOOKAHEAD_H_10 {
1263        position
1264            .wrapping_add(num_bytes)
1265            .wrapping_sub(STORE_LOOKAHEAD_H_10)
1266            .wrapping_add(1)
1267    } else {
1268        position
1269    };
1270    let mut cur_match_pos: usize = 0usize;
1271    let mut i: usize;
1272
1273    let mut orig_dist_cache = [0i32; 4];
1274
1275    let mut model: ZopfliCostModel<Alloc>;
1276    let mut matches = alloc_or_default::<u64, _>(alloc, matches_size);
1277    let gap: usize = 0usize;
1278    let shadow_matches: usize = 0usize;
1279    i = 0usize;
1280    while i.wrapping_add(hasher.HashTypeLength()).wrapping_sub(1) < num_bytes {
1281        {
1282            let pos: usize = position.wrapping_add(i);
1283            let max_distance: usize = min(pos, max_backward_limit);
1284            let max_length: usize = num_bytes.wrapping_sub(i);
1285
1286            let mut j: usize;
1287            {
1288                if matches_size < cur_match_pos.wrapping_add(128).wrapping_add(shadow_matches) {
1289                    let mut new_size: usize = if matches_size == 0usize {
1290                        cur_match_pos.wrapping_add(128).wrapping_add(shadow_matches)
1291                    } else {
1292                        matches_size
1293                    };
1294                    while new_size < cur_match_pos.wrapping_add(128).wrapping_add(shadow_matches) {
1295                        new_size = new_size.wrapping_mul(2);
1296                    }
1297                    let mut new_array = alloc_or_default::<u64, _>(alloc, new_size);
1298                    if matches_size != 0 {
1299                        for (dst, src) in new_array
1300                            .slice_mut()
1301                            .split_at_mut(matches_size)
1302                            .0
1303                            .iter_mut()
1304                            .zip(matches.slice().split_at(matches_size).0.iter())
1305                        {
1306                            *dst = *src;
1307                        }
1308                    }
1309                    {
1310                        <Alloc as Allocator<u64>>::free_cell(alloc, core::mem::take(&mut matches));
1311                    }
1312                    matches = new_array;
1313                    matches_size = new_size;
1314                }
1315            }
1316            if !(0i32 == 0) {
1317                return;
1318            }
1319            let num_found_matches: usize = FindAllMatchesH10(
1320                hasher,
1321                dictionary, //&params.dictionary ,
1322                ringbuffer,
1323                ringbuffer_mask,
1324                ringbuffer_break,
1325                pos,
1326                max_length,
1327                max_distance,
1328                gap,
1329                params,
1330                &mut matches.slice_mut()[cur_match_pos.wrapping_add(shadow_matches)..],
1331            );
1332            let cur_match_end: usize = cur_match_pos.wrapping_add(num_found_matches);
1333            j = cur_match_pos;
1334            while j.wrapping_add(1) < cur_match_end {
1335                {}
1336                j = j.wrapping_add(1);
1337            }
1338            num_matches.slice_mut()[i] = num_found_matches as u32;
1339            if num_found_matches > 0usize {
1340                let match_len =
1341                    BackwardMatch(matches.slice()[cur_match_end.wrapping_sub(1)]).length();
1342                if match_len > 325usize {
1343                    let skip: usize = match_len.wrapping_sub(1);
1344                    let tmp = matches.slice()[(cur_match_end.wrapping_sub(1) as usize)];
1345                    matches.slice_mut()[cur_match_pos] = tmp;
1346                    cur_match_pos = cur_match_pos.wrapping_add(1);
1347                    num_matches.slice_mut()[i] = 1u32;
1348                    hasher.StoreRange(
1349                        ringbuffer,
1350                        ringbuffer_mask,
1351                        pos.wrapping_add(1),
1352                        min(pos.wrapping_add(match_len), store_end),
1353                    );
1354                    for item in num_matches
1355                        .slice_mut()
1356                        .split_at_mut(i.wrapping_add(1))
1357                        .1
1358                        .split_at_mut(skip)
1359                        .0
1360                        .iter_mut()
1361                    {
1362                        *item = 0;
1363                    }
1364                    i = i.wrapping_add(skip);
1365                } else {
1366                    cur_match_pos = cur_match_end;
1367                }
1368            }
1369        }
1370        i = i.wrapping_add(1);
1371    }
1372    let orig_num_literals: usize = *num_literals;
1373    let orig_last_insert_len: usize = *last_insert_len;
1374    for (i, j) in orig_dist_cache
1375        .split_at_mut(4)
1376        .0
1377        .iter_mut()
1378        .zip(dist_cache.split_at(4).0)
1379    {
1380        *i = *j;
1381    }
1382    let orig_num_commands: usize = *num_commands;
1383    // FIXME: makes little sense to test if N+1 > 0 -- always true unless wrapping. Perhaps use allocate() instead?
1384    let mut nodes = alloc_or_default::<ZopfliNode, _>(alloc, num_bytes + 1);
1385    if !(0i32 == 0) {
1386        return;
1387    }
1388    model = ZopfliCostModel::init(alloc, &params.dist, num_bytes);
1389    if !(0i32 == 0) {
1390        return;
1391    }
1392    for i in 0usize..2usize {
1393        BrotliInitZopfliNodes(nodes.slice_mut(), num_bytes.wrapping_add(1));
1394        if i == 0usize {
1395            model.set_from_literal_costs(position, ringbuffer, ringbuffer_mask);
1396        } else {
1397            model.set_from_commands(
1398                position,
1399                ringbuffer,
1400                ringbuffer_mask,
1401                commands,
1402                num_commands.wrapping_sub(orig_num_commands),
1403                orig_last_insert_len,
1404            );
1405        }
1406        *num_commands = orig_num_commands;
1407        *num_literals = orig_num_literals;
1408        *last_insert_len = orig_last_insert_len;
1409        for (i, j) in dist_cache
1410            .split_at_mut(4)
1411            .0
1412            .iter_mut()
1413            .zip(orig_dist_cache.split_at(4).0)
1414        {
1415            *i = *j;
1416        }
1417        *num_commands = num_commands.wrapping_add(ZopfliIterate(
1418            num_bytes,
1419            position,
1420            ringbuffer,
1421            ringbuffer_mask,
1422            ringbuffer_break,
1423            params,
1424            max_backward_limit,
1425            gap,
1426            dist_cache,
1427            &mut model,
1428            num_matches.slice(),
1429            matches.slice(),
1430            nodes.slice_mut(),
1431        ));
1432        BrotliZopfliCreateCommands(
1433            num_bytes,
1434            position,
1435            max_backward_limit,
1436            nodes.slice(),
1437            dist_cache,
1438            last_insert_len,
1439            params,
1440            commands,
1441            num_literals,
1442        );
1443    }
1444    model.cleanup(alloc);
1445    <Alloc as Allocator<ZopfliNode>>::free_cell(alloc, nodes);
1446    <Alloc as Allocator<u64>>::free_cell(alloc, matches);
1447    <Alloc as Allocator<u32>>::free_cell(alloc, num_matches);
1448}