1use alloc::Allocator;
2use core;
3use core::cmp::{max, min};
4
5use super::super::alloc;
6use super::super::alloc::{SliceWrapper, SliceWrapperMut};
7use super::backward_references::{
8 AdvHashSpecialization, AdvHasher, AnyHasher, BasicHasher, BrotliCreateBackwardReferences,
9 BrotliEncoderMode, BrotliEncoderParams, BrotliHasherParams, H2Sub, H3Sub, H4Sub, H54Sub, H5Sub,
10 H6Sub, HQ5Sub, HQ7Sub, HowPrepared, StoreLookaheadThenStore, Struct1, UnionHasher, H9,
11 H9_BLOCK_BITS, H9_BLOCK_SIZE, H9_BUCKET_BITS, H9_NUM_LAST_DISTANCES_TO_CHECK,
12};
13use super::bit_cost::{shannon_entropy, BitsEntropy};
14use super::brotli_bit_stream::{
15 store_meta_block, store_meta_block_fast, store_meta_block_trivial,
16 store_uncompressed_meta_block, BrotliWriteEmptyLastMetaBlock, BrotliWriteMetadataMetaBlock,
17 BrotliWritePaddingMetaBlock, MetaBlockSplit, RecoderState,
18};
19use super::combined_alloc::BrotliAlloc;
20use super::command::{get_length_code, BrotliDistanceParams, Command};
21use super::compress_fragment::compress_fragment_fast;
22use super::compress_fragment_two_pass::{compress_fragment_two_pass, BrotliWriteBits};
23use super::constants::{
24 BROTLI_CONTEXT, BROTLI_CONTEXT_LUT, BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX,
25 BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS, BROTLI_WINDOW_GAP,
26};
27use super::hash_to_binary_tree::InitializeH10;
28use super::histogram::{
29 ContextType, CostAccessors, HistogramCommand, HistogramDistance, HistogramLiteral,
30};
31use super::interface;
32use super::metablock::{
33 BrotliBuildMetaBlock, BrotliBuildMetaBlockGreedy, BrotliInitDistanceParams,
34 BrotliOptimizeHistograms,
35};
36pub use super::parameters::BrotliEncoderParameter;
37use super::static_dict::{kNumDistanceCacheEntries, BrotliGetDictionary};
38use super::util::{floatX, Log2FloorNonZero};
39use crate::enc::combined_alloc::{alloc_default, allocate};
40use crate::enc::input_pair::InputReferenceMut;
41use crate::enc::utf8_util::is_mostly_utf8;
42
43static kCompressFragmentTwoPassBlockSize: usize = (1i32 << 17) as usize;
80
81static kMinUTF8Ratio: floatX = 0.75;
82
83pub struct RingBuffer<AllocU8: alloc::Allocator<u8>> {
84 pub size_: u32,
85 pub mask_: u32,
86 pub tail_size_: u32,
87 pub total_size_: u32,
88 pub cur_size_: u32,
89 pub pos_: u32,
90 pub data_mo: AllocU8::AllocatedMemory,
91 pub buffer_index: usize,
92}
93
94#[derive(PartialEq, Eq, Copy, Clone)]
95#[repr(i32)]
96pub enum BrotliEncoderStreamState {
97 BROTLI_STREAM_PROCESSING = 0,
98 BROTLI_STREAM_FLUSH_REQUESTED = 1,
99 BROTLI_STREAM_FINISHED = 2,
100 BROTLI_STREAM_METADATA_HEAD = 3,
101 BROTLI_STREAM_METADATA_BODY = 4,
102}
103
104#[derive(Clone, Copy, Debug)]
105enum NextOut {
106 DynamicStorage(u32),
107 TinyBuf(u32),
108 None,
109}
110fn GetNextOutInternal<'a>(
111 next_out: &NextOut,
112 storage: &'a mut [u8],
113 tiny_buf: &'a mut [u8; 16],
114) -> &'a mut [u8] {
115 match next_out {
116 &NextOut::DynamicStorage(offset) => &mut storage[offset as usize..],
117 &NextOut::TinyBuf(offset) => &mut tiny_buf[offset as usize..],
118 &NextOut::None => &mut [],
119 }
120}
121macro_rules! GetNextOut {
122 ($s : expr) => {
123 GetNextOutInternal(&$s.next_out_, $s.storage_.slice_mut(), &mut $s.tiny_buf_)
124 };
125}
126fn NextOutIncrement(next_out: &NextOut, inc: i32) -> NextOut {
127 match next_out {
128 &NextOut::DynamicStorage(offset) => NextOut::DynamicStorage((offset as i32 + inc) as u32),
129 &NextOut::TinyBuf(offset) => NextOut::TinyBuf((offset as i32 + inc) as u32),
130 &NextOut::None => NextOut::None,
131 }
132}
133fn IsNextOutNull(next_out: &NextOut) -> bool {
134 match next_out {
135 &NextOut::DynamicStorage(_) => false,
136 &NextOut::TinyBuf(_) => false,
137 &NextOut::None => true,
138 }
139}
140
141#[derive(Clone, Copy, Debug)]
142pub enum IsFirst {
143 NothingWritten,
144 HeaderWritten,
145 FirstCatableByteWritten,
146 BothCatableBytesWritten,
147}
148
149pub struct BrotliEncoderStateStruct<Alloc: BrotliAlloc> {
150 pub params: BrotliEncoderParams,
151 pub m8: Alloc,
152 pub hasher_: UnionHasher<Alloc>,
153 pub input_pos_: u64,
154 pub ringbuffer_: RingBuffer<Alloc>,
155 pub cmd_alloc_size_: usize,
156 pub commands_: <Alloc as Allocator<Command>>::AllocatedMemory, pub num_commands_: usize,
158 pub num_literals_: usize,
159 pub last_insert_len_: usize,
160 pub last_flush_pos_: u64,
161 pub last_processed_pos_: u64,
162 pub dist_cache_: [i32; 16],
163 pub saved_dist_cache_: [i32; kNumDistanceCacheEntries],
164 pub last_bytes_: u16,
165 pub last_bytes_bits_: u8,
166 pub prev_byte_: u8,
167 pub prev_byte2_: u8,
168 pub storage_size_: usize,
169 pub storage_: <Alloc as Allocator<u8>>::AllocatedMemory,
170 pub small_table_: [i32; 1024],
171 pub large_table_: <Alloc as Allocator<i32>>::AllocatedMemory,
172 pub cmd_depths_: [u8; 128],
174 pub cmd_bits_: [u16; 128],
175 pub cmd_code_: [u8; 512],
176 pub cmd_code_numbits_: usize,
177 pub command_buf_: <Alloc as Allocator<u32>>::AllocatedMemory,
178 pub literal_buf_: <Alloc as Allocator<u8>>::AllocatedMemory,
179 next_out_: NextOut,
180 pub available_out_: usize,
181 pub total_out_: u64,
182 pub tiny_buf_: [u8; 16],
183 pub remaining_metadata_bytes_: u32,
184 pub stream_state_: BrotliEncoderStreamState,
185 pub is_last_block_emitted_: bool,
186 pub is_initialized_: bool,
187 pub is_first_mb: IsFirst,
188 pub literal_scratch_space: <HistogramLiteral as CostAccessors>::i32vec,
189 pub command_scratch_space: <HistogramCommand as CostAccessors>::i32vec,
190 pub distance_scratch_space: <HistogramDistance as CostAccessors>::i32vec,
191 pub recoder_state: RecoderState,
192 custom_dictionary_size: Option<core::num::NonZeroUsize>,
193 custom_dictionary: bool,
194}
195
196pub fn set_parameter(
197 params: &mut BrotliEncoderParams,
198 p: BrotliEncoderParameter,
199 value: u32,
200) -> bool {
201 use crate::enc::parameters::BrotliEncoderParameter::*;
202 match p {
203 BROTLI_PARAM_MODE => {
204 params.mode = match value {
205 0 => BrotliEncoderMode::BROTLI_MODE_GENERIC,
206 1 => BrotliEncoderMode::BROTLI_MODE_TEXT,
207 2 => BrotliEncoderMode::BROTLI_MODE_FONT,
208 3 => BrotliEncoderMode::BROTLI_FORCE_LSB_PRIOR,
209 4 => BrotliEncoderMode::BROTLI_FORCE_MSB_PRIOR,
210 5 => BrotliEncoderMode::BROTLI_FORCE_UTF8_PRIOR,
211 6 => BrotliEncoderMode::BROTLI_FORCE_SIGNED_PRIOR,
212 _ => BrotliEncoderMode::BROTLI_MODE_GENERIC,
213 };
214 }
215 BROTLI_PARAM_QUALITY => params.quality = value as i32,
216 BROTLI_PARAM_STRIDE_DETECTION_QUALITY => params.stride_detection_quality = value as u8,
217 BROTLI_PARAM_HIGH_ENTROPY_DETECTION_QUALITY => {
218 params.high_entropy_detection_quality = value as u8
219 }
220 BROTLI_PARAM_CDF_ADAPTATION_DETECTION => params.cdf_adaptation_detection = value as u8,
221 BROTLI_PARAM_Q9_5 => params.q9_5 = (value != 0),
222 BROTLI_PARAM_PRIOR_BITMASK_DETECTION => params.prior_bitmask_detection = value as u8,
223 BROTLI_PARAM_SPEED => {
224 params.literal_adaptation[1].0 = value as u16;
225 if params.literal_adaptation[0] == (0, 0) {
226 params.literal_adaptation[0].0 = value as u16;
227 }
228 }
229 BROTLI_PARAM_SPEED_MAX => {
230 params.literal_adaptation[1].1 = value as u16;
231 if params.literal_adaptation[0].1 == 0 {
232 params.literal_adaptation[0].1 = value as u16;
233 }
234 }
235 BROTLI_PARAM_CM_SPEED => {
236 params.literal_adaptation[3].0 = value as u16;
237 if params.literal_adaptation[2] == (0, 0) {
238 params.literal_adaptation[2].0 = value as u16;
239 }
240 }
241 BROTLI_PARAM_CM_SPEED_MAX => {
242 params.literal_adaptation[3].1 = value as u16;
243 if params.literal_adaptation[2].1 == 0 {
244 params.literal_adaptation[2].1 = value as u16;
245 }
246 }
247 BROTLI_PARAM_SPEED_LOW => params.literal_adaptation[0].0 = value as u16,
248 BROTLI_PARAM_SPEED_LOW_MAX => params.literal_adaptation[0].1 = value as u16,
249 BROTLI_PARAM_CM_SPEED_LOW => params.literal_adaptation[2].0 = value as u16,
250 BROTLI_PARAM_CM_SPEED_LOW_MAX => params.literal_adaptation[2].1 = value as u16,
251 BROTLI_PARAM_LITERAL_BYTE_SCORE => params.hasher.literal_byte_score = value as i32,
252 BROTLI_METABLOCK_CALLBACK => params.log_meta_block = value != 0,
253 BROTLI_PARAM_LGWIN => params.lgwin = value as i32,
254 BROTLI_PARAM_LGBLOCK => params.lgblock = value as i32,
255 BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING => {
256 if value != 0 && value != 1 {
257 return false;
258 }
259 params.disable_literal_context_modeling = if value != 0 { 1 } else { 0 };
260 }
261 BROTLI_PARAM_SIZE_HINT => params.size_hint = value as usize,
262 BROTLI_PARAM_LARGE_WINDOW => params.large_window = value != 0,
263 BROTLI_PARAM_AVOID_DISTANCE_PREFIX_SEARCH => {
264 params.avoid_distance_prefix_search = value != 0
265 }
266 BROTLI_PARAM_CATABLE => {
267 params.catable = value != 0;
268 if !params.appendable {
269 params.appendable = value != 0;
270 }
271 params.use_dictionary = (value == 0);
272 }
273 BROTLI_PARAM_APPENDABLE => params.appendable = value != 0,
274 BROTLI_PARAM_MAGIC_NUMBER => params.magic_number = value != 0,
275 BROTLI_PARAM_FAVOR_EFFICIENCY => params.favor_cpu_efficiency = value != 0,
276 BROTLI_PARAM_BYTE_ALIGN => params.byte_align = value != 0,
277 BROTLI_PARAM_BARE_STREAM => {
278 params.bare_stream = value != 0;
279 if !params.byte_align {
280 params.byte_align = value != 0;
281 }
282 }
283 _ => return false,
284 }
285 true
286}
287
288impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
289 pub fn set_parameter(&mut self, p: BrotliEncoderParameter, value: u32) -> bool {
290 if self.is_initialized_ {
291 false
292 } else {
293 set_parameter(&mut self.params, p, value)
294 }
295 }
296}
297
298pub const BROTLI_LARGE_MAX_DISTANCE_BITS: u32 = 62;
300pub const BROTLI_LARGE_MIN_WBITS: u32 = 10;
301pub const BROTLI_LARGE_MAX_WBITS: u32 = 30;
302
303pub const BROTLI_MAX_DISTANCE_BITS: u32 = 24;
304pub const BROTLI_MAX_WINDOW_BITS: usize = BROTLI_MAX_DISTANCE_BITS as usize;
305pub const BROTLI_MAX_DISTANCE: usize = 0x03ff_fffc;
306pub const BROTLI_MAX_ALLOWED_DISTANCE: usize = 0x07ff_fffc;
307pub const BROTLI_NUM_DISTANCE_SHORT_CODES: u32 = 16;
308pub fn BROTLI_DISTANCE_ALPHABET_SIZE(NPOSTFIX: u32, NDIRECT: u32, MAXNBITS: u32) -> u32 {
309 BROTLI_NUM_DISTANCE_SHORT_CODES + (NDIRECT) + ((MAXNBITS) << ((NPOSTFIX) + 1))
310}
311
312pub const BROTLI_NUM_DISTANCE_SYMBOLS: usize = 1128;
317
318pub fn BrotliEncoderInitParams() -> BrotliEncoderParams {
319 BrotliEncoderParams {
320 dist: BrotliDistanceParams {
321 distance_postfix_bits: 0,
322 num_direct_distance_codes: 0,
323 alphabet_size: BROTLI_DISTANCE_ALPHABET_SIZE(0, 0, BROTLI_MAX_DISTANCE_BITS),
324 max_distance: BROTLI_MAX_DISTANCE,
325 },
326 mode: BrotliEncoderMode::BROTLI_MODE_GENERIC,
327 log_meta_block: false,
328 large_window: false,
329 avoid_distance_prefix_search: false,
330 quality: 11,
331 q9_5: false,
332 lgwin: 22i32,
333 lgblock: 0i32,
334 size_hint: 0usize,
335 disable_literal_context_modeling: 0i32,
336 stride_detection_quality: 0,
337 high_entropy_detection_quality: 0,
338 cdf_adaptation_detection: 0,
339 prior_bitmask_detection: 0,
340 literal_adaptation: [(0, 0); 4],
341 byte_align: false,
342 bare_stream: false,
343 catable: false,
344 use_dictionary: true,
345 appendable: false,
346 magic_number: false,
347 favor_cpu_efficiency: false,
348 hasher: BrotliHasherParams {
349 type_: 6,
350 block_bits: 9 - 1,
351 bucket_bits: 15,
352 hash_len: 5,
353 num_last_distances_to_check: 16,
354 literal_byte_score: 0,
355 },
356 }
357}
358
359impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
360 fn extend_last_command(&mut self, bytes: &mut u32, wrapped_last_processed_pos: &mut u32) {
361 let last_command = &mut self.commands_.slice_mut()[self.num_commands_ - 1];
362
363 let mask = self.ringbuffer_.mask_;
364 let max_backward_distance: u64 = (1u64 << self.params.lgwin) - BROTLI_WINDOW_GAP as u64;
365 let last_copy_len = u64::from(last_command.copy_len_) & 0x01ff_ffff;
366 let last_processed_pos: u64 = self.last_processed_pos_ - last_copy_len;
367 let max_distance: u64 = if last_processed_pos < max_backward_distance {
368 last_processed_pos
369 } else {
370 max_backward_distance
371 };
372 let cmd_dist: u64 = self.dist_cache_[0] as u64;
373 let distance_code: u32 = last_command.restore_distance_code(&self.params.dist);
374 if (distance_code < BROTLI_NUM_DISTANCE_SHORT_CODES
375 || distance_code as u64 - (BROTLI_NUM_DISTANCE_SHORT_CODES - 1) as u64 == cmd_dist)
376 {
377 if (cmd_dist <= max_distance) {
378 while (*bytes != 0
379 && self.ringbuffer_.data_mo.slice()[self.ringbuffer_.buffer_index
380 + (*wrapped_last_processed_pos as usize & mask as usize)]
381 == self.ringbuffer_.data_mo.slice()[self.ringbuffer_.buffer_index
382 + (((*wrapped_last_processed_pos as usize)
383 .wrapping_sub(cmd_dist as usize))
384 & mask as usize)])
385 {
386 last_command.copy_len_ += 1;
387 (*bytes) -= 1;
388 (*wrapped_last_processed_pos) += 1;
389 }
390 }
391 get_length_code(
393 last_command.insert_len_ as usize,
394 ((last_command.copy_len_ & 0x01ff_ffff) as i32
395 + (last_command.copy_len_ >> 25) as i32) as usize,
396 (last_command.dist_prefix_ & 0x03ff) == 0,
397 &mut last_command.cmd_prefix_,
398 );
399 }
400 }
401}
402
403fn RingBufferInit<AllocU8: alloc::Allocator<u8>>() -> RingBuffer<AllocU8> {
404 RingBuffer {
405 size_: 0,
406 mask_: 0, tail_size_: 0,
408 total_size_: 0,
409
410 cur_size_: 0,
411 pos_: 0,
412 data_mo: AllocU8::AllocatedMemory::default(),
413 buffer_index: 0usize,
414 }
415}
416
417impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
418 pub fn new(m8: Alloc) -> Self {
419 let cache: [i32; 16] = [4, 11, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
420 Self {
421 params: BrotliEncoderInitParams(),
422 input_pos_: 0,
423 num_commands_: 0,
424 num_literals_: 0,
425 last_insert_len_: 0,
426 last_flush_pos_: 0,
427 last_processed_pos_: 0,
428 prev_byte_: 0,
429 prev_byte2_: 0,
430 storage_size_: 0,
431 storage_: alloc_default::<u8, Alloc>(),
432 hasher_: UnionHasher::<Alloc>::default(),
433 large_table_: alloc_default::<i32, Alloc>(),
434 cmd_code_numbits_: 0,
436 command_buf_: alloc_default::<u32, Alloc>(),
437 literal_buf_: alloc_default::<u8, Alloc>(),
438 next_out_: NextOut::None,
439 available_out_: 0,
440 total_out_: 0,
441 is_first_mb: IsFirst::NothingWritten,
442 stream_state_: BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING,
443 is_last_block_emitted_: false,
444 is_initialized_: false,
445 ringbuffer_: RingBufferInit(),
446 commands_: alloc_default::<Command, Alloc>(),
447 cmd_alloc_size_: 0,
448 dist_cache_: cache,
449 saved_dist_cache_: [cache[0], cache[1], cache[2], cache[3]],
450 cmd_bits_: [0; 128],
451 cmd_depths_: [0; 128],
452 last_bytes_: 0,
453 last_bytes_bits_: 0,
454 cmd_code_: [0; 512],
455 m8,
456 remaining_metadata_bytes_: 0,
457 small_table_: [0; 1024],
458 tiny_buf_: [0; 16],
459 literal_scratch_space: HistogramLiteral::make_nnz_storage(),
460 command_scratch_space: HistogramCommand::make_nnz_storage(),
461 distance_scratch_space: HistogramDistance::make_nnz_storage(),
462 recoder_state: RecoderState::new(),
463 custom_dictionary: false,
464 custom_dictionary_size: None,
465 }
466 }
467}
468
469fn RingBufferFree<AllocU8: alloc::Allocator<u8>>(m: &mut AllocU8, rb: &mut RingBuffer<AllocU8>) {
470 m.free_cell(core::mem::take(&mut rb.data_mo));
471}
472fn DestroyHasher<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
473 m16: &mut Alloc,
474 handle: &mut UnionHasher<Alloc>,
475) {
476 handle.free(m16);
477}
478impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
513 fn cleanup(&mut self) {
514 <Alloc as Allocator<u8>>::free_cell(&mut self.m8, core::mem::take(&mut self.storage_));
515 <Alloc as Allocator<Command>>::free_cell(
516 &mut self.m8,
517 core::mem::take(&mut self.commands_),
518 );
519 RingBufferFree(&mut self.m8, &mut self.ringbuffer_);
520 DestroyHasher(&mut self.m8, &mut self.hasher_);
521 <Alloc as Allocator<i32>>::free_cell(&mut self.m8, core::mem::take(&mut self.large_table_));
522 <Alloc as Allocator<u32>>::free_cell(&mut self.m8, core::mem::take(&mut self.command_buf_));
523 <Alloc as Allocator<u8>>::free_cell(&mut self.m8, core::mem::take(&mut self.literal_buf_));
524 }
525}
526
527pub fn BrotliEncoderDestroyInstance<Alloc: BrotliAlloc>(s: &mut BrotliEncoderStateStruct<Alloc>) {
534 s.cleanup()
535}
536
537#[cfg(not(feature = "disallow_large_window_size"))]
538fn check_large_window_ok() -> bool {
539 true
540}
541#[cfg(feature = "disallow_large_window_size")]
542fn check_large_window_ok() -> bool {
543 false
544}
545
546pub fn SanitizeParams(params: &mut BrotliEncoderParams) {
547 params.quality = min(11i32, max(0i32, params.quality));
548 if params.lgwin < 10i32 {
549 params.lgwin = 10i32;
550 } else if params.lgwin > 24i32 {
551 if params.large_window && check_large_window_ok() {
552 if params.lgwin > 30i32 {
553 params.lgwin = 30i32;
554 }
555 } else {
556 params.lgwin = 24i32;
557 }
558 }
559 if params.catable {
560 params.appendable = true;
561 params.use_dictionary = false;
562 }
563 if params.bare_stream {
564 params.byte_align = true;
565 } else if !params.appendable {
566 params.byte_align = false;
567 }
568}
569
570fn ComputeLgBlock(params: &BrotliEncoderParams) -> i32 {
571 let mut lgblock: i32 = params.lgblock;
572 if params.quality == 0i32 || params.quality == 1i32 {
573 lgblock = params.lgwin;
574 } else if params.quality < 4i32 {
575 lgblock = 14i32;
576 } else if lgblock == 0i32 {
577 lgblock = 16i32;
578 if params.quality >= 9i32 && (params.lgwin > lgblock) {
579 lgblock = min(18i32, params.lgwin);
580 }
581 } else {
582 lgblock = min(24i32, max(16i32, lgblock));
583 }
584 lgblock
585}
586
587fn ComputeRbBits(params: &BrotliEncoderParams) -> i32 {
588 1i32 + max(params.lgwin, params.lgblock)
589}
590
591fn RingBufferSetup<AllocU8: alloc::Allocator<u8>>(
592 params: &BrotliEncoderParams,
593 rb: &mut RingBuffer<AllocU8>,
594) {
595 let window_bits: i32 = ComputeRbBits(params);
596 let tail_bits: i32 = params.lgblock;
597 rb.size_ = 1u32 << window_bits;
598 rb.mask_ = (1u32 << window_bits).wrapping_sub(1);
599 rb.tail_size_ = 1u32 << tail_bits;
600 rb.total_size_ = rb.size_.wrapping_add(rb.tail_size_);
601}
602
603fn EncodeWindowBits(
604 lgwin: i32,
605 large_window: bool,
606 last_bytes: &mut u16,
607 last_bytes_bits: &mut u8,
608) {
609 if large_window {
610 *last_bytes = (((lgwin & 0x3F) << 8) | 0x11) as u16;
611 *last_bytes_bits = 14;
612 } else if lgwin == 16i32 {
613 *last_bytes = 0u16;
614 *last_bytes_bits = 1u8;
615 } else if lgwin == 17i32 {
616 *last_bytes = 1u16;
617 *last_bytes_bits = 7u8;
618 } else if lgwin > 17i32 {
619 *last_bytes = ((lgwin - 17i32) << 1 | 1i32) as u16;
620 *last_bytes_bits = 4u8;
621 } else {
622 *last_bytes = ((lgwin - 8i32) << 4 | 1i32) as u16;
623 *last_bytes_bits = 7u8;
624 }
625}
626
627fn InitCommandPrefixCodes(
628 cmd_depths: &mut [u8],
629 cmd_bits: &mut [u16],
630 cmd_code: &mut [u8],
631 cmd_code_numbits: &mut usize,
632) {
633 static kDefaultCommandDepths: [u8; 128] = [
634 0, 4, 4, 5, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 0, 0, 0, 4, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6,
635 7, 7, 7, 7, 10, 10, 10, 10, 10, 10, 0, 4, 4, 5, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, 10, 10, 10,
636 10, 10, 10, 10, 10, 10, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6,
637 6, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 7, 7, 7, 8, 10, 12, 12,
638 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0,
639 ];
640 static kDefaultCommandBits: [u16; 128] = [
641 0, 0, 8, 9, 3, 35, 7, 71, 39, 103, 23, 47, 175, 111, 239, 31, 0, 0, 0, 4, 12, 2, 10, 6, 13,
642 29, 11, 43, 27, 59, 87, 55, 15, 79, 319, 831, 191, 703, 447, 959, 0, 14, 1, 25, 5, 21, 19,
643 51, 119, 159, 95, 223, 479, 991, 63, 575, 127, 639, 383, 895, 255, 767, 511, 1023, 14, 0,
644 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 59, 7, 39, 23, 55, 30, 1, 17, 9, 25, 5, 0, 8,
645 4, 12, 2, 10, 6, 21, 13, 29, 3, 19, 11, 15, 47, 31, 95, 63, 127, 255, 767, 2815, 1791,
646 3839, 511, 2559, 1535, 3583, 1023, 3071, 2047, 4095, 0, 0, 0, 0,
647 ];
648 static kDefaultCommandCode: [u8; 57] = [
649 0xff, 0x77, 0xd5, 0xbf, 0xe7, 0xde, 0xea, 0x9e, 0x51, 0x5d, 0xde, 0xc6, 0x70, 0x57, 0xbc,
650 0x58, 0x58, 0x58, 0xd8, 0xd8, 0x58, 0xd5, 0xcb, 0x8c, 0xea, 0xe0, 0xc3, 0x87, 0x1f, 0x83,
651 0xc1, 0x60, 0x1c, 0x67, 0xb2, 0xaa, 0x6, 0x83, 0xc1, 0x60, 0x30, 0x18, 0xcc, 0xa1, 0xce,
652 0x88, 0x54, 0x94, 0x46, 0xe1, 0xb0, 0xd0, 0x4e, 0xb2, 0xf7, 0x4, 0x0,
653 ];
654 static kDefaultCommandCodeNumBits: usize = 448usize;
655 cmd_depths[..].clone_from_slice(&kDefaultCommandDepths[..]);
656 cmd_bits[..].clone_from_slice(&kDefaultCommandBits[..]);
657 cmd_code[..kDefaultCommandCode.len()].clone_from_slice(&kDefaultCommandCode[..]);
658 *cmd_code_numbits = kDefaultCommandCodeNumBits;
659}
660
661impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
662 fn ensure_initialized(&mut self) -> bool {
663 if self.is_initialized_ {
664 return true;
665 }
666 SanitizeParams(&mut self.params);
667 self.params.lgblock = ComputeLgBlock(&mut self.params);
668 ChooseDistanceParams(&mut self.params);
669 self.remaining_metadata_bytes_ = u32::MAX;
670 RingBufferSetup(&mut self.params, &mut self.ringbuffer_);
671 {
672 let mut lgwin: i32 = self.params.lgwin;
673 if self.params.quality == 0i32 || self.params.quality == 1i32 {
674 lgwin = max(lgwin, 18i32);
675 }
676 if !(self.params.catable && self.params.bare_stream) {
677 EncodeWindowBits(
678 lgwin,
679 self.params.large_window,
680 &mut self.last_bytes_,
681 &mut self.last_bytes_bits_,
682 );
683 }
684 }
685 if self.params.quality == 0i32 {
686 InitCommandPrefixCodes(
687 &mut self.cmd_depths_[..],
688 &mut self.cmd_bits_[..],
689 &mut self.cmd_code_[..],
690 &mut self.cmd_code_numbits_,
691 );
692 }
693 if self.params.catable {
694 for item in self.dist_cache_.iter_mut() {
698 *item = 0x7ffffff0;
699 }
700 for item in self.saved_dist_cache_.iter_mut() {
701 *item = 0x7ffffff0;
702 }
703 }
704 self.is_initialized_ = true;
705 true
706 }
707}
708
709fn RingBufferInitBuffer<AllocU8: alloc::Allocator<u8>>(
710 m: &mut AllocU8,
711 buflen: u32,
712 rb: &mut RingBuffer<AllocU8>,
713) {
714 static kSlackForEightByteHashingEverywhere: usize = 7usize;
715 let mut new_data = m.alloc_cell(
716 ((2u32).wrapping_add(buflen) as usize).wrapping_add(kSlackForEightByteHashingEverywhere),
717 );
718 if !rb.data_mo.slice().is_empty() {
719 let lim: usize = ((2u32).wrapping_add(rb.cur_size_) as usize)
720 .wrapping_add(kSlackForEightByteHashingEverywhere);
721 new_data.slice_mut()[..lim].clone_from_slice(&rb.data_mo.slice()[..lim]);
722 m.free_cell(core::mem::take(&mut rb.data_mo));
723 }
724 let _ = core::mem::replace(&mut rb.data_mo, new_data);
725 rb.cur_size_ = buflen;
726 rb.buffer_index = 2usize;
727 rb.data_mo.slice_mut()[(rb.buffer_index.wrapping_sub(2))] = 0;
728 rb.data_mo.slice_mut()[(rb.buffer_index.wrapping_sub(1))] = 0;
729 for i in 0usize..kSlackForEightByteHashingEverywhere {
730 rb.data_mo.slice_mut()[rb
731 .buffer_index
732 .wrapping_add(rb.cur_size_ as usize)
733 .wrapping_add(i)] = 0;
734 }
735}
736
737fn RingBufferWriteTail<AllocU8: alloc::Allocator<u8>>(
738 bytes: &[u8],
739 n: usize,
740 rb: &mut RingBuffer<AllocU8>,
741) {
742 let masked_pos: usize = (rb.pos_ & rb.mask_) as usize;
743 if masked_pos < rb.tail_size_ as usize {
744 let p: usize = (rb.size_ as usize).wrapping_add(masked_pos);
745 let begin = rb.buffer_index.wrapping_add(p);
746 let lim = min(n, (rb.tail_size_ as usize).wrapping_sub(masked_pos));
747 rb.data_mo.slice_mut()[begin..(begin + lim)].clone_from_slice(&bytes[..lim]);
748 }
749}
750
751fn RingBufferWrite<AllocU8: alloc::Allocator<u8>>(
752 m: &mut AllocU8,
753 bytes: &[u8],
754 n: usize,
755 rb: &mut RingBuffer<AllocU8>,
756) {
757 if rb.pos_ == 0u32 && (n < rb.tail_size_ as usize) {
758 rb.pos_ = n as u32;
759 RingBufferInitBuffer(m, rb.pos_, rb);
760 rb.data_mo.slice_mut()[rb.buffer_index..(rb.buffer_index + n)]
761 .clone_from_slice(&bytes[..n]);
762 return;
763 }
764 if rb.cur_size_ < rb.total_size_ {
765 RingBufferInitBuffer(m, rb.total_size_, rb);
766 rb.data_mo.slice_mut()[rb
767 .buffer_index
768 .wrapping_add(rb.size_ as usize)
769 .wrapping_sub(2)] = 0u8;
770 rb.data_mo.slice_mut()[rb
771 .buffer_index
772 .wrapping_add(rb.size_ as usize)
773 .wrapping_sub(1)] = 0u8;
774 }
775 {
776 let masked_pos: usize = (rb.pos_ & rb.mask_) as usize;
777 RingBufferWriteTail(bytes, n, rb);
778 if masked_pos.wrapping_add(n) <= rb.size_ as usize {
779 let start = rb.buffer_index.wrapping_add(masked_pos);
781 rb.data_mo.slice_mut()[start..(start + n)].clone_from_slice(&bytes[..n]);
782 } else {
783 {
784 let start = rb.buffer_index.wrapping_add(masked_pos);
785 let mid = min(n, (rb.total_size_ as usize).wrapping_sub(masked_pos));
786 rb.data_mo.slice_mut()[start..(start + mid)].clone_from_slice(&bytes[..mid]);
787 }
788 let xstart = rb.buffer_index.wrapping_add(0);
789 let size = n.wrapping_sub((rb.size_ as usize).wrapping_sub(masked_pos));
790 let bytes_start = (rb.size_ as usize).wrapping_sub(masked_pos);
791 rb.data_mo.slice_mut()[xstart..(xstart + size)]
792 .clone_from_slice(&bytes[bytes_start..(bytes_start + size)]);
793 }
794 }
795 let data_2 = rb.data_mo.slice()[rb
796 .buffer_index
797 .wrapping_add(rb.size_ as usize)
798 .wrapping_sub(2)];
799 rb.data_mo.slice_mut()[rb.buffer_index.wrapping_sub(2)] = data_2;
800 let data_1 = rb.data_mo.slice()[rb
801 .buffer_index
802 .wrapping_add(rb.size_ as usize)
803 .wrapping_sub(1)];
804 rb.data_mo.slice_mut()[rb.buffer_index.wrapping_sub(1)] = data_1;
805 rb.pos_ = rb.pos_.wrapping_add(n as u32);
806 if rb.pos_ > 1u32 << 30 {
807 rb.pos_ = rb.pos_ & (1u32 << 30).wrapping_sub(1) | 1u32 << 30;
808 }
809}
810
811impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
812 pub fn copy_input_to_ring_buffer(&mut self, input_size: usize, input_buffer: &[u8]) {
813 if !self.ensure_initialized() {
814 return;
815 }
816 RingBufferWrite(
817 &mut self.m8,
818 input_buffer,
819 input_size,
820 &mut self.ringbuffer_,
821 );
822 self.input_pos_ = self.input_pos_.wrapping_add(input_size as u64);
823 if (self.ringbuffer_).pos_ <= (self.ringbuffer_).mask_ {
824 let start = (self.ringbuffer_)
825 .buffer_index
826 .wrapping_add((self.ringbuffer_).pos_ as usize);
827 for item in (self.ringbuffer_).data_mo.slice_mut()[start..(start + 7)].iter_mut() {
828 *item = 0;
829 }
830 }
831 }
832}
833
834fn ChooseHasher(params: &mut BrotliEncoderParams) {
835 let hparams = &mut params.hasher;
836 if params.quality >= 10 && !params.q9_5 {
837 hparams.type_ = 10;
838 } else if params.quality == 10 {
839 hparams.type_ = 9;
841 hparams.num_last_distances_to_check = H9_NUM_LAST_DISTANCES_TO_CHECK as i32;
842 hparams.block_bits = H9_BLOCK_BITS as i32;
843 hparams.bucket_bits = H9_BUCKET_BITS as i32;
844 hparams.hash_len = 4;
845 } else if params.quality == 9 {
846 hparams.type_ = 9;
847 hparams.num_last_distances_to_check = H9_NUM_LAST_DISTANCES_TO_CHECK as i32;
848 hparams.block_bits = H9_BLOCK_BITS as i32;
849 hparams.bucket_bits = H9_BUCKET_BITS as i32;
850 hparams.hash_len = 4;
851 } else if params.quality == 4 && (params.size_hint >= (1i32 << 20) as usize) {
852 hparams.type_ = 54i32;
853 } else if params.quality < 5 {
854 hparams.type_ = params.quality;
855 } else if params.lgwin <= 16 {
856 hparams.type_ = if params.quality < 7 {
857 40i32
858 } else if params.quality < 9 {
859 41i32
860 } else {
861 42i32
862 };
863 } else if ((params.q9_5 && params.size_hint > (1 << 20)) || params.size_hint > (1 << 22))
864 && (params.lgwin >= 19i32)
865 {
866 hparams.type_ = 6i32;
867 hparams.block_bits = min(params.quality - 1, 9);
868 hparams.bucket_bits = 15i32;
869 hparams.hash_len = 5i32;
870 hparams.num_last_distances_to_check = if params.quality < 7 {
871 4i32
872 } else if params.quality < 9 {
873 10i32
874 } else {
875 16i32
876 };
877 } else {
878 hparams.type_ = 5i32;
879 hparams.block_bits = min(params.quality - 1, 9);
880 hparams.bucket_bits = if params.quality < 7 && params.size_hint <= (1 << 20) {
881 14i32
882 } else {
883 15i32
884 };
885 hparams.num_last_distances_to_check = if params.quality < 7 {
886 4i32
887 } else if params.quality < 9 {
888 10i32
889 } else {
890 16i32
891 };
892 }
893}
894
895fn InitializeH2<AllocU32: alloc::Allocator<u32>>(
896 m32: &mut AllocU32,
897 params: &BrotliEncoderParams,
898) -> BasicHasher<H2Sub<AllocU32>> {
899 BasicHasher {
900 GetHasherCommon: Struct1 {
901 params: params.hasher,
902 is_prepared_: 1,
903 dict_num_lookups: 0,
904 dict_num_matches: 0,
905 },
906 buckets_: H2Sub {
907 buckets_: m32.alloc_cell(65537 + 8),
908 },
909 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
910 }
911}
912fn InitializeH3<AllocU32: alloc::Allocator<u32>>(
913 m32: &mut AllocU32,
914 params: &BrotliEncoderParams,
915) -> BasicHasher<H3Sub<AllocU32>> {
916 BasicHasher {
917 GetHasherCommon: Struct1 {
918 params: params.hasher,
919 is_prepared_: 1,
920 dict_num_lookups: 0,
921 dict_num_matches: 0,
922 },
923 buckets_: H3Sub {
924 buckets_: m32.alloc_cell(65538 + 8),
925 },
926 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
927 }
928}
929fn InitializeH4<AllocU32: alloc::Allocator<u32>>(
930 m32: &mut AllocU32,
931 params: &BrotliEncoderParams,
932) -> BasicHasher<H4Sub<AllocU32>> {
933 BasicHasher {
934 GetHasherCommon: Struct1 {
935 params: params.hasher,
936 is_prepared_: 1,
937 dict_num_lookups: 0,
938 dict_num_matches: 0,
939 },
940 buckets_: H4Sub {
941 buckets_: m32.alloc_cell(131072 + 8),
942 },
943 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
944 }
945}
946fn InitializeH54<AllocU32: alloc::Allocator<u32>>(
947 m32: &mut AllocU32,
948 params: &BrotliEncoderParams,
949) -> BasicHasher<H54Sub<AllocU32>> {
950 BasicHasher {
951 GetHasherCommon: Struct1 {
952 params: params.hasher,
953 is_prepared_: 1,
954 dict_num_lookups: 0,
955 dict_num_matches: 0,
956 },
957 buckets_: H54Sub {
958 buckets_: m32.alloc_cell(1048580 + 8),
959 },
960 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
961 }
962}
963
964fn InitializeH9<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
965 m16: &mut Alloc,
966 params: &BrotliEncoderParams,
967) -> H9<Alloc> {
968 H9 {
969 dict_search_stats_: Struct1 {
970 params: params.hasher,
971 is_prepared_: 1,
972 dict_num_lookups: 0,
973 dict_num_matches: 0,
974 },
975 num_: allocate::<u16, _>(m16, 1 << H9_BUCKET_BITS),
976 buckets_: allocate::<u32, _>(m16, H9_BLOCK_SIZE << H9_BUCKET_BITS),
977 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
978 }
979}
980
981fn InitializeH5<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
982 m16: &mut Alloc,
983 params: &BrotliEncoderParams,
984) -> UnionHasher<Alloc> {
985 let block_size = 1u64 << params.hasher.block_bits;
986 let bucket_size = 1u64 << params.hasher.bucket_bits;
987 let buckets: <Alloc as Allocator<u32>>::AllocatedMemory =
988 allocate::<u32, _>(m16, (bucket_size * block_size) as usize);
989 let num: <Alloc as Allocator<u16>>::AllocatedMemory =
990 allocate::<u16, _>(m16, bucket_size as usize);
991
992 if params.hasher.block_bits == (HQ5Sub {}).block_bits()
993 && (1 << params.hasher.bucket_bits) == (HQ5Sub {}).bucket_size()
994 {
995 return UnionHasher::H5q5(AdvHasher {
996 buckets,
997 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
998 num,
999 GetHasherCommon: Struct1 {
1000 params: params.hasher,
1001 is_prepared_: 1,
1002 dict_num_lookups: 0,
1003 dict_num_matches: 0,
1004 },
1005 specialization: HQ5Sub {},
1006 });
1007 }
1008 if params.hasher.block_bits == (HQ7Sub {}).block_bits()
1009 && (1 << params.hasher.bucket_bits) == (HQ7Sub {}).bucket_size()
1010 {
1011 return UnionHasher::H5q7(AdvHasher {
1012 buckets,
1013 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
1014 num,
1015 GetHasherCommon: Struct1 {
1016 params: params.hasher,
1017 is_prepared_: 1,
1018 dict_num_lookups: 0,
1019 dict_num_matches: 0,
1020 },
1021 specialization: HQ7Sub {},
1022 });
1023 }
1024 UnionHasher::H5(AdvHasher {
1025 buckets,
1026 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
1027 num,
1028 GetHasherCommon: Struct1 {
1029 params: params.hasher,
1030 is_prepared_: 1,
1031 dict_num_lookups: 0,
1032 dict_num_matches: 0,
1033 },
1034 specialization: H5Sub {
1035 hash_shift_: 32i32 - params.hasher.bucket_bits,
1036 bucket_size_: bucket_size as u32,
1037 block_bits_: params.hasher.block_bits,
1038 block_mask_: block_size.wrapping_sub(1) as u32,
1039 },
1040 })
1041}
1042fn InitializeH6<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1043 m16: &mut Alloc,
1044 params: &BrotliEncoderParams,
1045) -> UnionHasher<Alloc> {
1046 let block_size = 1u64 << params.hasher.block_bits;
1047 let bucket_size = 1u64 << params.hasher.bucket_bits;
1048 let buckets: <Alloc as Allocator<u32>>::AllocatedMemory =
1049 allocate::<u32, _>(m16, (bucket_size * block_size) as usize);
1050 let num: <Alloc as Allocator<u16>>::AllocatedMemory =
1051 allocate::<u16, _>(m16, bucket_size as usize);
1052 UnionHasher::H6(AdvHasher {
1053 buckets,
1054 num,
1055 h9_opts: super::backward_references::H9Opts::new(¶ms.hasher),
1056 GetHasherCommon: Struct1 {
1057 params: params.hasher,
1058 is_prepared_: 1,
1059 dict_num_lookups: 0,
1060 dict_num_matches: 0,
1061 },
1062 specialization: H6Sub {
1063 bucket_size_: 1u32 << params.hasher.bucket_bits,
1064 block_bits_: params.hasher.block_bits,
1065 block_mask_: block_size.wrapping_sub(1) as u32,
1066 hash_mask: 0xffffffffffffffffu64 >> (64i32 - 8i32 * params.hasher.hash_len),
1067 hash_shift_: 64i32 - params.hasher.bucket_bits,
1068 },
1069 })
1070}
1071
1072fn BrotliMakeHasher<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1073 m: &mut Alloc,
1074 params: &BrotliEncoderParams,
1075 ringbuffer_break: Option<core::num::NonZeroUsize>,
1076) -> UnionHasher<Alloc> {
1077 let hasher_type: i32 = params.hasher.type_;
1078 if hasher_type == 2i32 {
1079 return UnionHasher::H2(InitializeH2(m, params));
1080 }
1081 if hasher_type == 3i32 {
1082 return UnionHasher::H3(InitializeH3(m, params));
1083 }
1084 if hasher_type == 4i32 {
1085 return UnionHasher::H4(InitializeH4(m, params));
1086 }
1087 if hasher_type == 5i32 {
1088 return InitializeH5(m, params);
1089 }
1090 if hasher_type == 6i32 {
1091 return InitializeH6(m, params);
1092 }
1093 if hasher_type == 9i32 {
1094 return UnionHasher::H9(InitializeH9(m, params));
1095 }
1096 if hasher_type == 54i32 {
1108 return UnionHasher::H54(InitializeH54(m, params));
1109 }
1110 if hasher_type == 10i32 {
1111 return UnionHasher::H10(InitializeH10(m, false, params, ringbuffer_break, 0));
1112 }
1113 InitializeH6(m, params)
1115
1116 }
1118fn HasherReset<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(t: &mut UnionHasher<Alloc>) {
1119 match t {
1120 &mut UnionHasher::Uninit => {}
1121 _ => (t.GetHasherCommon()).is_prepared_ = 0i32,
1122 };
1123}
1124
1125pub(crate) fn hasher_setup<Alloc: Allocator<u16> + Allocator<u32>>(
1126 m16: &mut Alloc,
1127 handle: &mut UnionHasher<Alloc>,
1128 params: &mut BrotliEncoderParams,
1129 ringbuffer_break: Option<core::num::NonZeroUsize>,
1130 data: &[u8],
1131 position: usize,
1132 input_size: usize,
1133 is_last: bool,
1134) {
1135 let one_shot = position == 0 && is_last;
1136 let is_uninit = match (handle) {
1137 &mut UnionHasher::Uninit => true,
1138 _ => false,
1139 };
1140 if is_uninit {
1141 ChooseHasher(&mut (*params));
1143 *handle = BrotliMakeHasher(m16, params, ringbuffer_break);
1146 handle.GetHasherCommon().params = params.hasher;
1147 HasherReset(handle); handle.GetHasherCommon().is_prepared_ = 1;
1149 } else {
1150 match handle.Prepare(one_shot, input_size, data) {
1151 HowPrepared::ALREADY_PREPARED => {}
1152 HowPrepared::NEWLY_PREPARED => {
1153 if position == 0usize {
1154 let common = handle.GetHasherCommon();
1155 common.dict_num_lookups = 0usize;
1156 common.dict_num_matches = 0usize;
1157 }
1158 }
1159 }
1160 }
1161}
1162
1163fn HasherPrependCustomDictionary<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1164 m: &mut Alloc,
1165 handle: &mut UnionHasher<Alloc>,
1166 params: &mut BrotliEncoderParams,
1167 ringbuffer_break: Option<core::num::NonZeroUsize>,
1168 size: usize,
1169 dict: &[u8],
1170) {
1171 hasher_setup(
1172 m,
1173 handle,
1174 params,
1175 ringbuffer_break,
1176 dict,
1177 0usize,
1178 size,
1179 false,
1180 );
1181 match handle {
1182 &mut UnionHasher::H2(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1183 &mut UnionHasher::H3(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1184 &mut UnionHasher::H4(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1185 &mut UnionHasher::H5(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1186 &mut UnionHasher::H5q7(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1187 &mut UnionHasher::H5q5(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1188 &mut UnionHasher::H6(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1189 &mut UnionHasher::H9(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1190 &mut UnionHasher::H54(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1191 &mut UnionHasher::H10(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1192 &mut UnionHasher::Uninit => panic!("Uninitialized"),
1193 }
1194}
1195
1196impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1197 pub fn set_custom_dictionary(&mut self, size: usize, dict: &[u8]) {
1198 self.set_custom_dictionary_with_optional_precomputed_hasher(
1199 size,
1200 dict,
1201 UnionHasher::Uninit,
1202 false,
1203 )
1204 }
1205
1206 pub fn set_custom_dictionary_with_optional_precomputed_hasher(
1207 &mut self,
1208 size: usize,
1209 mut dict: &[u8],
1210 opt_hasher: UnionHasher<Alloc>,
1211 is_multithreading_file_continue: bool,
1212 ) {
1213 self.params.use_dictionary = false;
1214
1215 self.prev_byte_ = 0;
1216 self.prev_byte2_ = 0;
1217 if is_multithreading_file_continue {
1218 if size > 0 {
1219 self.prev_byte_ = dict[size.wrapping_sub(1)];
1220 }
1221 if size > 1 {
1222 self.prev_byte2_ = dict[size.wrapping_sub(2)];
1223 }
1224 }
1225
1226 let has_optional_hasher = if let UnionHasher::Uninit = opt_hasher {
1227 false
1228 } else {
1229 true
1230 };
1231 let max_dict_size: usize = (1usize << self.params.lgwin).wrapping_sub(16);
1232 self.hasher_ = opt_hasher;
1233 let mut dict_size: usize = size;
1234 if !self.ensure_initialized() {
1235 return;
1236 }
1237 if dict_size == 0 || self.params.quality == 0 || self.params.quality == 1 || size <= 1 {
1238 self.params.catable = true; self.params.appendable = true; return;
1241 }
1242 self.custom_dictionary = true;
1243 if size > max_dict_size {
1244 dict = &dict[size.wrapping_sub(max_dict_size)..];
1245 dict_size = max_dict_size;
1246 }
1247 self.custom_dictionary_size = core::num::NonZeroUsize::new(dict_size);
1248 self.copy_input_to_ring_buffer(dict_size, dict);
1249 self.last_flush_pos_ = dict_size as u64;
1250 self.last_processed_pos_ = dict_size as u64;
1251 let m16 = &mut self.m8;
1252 if cfg!(debug_assertions) || !has_optional_hasher {
1253 let mut orig_hasher = UnionHasher::Uninit;
1254 if has_optional_hasher {
1255 orig_hasher = core::mem::replace(&mut self.hasher_, UnionHasher::Uninit);
1256 }
1257 HasherPrependCustomDictionary(
1258 m16,
1259 &mut self.hasher_,
1260 &mut self.params,
1261 self.custom_dictionary_size,
1262 dict_size,
1263 dict,
1264 );
1265 if has_optional_hasher {
1266 debug_assert!(orig_hasher == self.hasher_);
1267 DestroyHasher(m16, &mut orig_hasher);
1268 }
1269 }
1270 }
1271}
1272
1273pub fn BrotliEncoderMaxCompressedSizeMulti(input_size: usize, num_threads: usize) -> usize {
1274 BrotliEncoderMaxCompressedSize(input_size) + num_threads * 8
1275}
1276
1277pub fn BrotliEncoderMaxCompressedSize(input_size: usize) -> usize {
1278 let magic_size = 16usize;
1279 let num_large_blocks: usize = input_size >> 14;
1280 let tail: usize = input_size.wrapping_sub(num_large_blocks << 24);
1281 let tail_overhead: usize = (if tail > (1i32 << 20) as usize {
1282 4i32
1283 } else {
1284 3i32
1285 }) as usize;
1286 let overhead: usize = (2usize)
1287 .wrapping_add((4usize).wrapping_mul(num_large_blocks))
1288 .wrapping_add(tail_overhead)
1289 .wrapping_add(1);
1290 let result: usize = input_size.wrapping_add(overhead);
1291 if input_size == 0usize {
1292 return 1 + magic_size;
1293 }
1294 if result < input_size {
1295 0usize
1296 } else {
1297 result + magic_size
1298 }
1299}
1300
1301fn InitOrStitchToPreviousBlock<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1302 m: &mut Alloc,
1303 handle: &mut UnionHasher<Alloc>,
1304 data: &[u8],
1305 mask: usize,
1306 ringbuffer_break: Option<core::num::NonZeroUsize>,
1307 params: &mut BrotliEncoderParams,
1308 position: usize,
1309 input_size: usize,
1310 is_last: bool,
1311) {
1312 hasher_setup(
1313 m,
1314 handle,
1315 params,
1316 ringbuffer_break,
1317 data,
1318 position,
1319 input_size,
1320 is_last,
1321 );
1322 handle.StitchToPreviousBlock(input_size, position, data, mask);
1323}
1324
1325fn should_compress(
1326 data: &[u8],
1327 mask: usize,
1328 last_flush_pos: u64,
1329 bytes: usize,
1330 num_literals: usize,
1331 num_commands: usize,
1332) -> bool {
1333 const K_SAMPLE_RATE: u32 = 13;
1334 const K_MIN_ENTROPY: floatX = 7.92;
1335
1336 if num_commands < (bytes >> 8) + 2 && num_literals as floatX > 0.99 * bytes as floatX {
1337 let mut literal_histo = [0u32; 256];
1338 let bit_cost_threshold = (bytes as floatX) * K_MIN_ENTROPY / (K_SAMPLE_RATE as floatX);
1339 let t = bytes
1340 .wrapping_add(K_SAMPLE_RATE as usize)
1341 .wrapping_sub(1)
1342 .wrapping_div(K_SAMPLE_RATE as usize);
1343 let mut pos = last_flush_pos as u32;
1344 for _ in 0..t {
1345 let value = &mut literal_histo[data[pos as usize & mask] as usize];
1346 *value = value.wrapping_add(1);
1347 pos = pos.wrapping_add(K_SAMPLE_RATE);
1348 }
1349 if BitsEntropy(&literal_histo[..], 256) > bit_cost_threshold {
1350 return false;
1351 }
1352 }
1353 true
1354}
1355
1356fn ChooseContextMode(
1358 params: &BrotliEncoderParams,
1359 data: &[u8],
1360 pos: usize,
1361 mask: usize,
1362 length: usize,
1363) -> ContextType {
1364 match params.mode {
1367 BrotliEncoderMode::BROTLI_FORCE_LSB_PRIOR => return ContextType::CONTEXT_LSB6,
1368 BrotliEncoderMode::BROTLI_FORCE_MSB_PRIOR => return ContextType::CONTEXT_MSB6,
1369 BrotliEncoderMode::BROTLI_FORCE_UTF8_PRIOR => return ContextType::CONTEXT_UTF8,
1370 BrotliEncoderMode::BROTLI_FORCE_SIGNED_PRIOR => return ContextType::CONTEXT_SIGNED,
1371 _ => {}
1372 }
1373 if (params.quality >= 10 && !is_mostly_utf8(data, pos, mask, length, kMinUTF8Ratio)) {
1374 return ContextType::CONTEXT_SIGNED;
1375 }
1376 ContextType::CONTEXT_UTF8
1377}
1378
1379#[derive(PartialEq, Eq, Copy, Clone)]
1380pub enum BrotliEncoderOperation {
1381 BROTLI_OPERATION_PROCESS = 0,
1382 BROTLI_OPERATION_FLUSH = 1,
1383 BROTLI_OPERATION_FINISH = 2,
1384 BROTLI_OPERATION_EMIT_METADATA = 3,
1385}
1386
1387#[allow(unused)]
1388fn MakeUncompressedStream(input: &[u8], input_size: usize, output: &mut [u8]) -> usize {
1389 let mut size: usize = input_size;
1390 let mut result: usize = 0usize;
1391 let mut offset: usize = 0usize;
1392 if input_size == 0usize {
1393 output[0] = 6u8;
1394 return 1;
1395 }
1396 output[result] = 0x21u8;
1397 result = result.wrapping_add(1);
1398 output[result] = 0x3u8;
1399 result = result.wrapping_add(1);
1400 while size > 0usize {
1401 let mut nibbles: u32 = 0u32;
1402
1403 let chunk_size: u32 = if size > (1u32 << 24) as usize {
1404 1u32 << 24
1405 } else {
1406 size as u32
1407 };
1408 if chunk_size > 1u32 << 16 {
1409 nibbles = if chunk_size > 1u32 << 20 { 2i32 } else { 1i32 } as u32;
1410 }
1411 let bits: u32 = nibbles << 1
1412 | chunk_size.wrapping_sub(1) << 3
1413 | 1u32 << (19u32).wrapping_add((4u32).wrapping_mul(nibbles));
1414 output[result] = bits as u8;
1415 result = result.wrapping_add(1);
1416 output[result] = (bits >> 8) as u8;
1417 result = result.wrapping_add(1);
1418 output[result] = (bits >> 16) as u8;
1419 result = result.wrapping_add(1);
1420 if nibbles == 2u32 {
1421 output[result] = (bits >> 24) as u8;
1422 result = result.wrapping_add(1);
1423 }
1424 output[result..(result + chunk_size as usize)]
1425 .clone_from_slice(&input[offset..(offset + chunk_size as usize)]);
1426 result = result.wrapping_add(chunk_size as usize);
1427 offset = offset.wrapping_add(chunk_size as usize);
1428 size = size.wrapping_sub(chunk_size as usize);
1429 }
1430 output[result] = 3u8;
1431 result = result.wrapping_add(1);
1432 result
1433}
1434
1435#[cfg_attr(not(feature = "ffi-api"), cfg(test))]
1436pub(crate) fn encoder_compress<
1437 Alloc: BrotliAlloc,
1438 MetablockCallback: FnMut(
1439 &mut interface::PredictionModeContextMap<InputReferenceMut>,
1440 &mut [interface::StaticCommand],
1441 interface::InputPair,
1442 &mut Alloc,
1443 ),
1444>(
1445 empty_m8: Alloc,
1446 m8: &mut Alloc,
1447 mut quality: i32,
1448 lgwin: i32,
1449 mode: BrotliEncoderMode,
1450 input_size: usize,
1451 input_buffer: &[u8],
1452 encoded_size: &mut usize,
1453 encoded_buffer: &mut [u8],
1454 metablock_callback: &mut MetablockCallback,
1455) -> bool {
1456 let out_size: usize = *encoded_size;
1457 let input_start = input_buffer;
1458 let output_start = encoded_buffer;
1459 let max_out_size: usize = BrotliEncoderMaxCompressedSize(input_size);
1460 if out_size == 0 {
1461 return false;
1462 }
1463 if input_size == 0 {
1464 *encoded_size = 1;
1465 output_start[0] = 6;
1466 return true;
1467 }
1468 let mut is_fallback = false;
1469 let mut is_9_5 = false;
1470 if quality == 10 {
1471 quality = 9;
1472 is_9_5 = true;
1473 }
1474 if !is_fallback {
1475 let mut s_orig = BrotliEncoderStateStruct::new(core::mem::replace(m8, empty_m8));
1476 if is_9_5 {
1477 let mut params = BrotliEncoderParams::default();
1478 params.q9_5 = true;
1479 params.quality = 10;
1480 ChooseHasher(&mut params);
1481 s_orig.hasher_ = BrotliMakeHasher(m8, ¶ms, None );
1482 }
1483 let mut result: bool;
1484 {
1485 let s = &mut s_orig;
1486 let mut available_in: usize = input_size;
1487 let next_in_array: &[u8] = input_buffer;
1488 let mut next_in_offset: usize = 0;
1489 let mut available_out: usize = *encoded_size;
1490 let next_out_array: &mut [u8] = output_start;
1491 let mut next_out_offset: usize = 0;
1492 let mut total_out = Some(0);
1493 s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, quality as u32);
1494 s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin as u32);
1495 s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_MODE, mode as u32);
1496 s.set_parameter(
1497 BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
1498 input_size as u32,
1499 );
1500 if lgwin > BROTLI_MAX_WINDOW_BITS as i32 {
1501 s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LARGE_WINDOW, 1);
1502 }
1503 result = s.compress_stream(
1504 BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
1505 &mut available_in,
1506 next_in_array,
1507 &mut next_in_offset,
1508 &mut available_out,
1509 next_out_array,
1510 &mut next_out_offset,
1511 &mut total_out,
1512 metablock_callback,
1513 );
1514 if !s.is_finished() {
1515 result = false;
1516 }
1517
1518 *encoded_size = total_out.unwrap();
1519 BrotliEncoderDestroyInstance(s);
1520 }
1521 let _ = core::mem::replace(m8, s_orig.m8);
1522 if !result || max_out_size != 0 && (*encoded_size > max_out_size) {
1523 is_fallback = true;
1524 } else {
1525 return true;
1526 }
1527 }
1528 assert_ne!(is_fallback, false);
1529 *encoded_size = 0;
1530 if max_out_size == 0 {
1531 return false;
1532 }
1533 if out_size >= max_out_size {
1534 *encoded_size = MakeUncompressedStream(input_start, input_size, output_start);
1535 return true;
1536 }
1537 false
1538}
1539
1540impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1541 fn inject_byte_padding_block(&mut self) {
1542 let mut seal: u32 = self.last_bytes_ as u32;
1543 let mut seal_bits: usize = self.last_bytes_bits_ as usize;
1544 let destination: &mut [u8];
1545 self.last_bytes_ = 0;
1546 self.last_bytes_bits_ = 0;
1547 seal |= 0x6u32 << seal_bits;
1548
1549 seal_bits = seal_bits.wrapping_add(6);
1550 if !IsNextOutNull(&self.next_out_) {
1551 destination = &mut GetNextOut!(*self)[self.available_out_..];
1552 } else {
1553 destination = &mut self.tiny_buf_[..];
1554 self.next_out_ = NextOut::TinyBuf(0);
1555 }
1556 destination[0] = seal as u8;
1557 if seal_bits > 8usize {
1558 destination[1] = (seal >> 8) as u8;
1559 }
1560 if seal_bits > 16usize {
1561 destination[2] = (seal >> 16) as u8;
1562 }
1563 self.available_out_ = self
1564 .available_out_
1565 .wrapping_add(seal_bits.wrapping_add(7) >> 3);
1566 }
1567
1568 fn inject_flush_or_push_output(
1569 &mut self,
1570 available_out: &mut usize,
1571 next_out_array: &mut [u8],
1572 next_out_offset: &mut usize,
1573 total_out: &mut Option<usize>,
1574 ) -> bool {
1575 if self.stream_state_ as i32
1576 == BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED as i32
1577 && (self.last_bytes_bits_ as i32 != 0i32)
1578 {
1579 self.inject_byte_padding_block();
1580 return true;
1581 }
1582 if self.available_out_ != 0usize && (*available_out != 0usize) {
1583 let copy_output_size: usize = min(self.available_out_, *available_out);
1584 (*next_out_array)[(*next_out_offset)..(*next_out_offset + copy_output_size)]
1585 .clone_from_slice(&GetNextOut!(self)[..copy_output_size]);
1586 *next_out_offset = next_out_offset.wrapping_add(copy_output_size);
1588 *available_out = available_out.wrapping_sub(copy_output_size);
1589 self.next_out_ = NextOutIncrement(&self.next_out_, (copy_output_size as i32));
1590 self.available_out_ = self.available_out_.wrapping_sub(copy_output_size);
1591 self.total_out_ = self.total_out_.wrapping_add(copy_output_size as u64);
1592 if let &mut Some(ref mut total_out_inner) = total_out {
1593 *total_out_inner = self.total_out_ as usize;
1594 }
1595 return true;
1596 }
1597 false
1598 }
1599
1600 fn unprocessed_input_size(&self) -> u64 {
1601 self.input_pos_.wrapping_sub(self.last_processed_pos_)
1602 }
1603
1604 fn update_size_hint(&mut self, available_in: usize) {
1605 if self.params.size_hint == 0usize {
1606 let delta: u64 = self.unprocessed_input_size();
1607 let tail: u64 = available_in as u64;
1608 let limit: u32 = 1u32 << 30;
1609 let total: u32;
1610 if delta >= u64::from(limit)
1611 || tail >= u64::from(limit)
1612 || delta.wrapping_add(tail) >= u64::from(limit)
1613 {
1614 total = limit;
1615 } else {
1616 total = delta.wrapping_add(tail) as u32;
1617 }
1618 self.params.size_hint = total as usize;
1619 }
1620 }
1621}
1622
1623fn WrapPosition(position: u64) -> u32 {
1624 let mut result: u32 = position as u32;
1625 let gb: u64 = position >> 30;
1626 if gb > 2 {
1627 result = result & (1u32 << 30).wrapping_sub(1)
1628 | ((gb.wrapping_sub(1) & 1) as u32).wrapping_add(1) << 30;
1629 }
1630 result
1631}
1632
1633impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1634 fn get_brotli_storage(&mut self, size: usize) {
1635 if self.storage_size_ < size {
1636 <Alloc as Allocator<u8>>::free_cell(&mut self.m8, core::mem::take(&mut self.storage_));
1637 self.storage_ = allocate::<u8, _>(&mut self.m8, size);
1638 self.storage_size_ = size;
1639 }
1640 }
1641}
1642
1643fn MaxHashTableSize(quality: i32) -> usize {
1644 (if quality == 0i32 {
1645 1i32 << 15
1646 } else {
1647 1i32 << 17
1648 }) as usize
1649}
1650
1651fn HashTableSize(max_table_size: usize, input_size: usize) -> usize {
1652 let mut htsize: usize = 256usize;
1653 while htsize < max_table_size && (htsize < input_size) {
1654 htsize <<= 1i32;
1655 }
1656 htsize
1657}
1658
1659macro_rules! GetHashTable {
1660 ($s : expr, $quality: expr, $input_size : expr, $table_size : expr) => {
1661 GetHashTableInternal(
1662 &mut $s.m8,
1663 &mut $s.small_table_,
1664 &mut $s.large_table_,
1665 $quality,
1666 $input_size,
1667 $table_size,
1668 )
1669 };
1670}
1671fn GetHashTableInternal<'a, AllocI32: alloc::Allocator<i32>>(
1672 mi32: &mut AllocI32,
1673 small_table_: &'a mut [i32; 1024],
1674 large_table_: &'a mut AllocI32::AllocatedMemory,
1675 quality: i32,
1676 input_size: usize,
1677 table_size: &mut usize,
1678) -> &'a mut [i32] {
1679 let max_table_size: usize = MaxHashTableSize(quality);
1680 let mut htsize: usize = HashTableSize(max_table_size, input_size);
1681 let table: &mut [i32];
1682 if quality == 0i32 && htsize & 0xaaaaausize == 0usize {
1683 htsize <<= 1i32;
1684 }
1685 if htsize <= small_table_.len() {
1686 table = &mut small_table_[..];
1687 } else {
1688 if htsize > large_table_.slice().len() {
1689 {
1691 mi32.free_cell(core::mem::take(large_table_));
1692 }
1693 *large_table_ = mi32.alloc_cell(htsize);
1694 }
1695 table = large_table_.slice_mut();
1696 }
1697 *table_size = htsize;
1698 for item in table[..htsize].iter_mut() {
1699 *item = 0;
1700 }
1701 table }
1703
1704impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1705 fn update_last_processed_pos(&mut self) -> bool {
1706 let wrapped_last_processed_pos: u32 = WrapPosition(self.last_processed_pos_);
1707 let wrapped_input_pos: u32 = WrapPosition(self.input_pos_);
1708 self.last_processed_pos_ = self.input_pos_;
1709 wrapped_input_pos < wrapped_last_processed_pos
1710 }
1711}
1712
1713fn MaxMetablockSize(params: &BrotliEncoderParams) -> usize {
1714 1 << min(ComputeRbBits(params), 24)
1715}
1716
1717fn ChooseContextMap(
1718 quality: i32,
1719 bigram_histo: &mut [u32],
1720 num_literal_contexts: &mut usize,
1721 literal_context_map: &mut &[u32],
1722) {
1723 static kStaticContextMapContinuation: [u32; 64] = [
1724 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1725 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1726 0, 0, 0, 0,
1727 ];
1728 static kStaticContextMapSimpleUTF8: [u32; 64] = [
1729 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1730 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1731 0, 0, 0, 0,
1732 ];
1733 let mut monogram_histo = [0u32; 3];
1734 let mut two_prefix_histo = [0u32; 6];
1735
1736 let mut i: usize;
1737 let mut entropy = [0.0 as floatX; 4];
1738 i = 0usize;
1739 while i < 9usize {
1740 {
1741 {
1742 let _rhs = bigram_histo[i];
1743 let _lhs = &mut monogram_histo[i.wrapping_rem(3)];
1744 *_lhs = (*_lhs).wrapping_add(_rhs);
1745 }
1746 {
1747 let _rhs = bigram_histo[i];
1748 let _lhs = &mut two_prefix_histo[i.wrapping_rem(6)];
1749 *_lhs = (*_lhs).wrapping_add(_rhs);
1750 }
1751 }
1752 i = i.wrapping_add(1);
1753 }
1754 entropy[1] = shannon_entropy(&monogram_histo[..], 3).0;
1755 entropy[2] =
1756 shannon_entropy(&two_prefix_histo[..], 3).0 + shannon_entropy(&two_prefix_histo[3..], 3).0;
1757 entropy[3] = 0.0;
1758 for i in 0usize..3usize {
1759 entropy[3] += shannon_entropy(&bigram_histo[(3usize).wrapping_mul(i)..], 3).0;
1760 }
1761 let total: usize = monogram_histo[0]
1762 .wrapping_add(monogram_histo[1])
1763 .wrapping_add(monogram_histo[2]) as usize;
1764 entropy[0] = 1.0 / (total as floatX);
1765 entropy[1] *= entropy[0];
1766 entropy[2] *= entropy[0];
1767 entropy[3] *= entropy[0];
1768 if quality < 7i32 {
1769 entropy[3] = entropy[1] * 10.0;
1770 }
1771 if entropy[1] - entropy[2] < 0.2 && entropy[1] - entropy[3] < 0.2 {
1772 *num_literal_contexts = 1;
1773 } else if entropy[2] - entropy[3] < 0.02 {
1774 *num_literal_contexts = 2usize;
1775 *literal_context_map = &kStaticContextMapSimpleUTF8[..];
1776 } else {
1777 *num_literal_contexts = 3usize;
1778 *literal_context_map = &kStaticContextMapContinuation[..];
1779 }
1780}
1781
1782static kStaticContextMapComplexUTF8: [u32; 64] = [
1783 11, 11, 12, 12, 0, 0, 0, 0, 1, 1, 9, 9, 2, 2, 2, 2, 1, 1, 1, 1, 8, 3, 3, 3, 1, 1, 1, 1, 2, 2, 2, 2, 8, 4, 4, 4, 8, 7, 4, 4, 8, 0, 0, 0, 3, 3, 3, 3, 5, 5, 10, 5, 5, 5, 10, 5, 6, 6, 6, 6, 6, 6, 6, 6,
1798];
1799fn ShouldUseComplexStaticContextMap(
1803 input: &[u8],
1804 mut start_pos: usize,
1805 length: usize,
1806 mask: usize,
1807 quality: i32,
1808 size_hint: usize,
1809 num_literal_contexts: &mut usize,
1810 literal_context_map: &mut &[u32],
1811) -> bool {
1812 let _ = quality;
1813 if (size_hint < (1 << 20)) {
1816 false
1817 } else {
1818 let end_pos = start_pos + length;
1819 let mut combined_histo: [u32; 32] = [0; 32];
1823 let mut context_histo: [[u32; 32]; 13] = [[0; 32]; 13];
1824 let mut total = 0u32;
1825 let mut entropy = [0.0 as floatX; 3];
1826 let utf8_lut = BROTLI_CONTEXT_LUT(ContextType::CONTEXT_UTF8);
1827 while start_pos + 64 <= end_pos {
1828 let stride_end_pos = start_pos + 64;
1829 let mut prev2 = input[start_pos & mask];
1830 let mut prev1 = input[(start_pos + 1) & mask];
1831
1832 for pos in start_pos + 2..stride_end_pos {
1835 let literal = input[pos & mask];
1836 let context = kStaticContextMapComplexUTF8
1837 [BROTLI_CONTEXT(prev1, prev2, utf8_lut) as usize]
1838 as u8;
1839 total += 1;
1840 combined_histo[(literal >> 3) as usize] += 1;
1841 context_histo[context as usize][(literal >> 3) as usize] += 1;
1842 prev2 = prev1;
1843 prev1 = literal;
1844 }
1845 start_pos += 4096;
1846 }
1847 entropy[1] = shannon_entropy(&combined_histo[..], 32).0;
1848 entropy[2] = 0.0;
1849 for i in 0..13 {
1850 assert!(i < 13);
1851 entropy[2] += shannon_entropy(&context_histo[i][..], 32).0;
1852 }
1853 entropy[0] = 1.0 / (total as floatX);
1854 entropy[1] *= entropy[0];
1855 entropy[2] *= entropy[0];
1856 if (entropy[2] > 3.0 || entropy[1] - entropy[2] < 0.2) {
1864 false
1865 } else {
1866 *num_literal_contexts = 13;
1867 *literal_context_map = &kStaticContextMapComplexUTF8;
1868 true
1869 }
1870 }
1871}
1872
1873fn DecideOverLiteralContextModeling(
1874 input: &[u8],
1875 mut start_pos: usize,
1876 length: usize,
1877 mask: usize,
1878 quality: i32,
1879 size_hint: usize,
1880 num_literal_contexts: &mut usize,
1881 literal_context_map: &mut &[u32],
1882) {
1883 if quality < 5i32 || length < 64usize {
1884 } else if ShouldUseComplexStaticContextMap(
1885 input,
1886 start_pos,
1887 length,
1888 mask,
1889 quality,
1890 size_hint,
1891 num_literal_contexts,
1892 literal_context_map,
1893 ) {
1894 } else {
1895 let end_pos: usize = start_pos.wrapping_add(length);
1896 let mut bigram_prefix_histo = [0u32; 9];
1897 while start_pos.wrapping_add(64) <= end_pos {
1898 {
1899 static lut: [i32; 4] = [0, 0, 1, 2];
1900 let stride_end_pos: usize = start_pos.wrapping_add(64);
1901 let mut prev: i32 = lut[(input[(start_pos & mask)] as i32 >> 6) as usize] * 3i32;
1902 let mut pos: usize;
1903 pos = start_pos.wrapping_add(1);
1904 while pos < stride_end_pos {
1905 {
1906 let literal: u8 = input[(pos & mask)];
1907 {
1908 let _rhs = 1;
1909 let cur_ind = (prev + lut[(literal as i32 >> 6) as usize]);
1910 let _lhs = &mut bigram_prefix_histo[cur_ind as usize];
1911 *_lhs = (*_lhs).wrapping_add(_rhs as u32);
1912 }
1913 prev = lut[(literal as i32 >> 6) as usize] * 3i32;
1914 }
1915 pos = pos.wrapping_add(1);
1916 }
1917 }
1918 start_pos = start_pos.wrapping_add(4096);
1919 }
1920 ChooseContextMap(
1921 quality,
1922 &mut bigram_prefix_histo[..],
1923 num_literal_contexts,
1924 literal_context_map,
1925 );
1926 }
1927}
1928fn WriteEmptyLastBlocksInternal(
1929 params: &BrotliEncoderParams,
1930 storage_ix: &mut usize,
1931 storage: &mut [u8],
1932) {
1933 if params.byte_align {
1935 BrotliWritePaddingMetaBlock(storage_ix, storage);
1936 }
1937 if !params.bare_stream {
1938 BrotliWriteEmptyLastMetaBlock(storage_ix, storage)
1939 }
1940}
1941fn WriteMetaBlockInternal<Alloc: BrotliAlloc, Cb>(
1942 alloc: &mut Alloc,
1943 data: &[u8],
1944 mask: usize,
1945 last_flush_pos: u64,
1946 bytes: usize,
1947 mut is_last: bool,
1948 literal_context_mode: ContextType,
1949 params: &BrotliEncoderParams,
1950 lit_scratch_space: &mut <HistogramLiteral as CostAccessors>::i32vec,
1951 cmd_scratch_space: &mut <HistogramCommand as CostAccessors>::i32vec,
1952 dst_scratch_space: &mut <HistogramDistance as CostAccessors>::i32vec,
1953 prev_byte: u8,
1954 prev_byte2: u8,
1955 num_literals: usize,
1956 num_commands: usize,
1957 commands: &mut [Command],
1958 saved_dist_cache: &[i32; kNumDistanceCacheEntries],
1959 dist_cache: &mut [i32; 16],
1960 recoder_state: &mut RecoderState,
1961 storage_ix: &mut usize,
1962 storage: &mut [u8],
1963 cb: &mut Cb,
1964) where
1965 Cb: FnMut(
1966 &mut interface::PredictionModeContextMap<InputReferenceMut>,
1967 &mut [interface::StaticCommand],
1968 interface::InputPair,
1969 &mut Alloc,
1970 ),
1971{
1972 let actual_is_last = is_last;
1973 if params.appendable || params.byte_align {
1974 is_last = false;
1975 } else {
1976 assert!(!params.catable); }
1978 let wrapped_last_flush_pos: u32 = WrapPosition(last_flush_pos);
1979
1980 let literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
1981 let mut block_params = params.clone();
1982 if bytes == 0usize {
1983 WriteEmptyLastBlocksInternal(params, storage_ix, storage);
1984 return;
1985 }
1986 if !should_compress(
1987 data,
1988 mask,
1989 last_flush_pos,
1990 bytes,
1991 num_literals,
1992 num_commands,
1993 ) {
1994 dist_cache[..4].clone_from_slice(&saved_dist_cache[..4]);
1995 store_uncompressed_meta_block(
1996 alloc,
1997 is_last,
1998 data,
1999 wrapped_last_flush_pos as usize,
2000 mask,
2001 params,
2002 bytes,
2003 recoder_state,
2004 storage_ix,
2005 storage,
2006 false,
2007 cb,
2008 );
2009 if actual_is_last != is_last {
2010 WriteEmptyLastBlocksInternal(params, storage_ix, storage);
2011 }
2012 return;
2013 }
2014 let saved_byte_location = (*storage_ix) >> 3;
2015 let last_bytes: u16 =
2016 ((storage[saved_byte_location + 1] as u16) << 8) | storage[saved_byte_location] as u16;
2017 let last_bytes_bits: u8 = *storage_ix as u8;
2018 if params.quality <= 2 {
2027 store_meta_block_fast(
2028 alloc,
2029 data,
2030 wrapped_last_flush_pos as usize,
2031 bytes,
2032 mask,
2033 is_last,
2034 params,
2035 saved_dist_cache,
2036 commands,
2037 num_commands,
2038 recoder_state,
2039 storage_ix,
2040 storage,
2041 cb,
2042 );
2043 } else if params.quality < 4 {
2044 store_meta_block_trivial(
2045 alloc,
2046 data,
2047 wrapped_last_flush_pos as usize,
2048 bytes,
2049 mask,
2050 is_last,
2051 params,
2052 saved_dist_cache,
2053 commands,
2054 num_commands,
2055 recoder_state,
2056 storage_ix,
2057 storage,
2058 cb,
2059 );
2060 } else {
2061 let mut mb = MetaBlockSplit::<Alloc>::new();
2064 if params.quality < 10i32 {
2065 let mut num_literal_contexts: usize = 1;
2066 let mut literal_context_map: &[u32] = &[];
2067 if params.disable_literal_context_modeling == 0 {
2068 DecideOverLiteralContextModeling(
2069 data,
2070 wrapped_last_flush_pos as usize,
2071 bytes,
2072 mask,
2073 params.quality,
2074 params.size_hint,
2075 &mut num_literal_contexts,
2076 &mut literal_context_map,
2077 );
2078 }
2079 BrotliBuildMetaBlockGreedy(
2080 alloc,
2081 data,
2082 wrapped_last_flush_pos as usize,
2083 mask,
2084 prev_byte,
2085 prev_byte2,
2086 literal_context_mode,
2087 literal_context_lut,
2088 num_literal_contexts,
2089 literal_context_map,
2090 commands,
2091 num_commands,
2092 &mut mb,
2093 );
2094 } else {
2095 BrotliBuildMetaBlock(
2096 alloc,
2097 data,
2098 wrapped_last_flush_pos as usize,
2099 mask,
2100 &mut block_params,
2101 prev_byte,
2102 prev_byte2,
2103 commands,
2104 num_commands,
2105 literal_context_mode,
2106 lit_scratch_space,
2107 cmd_scratch_space,
2108 dst_scratch_space,
2109 &mut mb,
2110 );
2111 }
2112 if params.quality >= 4i32 {
2113 let mut num_effective_dist_codes = block_params.dist.alphabet_size;
2114 if num_effective_dist_codes > BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS as u32 {
2115 num_effective_dist_codes = BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS as u32;
2116 }
2117 BrotliOptimizeHistograms(num_effective_dist_codes as usize, &mut mb);
2118 }
2119 store_meta_block(
2120 alloc,
2121 data,
2122 wrapped_last_flush_pos as usize,
2123 bytes,
2124 mask,
2125 prev_byte,
2126 prev_byte2,
2127 is_last,
2128 &block_params,
2129 literal_context_mode,
2130 saved_dist_cache,
2131 commands,
2132 num_commands,
2133 &mut mb,
2134 recoder_state,
2135 storage_ix,
2136 storage,
2137 cb,
2138 );
2139 mb.destroy(alloc);
2140 }
2141 if bytes + 4 + saved_byte_location < (*storage_ix >> 3) {
2142 dist_cache[..4].clone_from_slice(&saved_dist_cache[..4]);
2143 storage[saved_byte_location] = last_bytes as u8;
2147 storage[saved_byte_location + 1] = (last_bytes >> 8) as u8;
2148 *storage_ix = last_bytes_bits as usize;
2149 store_uncompressed_meta_block(
2150 alloc,
2151 is_last,
2152 data,
2153 wrapped_last_flush_pos as usize,
2154 mask,
2155 params,
2156 bytes,
2157 recoder_state,
2158 storage_ix,
2159 storage,
2160 true,
2161 cb,
2162 );
2163 }
2164 if actual_is_last != is_last {
2165 WriteEmptyLastBlocksInternal(params, storage_ix, storage);
2166 }
2167}
2168
2169fn ChooseDistanceParams(params: &mut BrotliEncoderParams) {
2170 let mut num_direct_distance_codes = 0u32;
2171 let mut distance_postfix_bits = 0u32;
2172
2173 if params.quality >= 4 {
2174 if params.mode == BrotliEncoderMode::BROTLI_MODE_FONT {
2175 distance_postfix_bits = 1;
2176 num_direct_distance_codes = 12;
2177 } else {
2178 distance_postfix_bits = params.dist.distance_postfix_bits;
2179 num_direct_distance_codes = params.dist.num_direct_distance_codes;
2180 }
2181 let ndirect_msb = (num_direct_distance_codes >> distance_postfix_bits) & 0x0f;
2182 if distance_postfix_bits > BROTLI_MAX_NPOSTFIX as u32
2183 || num_direct_distance_codes > BROTLI_MAX_NDIRECT as u32
2184 || (ndirect_msb << distance_postfix_bits) != num_direct_distance_codes
2185 {
2186 distance_postfix_bits = 0;
2187 num_direct_distance_codes = 0;
2188 }
2189 }
2190 BrotliInitDistanceParams(params, distance_postfix_bits, num_direct_distance_codes);
2191 }
2212
2213impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2214 fn encode_data<MetablockCallback>(
2215 &mut self,
2216 is_last: bool,
2217 force_flush: bool,
2218 out_size: &mut usize,
2219 callback: &mut MetablockCallback,
2220 ) -> bool
2222 where
2223 MetablockCallback: FnMut(
2224 &mut interface::PredictionModeContextMap<InputReferenceMut>,
2225 &mut [interface::StaticCommand],
2226 interface::InputPair,
2227 &mut Alloc,
2228 ),
2229 {
2230 let mut delta: u64 = self.unprocessed_input_size();
2231 let mut bytes: u32 = delta as u32;
2232 let mask = self.ringbuffer_.mask_;
2233 if !self.ensure_initialized() {
2234 return false;
2235 }
2236 let dictionary = BrotliGetDictionary();
2237 if self.is_last_block_emitted_ {
2238 return false;
2239 }
2240 if is_last {
2241 self.is_last_block_emitted_ = true;
2242 }
2243 if delta > self.input_block_size() as u64 {
2244 return false;
2245 }
2246 let mut storage_ix: usize = usize::from(self.last_bytes_bits_);
2247 {
2248 let meta_size = max(
2249 bytes as usize,
2250 self.input_pos_.wrapping_sub(self.last_flush_pos_) as usize,
2251 );
2252 self.get_brotli_storage((2usize).wrapping_mul(meta_size).wrapping_add(503 + 24));
2253 }
2254 {
2255 self.storage_.slice_mut()[0] = self.last_bytes_ as u8;
2256 self.storage_.slice_mut()[1] = (self.last_bytes_ >> 8) as u8;
2257 }
2258 let mut catable_header_size = 0;
2259 if let IsFirst::NothingWritten = self.is_first_mb {
2260 if self.params.magic_number {
2261 BrotliWriteMetadataMetaBlock(
2262 &self.params,
2263 &mut storage_ix,
2264 self.storage_.slice_mut(),
2265 );
2266 self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2267 | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2268 self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2269 self.next_out_ = NextOut::DynamicStorage(0);
2270 catable_header_size = storage_ix >> 3;
2271 *out_size = catable_header_size;
2272 self.is_first_mb = IsFirst::HeaderWritten;
2273 }
2274 if bytes == 0
2276 && self.params.byte_align
2277 && self.params.appendable
2278 && !self.params.catable
2279 {
2280 BrotliWritePaddingMetaBlock(&mut storage_ix, self.storage_.slice_mut());
2281 }
2282 }
2283 if let IsFirst::BothCatableBytesWritten = self.is_first_mb {
2284 } else if !self.params.catable {
2286 self.is_first_mb = IsFirst::BothCatableBytesWritten;
2287 } else if bytes != 0 {
2288 assert!(self.last_processed_pos_ < 2 || self.custom_dictionary);
2289 let num_bytes_to_write_uncompressed: usize = min(2, bytes as usize);
2290 {
2291 let data =
2292 &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..];
2293 store_uncompressed_meta_block(
2294 &mut self.m8,
2295 false,
2296 data,
2297 self.last_flush_pos_ as usize,
2298 mask as usize,
2299 &self.params,
2300 num_bytes_to_write_uncompressed,
2301 &mut self.recoder_state,
2302 &mut storage_ix,
2303 self.storage_.slice_mut(),
2304 false, callback,
2306 );
2307 self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2308 | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2309 self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2310 self.prev_byte2_ = self.prev_byte_;
2311 self.prev_byte_ = data[self.last_flush_pos_ as usize & mask as usize];
2312 if num_bytes_to_write_uncompressed == 2 {
2313 self.prev_byte2_ = self.prev_byte_;
2314 self.prev_byte_ = data[(self.last_flush_pos_ + 1) as usize & mask as usize];
2315 }
2316 }
2317 self.last_flush_pos_ += num_bytes_to_write_uncompressed as u64;
2318 bytes -= num_bytes_to_write_uncompressed as u32;
2319 self.last_processed_pos_ += num_bytes_to_write_uncompressed as u64;
2320 if num_bytes_to_write_uncompressed >= 2 {
2321 self.is_first_mb = IsFirst::BothCatableBytesWritten;
2322 } else if num_bytes_to_write_uncompressed == 1 {
2323 if let IsFirst::FirstCatableByteWritten = self.is_first_mb {
2324 self.is_first_mb = IsFirst::BothCatableBytesWritten;
2325 } else {
2326 self.is_first_mb = IsFirst::FirstCatableByteWritten;
2327 }
2328 }
2329 catable_header_size = storage_ix >> 3;
2330 self.next_out_ = NextOut::DynamicStorage(0);
2331 *out_size = catable_header_size;
2332 delta = self.unprocessed_input_size();
2333 }
2334 let mut wrapped_last_processed_pos: u32 = WrapPosition(self.last_processed_pos_);
2335 if self.params.quality == 1i32 && self.command_buf_.slice().is_empty() {
2336 let new_buf = allocate::<u32, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2337 self.command_buf_ = new_buf;
2338 let new_buf8 = allocate::<u8, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2339 self.literal_buf_ = new_buf8;
2340 }
2341
2342 if self.params.quality == 0i32 || self.params.quality == 1i32 {
2343 let mut table_size: usize = 0;
2344 {
2345 if delta == 0 && !is_last {
2346 *out_size = catable_header_size;
2347 return true;
2348 }
2349 let data =
2350 &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..];
2351
2352 let table: &mut [i32] =
2356 GetHashTable!(self, self.params.quality, bytes as usize, &mut table_size);
2357
2358 if self.params.quality == 0i32 {
2359 compress_fragment_fast(
2360 &mut self.m8,
2361 &mut data[((wrapped_last_processed_pos & mask) as usize)..],
2362 bytes as usize,
2363 is_last,
2364 table,
2365 table_size,
2366 &mut self.cmd_depths_[..],
2367 &mut self.cmd_bits_[..],
2368 &mut self.cmd_code_numbits_,
2369 &mut self.cmd_code_[..],
2370 &mut storage_ix,
2371 self.storage_.slice_mut(),
2372 );
2373 } else {
2374 compress_fragment_two_pass(
2375 &mut self.m8,
2376 &mut data[((wrapped_last_processed_pos & mask) as usize)..],
2377 bytes as usize,
2378 is_last,
2379 self.command_buf_.slice_mut(),
2380 self.literal_buf_.slice_mut(),
2381 table,
2382 table_size,
2383 &mut storage_ix,
2384 self.storage_.slice_mut(),
2385 );
2386 }
2387 self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2388 | ((self.storage_.slice()[(storage_ix >> 3) + 1] as u16) << 8);
2389 self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2390 }
2391 self.update_last_processed_pos();
2392 self.next_out_ = NextOut::DynamicStorage(0); *out_size = storage_ix >> 3;
2395 return true;
2396 }
2397 {
2398 let mut newsize: usize = self
2399 .num_commands_
2400 .wrapping_add(bytes.wrapping_div(2) as usize)
2401 .wrapping_add(1);
2402 if newsize > self.cmd_alloc_size_ {
2403 newsize = newsize.wrapping_add(bytes.wrapping_div(4).wrapping_add(16) as usize);
2404 self.cmd_alloc_size_ = newsize;
2405 let mut new_commands = allocate::<Command, _>(&mut self.m8, newsize);
2406 if !self.commands_.slice().is_empty() {
2407 new_commands.slice_mut()[..self.num_commands_]
2408 .clone_from_slice(&self.commands_.slice()[..self.num_commands_]);
2409 <Alloc as Allocator<Command>>::free_cell(
2410 &mut self.m8,
2411 core::mem::take(&mut self.commands_),
2412 );
2413 }
2414 self.commands_ = new_commands;
2415 }
2416 }
2417 InitOrStitchToPreviousBlock(
2418 &mut self.m8,
2419 &mut self.hasher_,
2420 &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2421 mask as usize,
2422 self.custom_dictionary_size,
2423 &mut self.params,
2424 wrapped_last_processed_pos as usize,
2425 bytes as usize,
2426 is_last,
2427 );
2428 let literal_context_mode = ChooseContextMode(
2429 &self.params,
2430 self.ringbuffer_.data_mo.slice(),
2431 WrapPosition(self.last_flush_pos_) as usize,
2432 mask as usize,
2433 (self.input_pos_.wrapping_sub(self.last_flush_pos_)) as usize,
2434 );
2435 if self.num_commands_ != 0 && self.last_insert_len_ == 0 {
2436 self.extend_last_command(&mut bytes, &mut wrapped_last_processed_pos);
2437 }
2438 BrotliCreateBackwardReferences(
2439 &mut self.m8,
2440 dictionary,
2441 bytes as usize,
2442 wrapped_last_processed_pos as usize,
2443 &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2444 mask as usize,
2445 self.custom_dictionary_size,
2446 &mut self.params,
2447 &mut self.hasher_,
2448 &mut self.dist_cache_,
2449 &mut self.last_insert_len_,
2450 &mut self.commands_.slice_mut()[self.num_commands_..],
2451 &mut self.num_commands_,
2452 &mut self.num_literals_,
2453 );
2454 {
2455 let max_length: usize = MaxMetablockSize(&mut self.params);
2456 let max_literals: usize = max_length.wrapping_div(8);
2457 let max_commands: usize = max_length.wrapping_div(8);
2458 let processed_bytes: usize =
2459 self.input_pos_.wrapping_sub(self.last_flush_pos_) as usize;
2460 let next_input_fits_metablock =
2461 processed_bytes.wrapping_add(self.input_block_size()) <= max_length;
2462 let should_flush = self.params.quality < 4
2463 && self.num_literals_.wrapping_add(self.num_commands_) >= 0x2fff;
2464 if !is_last
2465 && !force_flush
2466 && !should_flush
2467 && next_input_fits_metablock
2468 && self.num_literals_ < max_literals
2469 && self.num_commands_ < max_commands
2470 {
2471 if self.update_last_processed_pos() {
2472 HasherReset(&mut self.hasher_);
2473 }
2474 *out_size = catable_header_size;
2475 return true;
2476 }
2477 }
2478 if self.last_insert_len_ > 0usize {
2479 self.commands_.slice_mut()[self.num_commands_].init_insert(self.last_insert_len_);
2480 self.num_commands_ = self.num_commands_.wrapping_add(1);
2481 self.num_literals_ = self.num_literals_.wrapping_add(self.last_insert_len_);
2482 self.last_insert_len_ = 0usize;
2483 }
2484 if !is_last && self.input_pos_ == self.last_flush_pos_ {
2485 *out_size = catable_header_size;
2486 return true;
2487 }
2488 {
2489 let metablock_size: u32 = self.input_pos_.wrapping_sub(self.last_flush_pos_) as u32;
2490 WriteMetaBlockInternal(
2495 &mut self.m8,
2496 &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2497 mask as usize,
2498 self.last_flush_pos_,
2499 metablock_size as usize,
2500 is_last,
2501 literal_context_mode,
2502 &mut self.params,
2503 &mut self.literal_scratch_space,
2504 &mut self.command_scratch_space,
2505 &mut self.distance_scratch_space,
2506 self.prev_byte_,
2507 self.prev_byte2_,
2508 self.num_literals_,
2509 self.num_commands_,
2510 self.commands_.slice_mut(),
2511 &mut self.saved_dist_cache_,
2512 &mut self.dist_cache_,
2513 &mut self.recoder_state,
2514 &mut storage_ix,
2515 self.storage_.slice_mut(),
2516 callback,
2517 );
2518
2519 self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2520 | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2521 self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2522 self.last_flush_pos_ = self.input_pos_;
2523 if self.update_last_processed_pos() {
2524 HasherReset(&mut self.hasher_);
2525 }
2526 let data = &self.ringbuffer_.data_mo.slice()[self.ringbuffer_.buffer_index..];
2527 if self.last_flush_pos_ > 0 {
2528 self.prev_byte_ =
2529 data[(((self.last_flush_pos_ as u32).wrapping_sub(1) & mask) as usize)];
2530 }
2531 if self.last_flush_pos_ > 1 {
2532 self.prev_byte2_ =
2533 data[((self.last_flush_pos_.wrapping_sub(2) as u32 & mask) as usize)];
2534 }
2535 self.num_commands_ = 0usize;
2536 self.num_literals_ = 0usize;
2537 self.saved_dist_cache_
2538 .clone_from_slice(self.dist_cache_.split_at(4).0);
2539 self.next_out_ = NextOut::DynamicStorage(0); *out_size = storage_ix >> 3;
2541 true
2542 }
2543 }
2544
2545 fn write_metadata_header(&mut self) -> usize {
2546 let block_size = self.remaining_metadata_bytes_ as usize;
2547 let header = GetNextOut!(*self);
2548 let mut storage_ix: usize;
2549 storage_ix = self.last_bytes_bits_ as usize;
2550 header[0] = self.last_bytes_ as u8;
2551 header[1] = (self.last_bytes_ >> 8) as u8;
2552 self.last_bytes_ = 0;
2553 self.last_bytes_bits_ = 0;
2554 BrotliWriteBits(1, 0, &mut storage_ix, header);
2555 BrotliWriteBits(2usize, 3, &mut storage_ix, header);
2556 BrotliWriteBits(1, 0, &mut storage_ix, header);
2557 if block_size == 0usize {
2558 BrotliWriteBits(2usize, 0, &mut storage_ix, header);
2559 } else {
2560 let nbits: u32 = if block_size == 1 {
2561 0u32
2562 } else {
2563 Log2FloorNonZero((block_size as u32).wrapping_sub(1) as (u64)).wrapping_add(1)
2564 };
2565 let nbytes: u32 = nbits.wrapping_add(7).wrapping_div(8);
2566 BrotliWriteBits(2usize, nbytes as (u64), &mut storage_ix, header);
2567 BrotliWriteBits(
2568 (8u32).wrapping_mul(nbytes) as usize,
2569 block_size.wrapping_sub(1) as u64,
2570 &mut storage_ix,
2571 header,
2572 );
2573 }
2574 storage_ix.wrapping_add(7u32 as usize) >> 3
2575 }
2576}
2577
2578impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2579 fn process_metadata<
2580 MetaBlockCallback: FnMut(
2581 &mut interface::PredictionModeContextMap<InputReferenceMut>,
2582 &mut [interface::StaticCommand],
2583 interface::InputPair,
2584 &mut Alloc,
2585 ),
2586 >(
2587 &mut self,
2588 available_in: &mut usize,
2589 next_in_array: &[u8],
2590 next_in_offset: &mut usize,
2591 available_out: &mut usize,
2592 next_out_array: &mut [u8],
2593 next_out_offset: &mut usize,
2594 total_out: &mut Option<usize>,
2595 metablock_callback: &mut MetaBlockCallback,
2596 ) -> bool {
2597 if *available_in > (1u32 << 24) as usize {
2598 return false;
2599 }
2600 if self.stream_state_ as i32 == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32 {
2601 self.remaining_metadata_bytes_ = *available_in as u32;
2602 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD;
2603 }
2604 if self.stream_state_ as i32 != BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2605 && (self.stream_state_ as i32
2606 != BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY as i32)
2607 {
2608 return false;
2609 }
2610 loop {
2611 if self.inject_flush_or_push_output(
2612 available_out,
2613 next_out_array,
2614 next_out_offset,
2615 total_out,
2616 ) {
2617 continue;
2618 }
2619 if self.available_out_ != 0usize {
2620 break;
2621 }
2622 if self.input_pos_ != self.last_flush_pos_ {
2623 let mut avail_out: usize = self.available_out_;
2624 let result = self.encode_data(false, true, &mut avail_out, metablock_callback);
2625 self.available_out_ = avail_out;
2626 if !result {
2627 return false;
2628 }
2629 continue;
2630 }
2631 if self.stream_state_ as i32
2632 == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2633 {
2634 self.next_out_ = NextOut::TinyBuf(0);
2635 self.available_out_ = self.write_metadata_header();
2636 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY;
2637 {
2638 continue;
2639 }
2640 } else {
2641 if self.remaining_metadata_bytes_ == 0u32 {
2642 self.remaining_metadata_bytes_ = u32::MAX;
2643 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING;
2644 {
2645 break;
2646 }
2647 }
2648 if *available_out != 0 {
2649 let copy: u32 =
2650 min(self.remaining_metadata_bytes_ as usize, *available_out) as u32;
2651 next_out_array[*next_out_offset..(*next_out_offset + copy as usize)]
2652 .clone_from_slice(
2653 &next_in_array[*next_in_offset..(*next_in_offset + copy as usize)],
2654 );
2655 *next_in_offset += copy as usize;
2658 *available_in = available_in.wrapping_sub(copy as usize);
2659 self.remaining_metadata_bytes_ =
2660 self.remaining_metadata_bytes_.wrapping_sub(copy);
2661 *next_out_offset += copy as usize;
2662 *available_out = available_out.wrapping_sub(copy as usize);
2664 } else {
2665 let copy: u32 = min(self.remaining_metadata_bytes_, 16u32);
2666 self.next_out_ = NextOut::TinyBuf(0);
2667 GetNextOut!(self)[..(copy as usize)].clone_from_slice(
2668 &next_in_array[*next_in_offset..(*next_in_offset + copy as usize)],
2669 );
2670 *next_in_offset += copy as usize;
2673 *available_in = available_in.wrapping_sub(copy as usize);
2674 self.remaining_metadata_bytes_ =
2675 self.remaining_metadata_bytes_.wrapping_sub(copy);
2676 self.available_out_ = copy as usize;
2677 }
2678 {
2679 continue;
2680 }
2681 }
2682 }
2683 true
2684 }
2685}
2686fn CheckFlushCompleteInner(
2687 stream_state: &mut BrotliEncoderStreamState,
2688 available_out: usize,
2689 next_out: &mut NextOut,
2690) {
2691 if *stream_state == BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED
2692 && (available_out == 0)
2693 {
2694 *stream_state = BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING;
2695 *next_out = NextOut::None;
2696 }
2697}
2698
2699impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2700 fn check_flush_complete(&mut self) {
2701 CheckFlushCompleteInner(
2702 &mut self.stream_state_,
2703 self.available_out_,
2704 &mut self.next_out_,
2705 );
2706 }
2707
2708 fn compress_stream_fast(
2709 &mut self,
2710 op: BrotliEncoderOperation,
2711 available_in: &mut usize,
2712 next_in_array: &[u8],
2713 next_in_offset: &mut usize,
2714 available_out: &mut usize,
2715 next_out_array: &mut [u8],
2716 next_out_offset: &mut usize,
2717 total_out: &mut Option<usize>,
2718 ) -> bool {
2719 let block_size_limit: usize = 1 << self.params.lgwin;
2720 let buf_size: usize = min(
2721 kCompressFragmentTwoPassBlockSize,
2722 min(*available_in, block_size_limit),
2723 );
2724 let mut command_buf = alloc_default::<u32, Alloc>();
2725 let mut literal_buf = alloc_default::<u8, Alloc>();
2726 if self.params.quality != 0i32 && (self.params.quality != 1i32) {
2727 return false;
2728 }
2729 if self.params.quality == 1i32 {
2730 if self.command_buf_.slice().is_empty()
2731 && (buf_size == kCompressFragmentTwoPassBlockSize)
2732 {
2733 self.command_buf_ =
2734 allocate::<u32, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2735 self.literal_buf_ =
2736 allocate::<u8, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2737 }
2738 if !self.command_buf_.slice().is_empty() {
2739 command_buf = core::mem::take(&mut self.command_buf_);
2740 literal_buf = core::mem::take(&mut self.literal_buf_);
2741 } else {
2742 command_buf = allocate::<u32, _>(&mut self.m8, buf_size);
2743 literal_buf = allocate::<u8, _>(&mut self.m8, buf_size);
2744 }
2745 }
2746 loop {
2747 if self.inject_flush_or_push_output(
2748 available_out,
2749 next_out_array,
2750 next_out_offset,
2751 total_out,
2752 ) {
2753 continue;
2754 }
2755 if self.available_out_ == 0usize
2756 && (self.stream_state_ as i32
2757 == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32)
2758 && (*available_in != 0usize
2759 || op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_PROCESS as i32)
2760 {
2761 let block_size: usize = min(block_size_limit, *available_in);
2762 let is_last = *available_in == block_size
2763 && op == BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
2764 let force_flush = *available_in == block_size
2765 && op == BrotliEncoderOperation::BROTLI_OPERATION_FLUSH;
2766 let max_out_size: usize = (2usize).wrapping_mul(block_size).wrapping_add(503);
2767 let mut inplace: i32 = 1i32;
2768 let storage: &mut [u8];
2769 let mut storage_ix: usize = self.last_bytes_bits_ as usize;
2770 let mut table_size: usize = 0;
2771
2772 if force_flush && block_size == 0 {
2773 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2774 {
2775 continue;
2776 }
2777 }
2778 if max_out_size <= *available_out {
2779 storage = &mut next_out_array[*next_out_offset..]; } else {
2781 inplace = 0i32;
2782 self.get_brotli_storage(max_out_size);
2783 storage = self.storage_.slice_mut();
2784 }
2785 storage[0] = self.last_bytes_ as u8;
2786 storage[1] = (self.last_bytes_ >> 8) as u8;
2787 let table: &mut [i32] =
2788 GetHashTable!(self, self.params.quality, block_size, &mut table_size);
2789 if self.params.quality == 0i32 {
2790 compress_fragment_fast(
2791 &mut self.m8,
2792 &(next_in_array)[*next_in_offset..],
2793 block_size,
2794 is_last,
2795 table,
2796 table_size,
2797 &mut self.cmd_depths_[..],
2798 &mut self.cmd_bits_[..],
2799 &mut self.cmd_code_numbits_,
2800 &mut self.cmd_code_[..],
2801 &mut storage_ix,
2802 storage,
2803 );
2804 } else {
2805 compress_fragment_two_pass(
2806 &mut self.m8,
2807 &(next_in_array)[*next_in_offset..],
2808 block_size,
2809 is_last,
2810 command_buf.slice_mut(),
2811 literal_buf.slice_mut(),
2812 table,
2813 table_size,
2814 &mut storage_ix,
2815 storage,
2816 );
2817 }
2818 *next_in_offset += block_size;
2819 *available_in = available_in.wrapping_sub(block_size);
2820 if inplace != 0 {
2821 let out_bytes: usize = storage_ix >> 3;
2822 *next_out_offset += out_bytes;
2823 *available_out = available_out.wrapping_sub(out_bytes);
2824 self.total_out_ = self.total_out_.wrapping_add(out_bytes as u64);
2825 if let &mut Some(ref mut total_out_inner) = total_out {
2826 *total_out_inner = self.total_out_ as usize;
2827 }
2828 } else {
2829 let out_bytes: usize = storage_ix >> 3;
2830 self.next_out_ = NextOut::DynamicStorage(0);
2831 self.available_out_ = out_bytes;
2832 }
2833 self.last_bytes_ = storage[(storage_ix >> 3)] as u16
2834 | ((storage[1 + (storage_ix >> 3)] as u16) << 8);
2835 self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2836 if force_flush {
2837 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2838 }
2839 if is_last {
2840 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FINISHED;
2841 }
2842 {
2843 continue;
2844 }
2845 }
2846 {
2847 break;
2848 }
2849 }
2850 if command_buf.slice().len() == kCompressFragmentTwoPassBlockSize
2851 && self.command_buf_.slice().is_empty()
2852 {
2853 self.command_buf_ = core::mem::take(&mut command_buf);
2855 self.literal_buf_ = core::mem::take(&mut literal_buf);
2856 } else {
2857 <Alloc as Allocator<u32>>::free_cell(&mut self.m8, command_buf);
2858 <Alloc as Allocator<u8>>::free_cell(&mut self.m8, literal_buf);
2859 }
2860 self.check_flush_complete();
2861 true
2862 }
2863
2864 fn remaining_input_block_size(&mut self) -> usize {
2865 let delta: u64 = self.unprocessed_input_size();
2866 let block_size = self.input_block_size();
2867 if delta >= block_size as u64 {
2868 return 0usize;
2869 }
2870 (block_size as u64).wrapping_sub(delta) as usize
2871 }
2872
2873 pub fn compress_stream<
2874 MetablockCallback: FnMut(
2875 &mut interface::PredictionModeContextMap<InputReferenceMut>,
2876 &mut [interface::StaticCommand],
2877 interface::InputPair,
2878 &mut Alloc,
2879 ),
2880 >(
2881 &mut self,
2882 op: BrotliEncoderOperation,
2883 available_in: &mut usize,
2884 next_in_array: &[u8],
2885 next_in_offset: &mut usize,
2886 available_out: &mut usize,
2887 next_out_array: &mut [u8],
2888 next_out_offset: &mut usize,
2889 total_out: &mut Option<usize>,
2890 metablock_callback: &mut MetablockCallback,
2891 ) -> bool {
2892 if !self.ensure_initialized() {
2893 return false;
2894 }
2895 if self.remaining_metadata_bytes_ != u32::MAX {
2896 if *available_in != self.remaining_metadata_bytes_ as usize {
2897 return false;
2898 }
2899 if op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA as i32 {
2900 return false;
2901 }
2902 }
2903 if op as i32 == BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA as i32 {
2904 self.update_size_hint(0);
2905 return self.process_metadata(
2906 available_in,
2907 next_in_array,
2908 next_in_offset,
2909 available_out,
2910 next_out_array,
2911 next_out_offset,
2912 total_out,
2913 metablock_callback,
2914 );
2915 }
2916 if self.stream_state_ as i32 == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2917 || self.stream_state_ as i32
2918 == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY as i32
2919 {
2920 return false;
2921 }
2922 if self.stream_state_ as i32 != BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32
2923 && (*available_in != 0usize)
2924 {
2925 return false;
2926 }
2927 if (self.params.quality == 0i32 || self.params.quality == 1i32) && !self.params.catable {
2928 return self.compress_stream_fast(
2930 op,
2931 available_in,
2932 next_in_array,
2933 next_in_offset,
2934 available_out,
2935 next_out_array,
2936 next_out_offset,
2937 total_out,
2938 );
2939 }
2940 loop {
2941 let remaining_block_size: usize = self.remaining_input_block_size();
2942 if remaining_block_size != 0usize && (*available_in != 0usize) {
2943 let copy_input_size: usize = min(remaining_block_size, *available_in);
2944 self.copy_input_to_ring_buffer(copy_input_size, &next_in_array[*next_in_offset..]);
2945 *next_in_offset += copy_input_size;
2946 *available_in = available_in.wrapping_sub(copy_input_size);
2947 {
2948 continue;
2949 }
2950 }
2951 if self.inject_flush_or_push_output(
2952 available_out,
2953 next_out_array,
2954 next_out_offset,
2955 total_out,
2956 ) {
2957 continue;
2958 }
2959 if self.available_out_ == 0usize
2960 && (self.stream_state_ as i32
2961 == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32)
2962 && (remaining_block_size == 0usize
2963 || op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_PROCESS as i32)
2964 {
2965 let is_last =
2966 *available_in == 0 && op == BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
2967 let force_flush =
2968 *available_in == 0 && op == BrotliEncoderOperation::BROTLI_OPERATION_FLUSH;
2969
2970 self.update_size_hint(*available_in);
2971 let mut avail_out = self.available_out_;
2972 let result =
2973 self.encode_data(is_last, force_flush, &mut avail_out, metablock_callback);
2974 self.available_out_ = avail_out;
2975 if !result {
2977 return false;
2978 }
2979 if force_flush {
2980 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2981 }
2982 if is_last {
2983 self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FINISHED;
2984 }
2985 {
2986 continue;
2987 }
2988 }
2989 {
2990 break;
2991 }
2992 }
2993 self.check_flush_complete();
2994 true
2995 }
2996
2997 pub fn is_finished(&self) -> bool {
2998 self.stream_state_ == BrotliEncoderStreamState::BROTLI_STREAM_FINISHED
2999 && !self.has_more_output()
3000 }
3001
3002 pub fn has_more_output(&self) -> bool {
3003 self.available_out_ != 0
3004 }
3005
3006 pub fn take_output(&mut self, size: &mut usize) -> &[u8] {
3007 let mut consumed_size: usize = self.available_out_;
3008 let mut result: &[u8] = GetNextOut!(*self);
3009 if *size != 0 {
3010 consumed_size = min(*size, self.available_out_);
3011 }
3012 if consumed_size != 0 {
3013 self.next_out_ = NextOutIncrement(&self.next_out_, consumed_size as i32);
3014 self.available_out_ = self.available_out_.wrapping_sub(consumed_size);
3015 self.total_out_ = self.total_out_.wrapping_add(consumed_size as u64);
3016 CheckFlushCompleteInner(
3017 &mut self.stream_state_,
3018 self.available_out_,
3019 &mut self.next_out_,
3020 );
3021 *size = consumed_size;
3022 } else {
3023 *size = 0usize;
3024 result = &[];
3025 }
3026 result
3027 }
3028}
3029
3030pub fn BrotliEncoderVersion() -> u32 {
3031 0x0100_0f01
3032}
3033
3034impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
3035 pub fn input_block_size(&mut self) -> usize {
3036 if !self.ensure_initialized() {
3037 return 0;
3038 }
3039 1 << self.params.lgblock
3040 }
3041
3042 pub fn write_data<
3043 'a,
3044 MetablockCallback: FnMut(
3045 &mut interface::PredictionModeContextMap<InputReferenceMut>,
3046 &mut [interface::StaticCommand],
3047 interface::InputPair,
3048 &mut Alloc,
3049 ),
3050 >(
3051 &'a mut self,
3052 is_last: i32,
3054 force_flush: i32,
3056 out_size: &mut usize,
3058 output: &'a mut &'a mut [u8],
3060 metablock_callback: &mut MetablockCallback,
3061 ) -> bool {
3062 let ret = self.encode_data(is_last != 0, force_flush != 0, out_size, metablock_callback);
3063 *output = self.storage_.slice_mut();
3064 ret
3065 }
3066}
3067
3068#[cfg(feature = "std")]
3069mod test {
3070 #[cfg(test)]
3071 use alloc_stdlib::StandardAlloc;
3072
3073 #[test]
3074 fn test_encoder_compress() {
3075 let input = include_bytes!("../../testdata/alice29.txt");
3076 let mut output_buffer = [0; 100000];
3077 let mut output_len = output_buffer.len();
3078 let ret = super::encoder_compress(
3079 StandardAlloc::default(),
3080 &mut StandardAlloc::default(),
3081 9,
3082 16,
3083 super::BrotliEncoderMode::BROTLI_MODE_GENERIC,
3084 input.len(),
3085 input,
3086 &mut output_len,
3087 &mut output_buffer,
3088 &mut |_, _, _, _| (),
3089 );
3090 assert!(ret);
3091 assert_eq!(output_len, 51737);
3092 let mut roundtrip = [0u8; 200000];
3093 let (_, s, t) = super::super::test::oneshot_decompress(
3094 &output_buffer[..output_len],
3095 &mut roundtrip[..],
3096 );
3097 assert_eq!(roundtrip[..t], input[..]);
3098 assert_eq!(s, output_len);
3099 }
3100}