Skip to main content

simd_brotli/enc/
encode.rs

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