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