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