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
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 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 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 let max_backward = handle.window_mask_ - max(BROTLI_WINDOW_GAP - 1, position - i);
283 let mut _best_len = 0;
284 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 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, ¶ms.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 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, 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 let mut nodes = alloc_or_default::<ZopfliNode, _>(alloc, num_bytes + 1);
1380 if !(0i32 == 0) {
1381 return;
1382 }
1383 model = ZopfliCostModel::init(alloc, ¶ms.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}