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
28pub 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 ¶ms.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 literal_costs_: alloc_or_default::<floatX, _>(m, num_bytes + 2),
211 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 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 let max_backward = handle.window_mask_ - max(BROTLI_WINDOW_GAP - 1, position - i);
282 let mut _best_len = 0;
283 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, ¶ms.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 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, 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 let mut nodes = alloc_or_default::<ZopfliNode, _>(alloc, num_bytes + 1);
1351 if !(0i32 == 0) {
1352 return;
1353 }
1354 model = ZopfliCostModel::init(alloc, ¶ms.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}