Skip to main content

brotli/enc/
encode.rs

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