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