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