Skip to main content

simd_brotli/enc/
encode.rs

1use crate::alloc::Allocator;
2use core;
3use core::cmp::{max, min};
4
5use super::super::alloc;
6use super::super::alloc::{SliceWrapper, SliceWrapperMut};
7use super::backward_references::{
8    AdvHashSpecialization, AdvHasher, AnyHasher, BasicHasher, BrotliCreateBackwardReferences,
9    BrotliEncoderMode, BrotliEncoderParams, BrotliHasherParams, H2Sub, H3Sub, H4Sub, H5Sub, H6Sub,
10    H9, H9_BLOCK_BITS, H9_BLOCK_SIZE, H9_BUCKET_BITS, H9_NUM_LAST_DISTANCES_TO_CHECK, H54Sub,
11    HQ5Sub, HQ7Sub, HowPrepared, StoreLookaheadThenStore, Struct1, UnionHasher,
12};
13use super::bit_cost::{BitsEntropy, shannon_entropy};
14use super::brotli_bit_stream::{
15    BrotliWriteEmptyLastMetaBlock, BrotliWriteMetadataMetaBlock, BrotliWritePaddingMetaBlock,
16    MetaBlockSplit, RecoderState, store_meta_block, store_meta_block_fast,
17    store_meta_block_trivial, store_uncompressed_meta_block,
18};
19use super::combined_alloc::BrotliAlloc;
20use super::command::{BrotliDistanceParams, Command, get_length_code};
21use super::compress_fragment::compress_fragment_fast;
22use super::compress_fragment_two_pass::{BrotliWriteBits, compress_fragment_two_pass};
23use super::constants::{
24    BROTLI_CONTEXT, BROTLI_CONTEXT_LUT, BROTLI_MAX_NDIRECT, BROTLI_MAX_NPOSTFIX,
25    BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS, BROTLI_WINDOW_GAP,
26};
27use super::hash_to_binary_tree::InitializeH10;
28use super::histogram::{
29    ContextType, CostAccessors, HistogramCommand, HistogramDistance, HistogramLiteral,
30};
31use super::interface;
32use super::metablock::{
33    BrotliBuildMetaBlock, BrotliBuildMetaBlockGreedy, BrotliInitDistanceParams,
34    BrotliOptimizeHistograms,
35};
36pub use super::parameters::BrotliEncoderParameter;
37use super::static_dict::{BrotliGetDictionary, kNumDistanceCacheEntries};
38use super::util::{Log2FloorNonZero, floatX};
39use crate::enc::combined_alloc::{alloc_default, allocate};
40use crate::enc::input_pair::InputReferenceMut;
41use crate::enc::utf8_util::is_mostly_utf8;
42
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_2021) => {
123        GetNextOutInternal(&$s.next_out_, $s.storage_.slice_mut(), &mut $s.tiny_buf_)
124    };
125}
126fn NextOutIncrement(next_out: &NextOut, inc: i32) -> NextOut {
127    match next_out {
128        &NextOut::DynamicStorage(offset) => NextOut::DynamicStorage((offset as i32 + inc) as u32),
129        &NextOut::TinyBuf(offset) => NextOut::TinyBuf((offset as i32 + inc) as u32),
130        &NextOut::None => NextOut::None,
131    }
132}
133fn IsNextOutNull(next_out: &NextOut) -> bool {
134    match next_out {
135        &NextOut::DynamicStorage(_) => false,
136        &NextOut::TinyBuf(_) => false,
137        &NextOut::None => true,
138    }
139}
140
141#[derive(Clone, Copy, Debug)]
142pub enum IsFirst {
143    NothingWritten,
144    HeaderWritten,
145    FirstCatableByteWritten,
146    BothCatableBytesWritten,
147}
148
149pub struct BrotliEncoderStateStruct<Alloc: BrotliAlloc> {
150    pub params: BrotliEncoderParams,
151    pub m8: Alloc,
152    pub hasher_: UnionHasher<Alloc>,
153    pub input_pos_: u64,
154    pub ringbuffer_: RingBuffer<Alloc>,
155    pub cmd_alloc_size_: usize,
156    pub commands_: <Alloc as Allocator<Command>>::AllocatedMemory, // 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    #[cfg_attr(feature = "hotpath", hotpath::measure)]
813    pub fn copy_input_to_ring_buffer(&mut self, input_size: usize, input_buffer: &[u8]) {
814        if !self.ensure_initialized() {
815            return;
816        }
817        RingBufferWrite(
818            &mut self.m8,
819            input_buffer,
820            input_size,
821            &mut self.ringbuffer_,
822        );
823        self.input_pos_ = self.input_pos_.wrapping_add(input_size as u64);
824        if (self.ringbuffer_).pos_ <= (self.ringbuffer_).mask_ {
825            let start = (self.ringbuffer_)
826                .buffer_index
827                .wrapping_add((self.ringbuffer_).pos_ as usize);
828            for item in (self.ringbuffer_).data_mo.slice_mut()[start..(start + 7)].iter_mut() {
829                *item = 0;
830            }
831        }
832    }
833}
834
835fn ChooseHasher(params: &mut BrotliEncoderParams) {
836    let hparams = &mut params.hasher;
837    if params.quality >= 10 && !params.q9_5 {
838        hparams.type_ = 10;
839    } else if params.quality == 10 {
840        // we are using quality 10 as a proxy for "9.5"
841        hparams.type_ = 9;
842        hparams.num_last_distances_to_check = H9_NUM_LAST_DISTANCES_TO_CHECK as i32;
843        hparams.block_bits = H9_BLOCK_BITS as i32;
844        hparams.bucket_bits = H9_BUCKET_BITS as i32;
845        hparams.hash_len = 4;
846    } else if params.quality == 9 {
847        hparams.type_ = 9;
848        hparams.num_last_distances_to_check = H9_NUM_LAST_DISTANCES_TO_CHECK as i32;
849        hparams.block_bits = H9_BLOCK_BITS as i32;
850        hparams.bucket_bits = H9_BUCKET_BITS as i32;
851        hparams.hash_len = 4;
852    } else if params.quality == 4 && (params.size_hint >= (1i32 << 20) as usize) {
853        hparams.type_ = 54i32;
854    } else if params.quality < 5 {
855        hparams.type_ = params.quality;
856    } else if params.lgwin <= 16 {
857        hparams.type_ = if params.quality < 7 {
858            40i32
859        } else if params.quality < 9 {
860            41i32
861        } else {
862            42i32
863        };
864    } else if ((params.q9_5 && params.size_hint > (1 << 20)) || params.size_hint > (1 << 22))
865        && (params.lgwin >= 19i32)
866    {
867        hparams.type_ = 6i32;
868        hparams.block_bits = min(params.quality - 1, 9);
869        hparams.bucket_bits = 15i32;
870        hparams.hash_len = 5i32;
871        hparams.num_last_distances_to_check = if params.quality < 7 {
872            4i32
873        } else if params.quality < 9 {
874            10i32
875        } else {
876            16i32
877        };
878    } else {
879        hparams.type_ = 5i32;
880        hparams.block_bits = min(params.quality - 1, 9);
881        hparams.bucket_bits = if params.quality < 7 && params.size_hint <= (1 << 20) {
882            14i32
883        } else {
884            15i32
885        };
886        hparams.num_last_distances_to_check = if params.quality < 7 {
887            4i32
888        } else if params.quality < 9 {
889            10i32
890        } else {
891            16i32
892        };
893    }
894}
895
896fn InitializeH2<AllocU32: alloc::Allocator<u32>>(
897    m32: &mut AllocU32,
898    params: &BrotliEncoderParams,
899) -> BasicHasher<H2Sub<AllocU32>> {
900    BasicHasher {
901        GetHasherCommon: Struct1 {
902            params: params.hasher,
903            is_prepared_: 1,
904            dict_num_lookups: 0,
905            dict_num_matches: 0,
906        },
907        buckets_: H2Sub {
908            buckets_: m32.alloc_cell(65537 + 8),
909        },
910        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
911    }
912}
913fn InitializeH3<AllocU32: alloc::Allocator<u32>>(
914    m32: &mut AllocU32,
915    params: &BrotliEncoderParams,
916) -> BasicHasher<H3Sub<AllocU32>> {
917    BasicHasher {
918        GetHasherCommon: Struct1 {
919            params: params.hasher,
920            is_prepared_: 1,
921            dict_num_lookups: 0,
922            dict_num_matches: 0,
923        },
924        buckets_: H3Sub {
925            buckets_: m32.alloc_cell(65538 + 8),
926        },
927        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
928    }
929}
930fn InitializeH4<AllocU32: alloc::Allocator<u32>>(
931    m32: &mut AllocU32,
932    params: &BrotliEncoderParams,
933) -> BasicHasher<H4Sub<AllocU32>> {
934    BasicHasher {
935        GetHasherCommon: Struct1 {
936            params: params.hasher,
937            is_prepared_: 1,
938            dict_num_lookups: 0,
939            dict_num_matches: 0,
940        },
941        buckets_: H4Sub {
942            buckets_: m32.alloc_cell(131072 + 8),
943        },
944        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
945    }
946}
947fn InitializeH54<AllocU32: alloc::Allocator<u32>>(
948    m32: &mut AllocU32,
949    params: &BrotliEncoderParams,
950) -> BasicHasher<H54Sub<AllocU32>> {
951    BasicHasher {
952        GetHasherCommon: Struct1 {
953            params: params.hasher,
954            is_prepared_: 1,
955            dict_num_lookups: 0,
956            dict_num_matches: 0,
957        },
958        buckets_: H54Sub {
959            buckets_: m32.alloc_cell(1048580 + 8),
960        },
961        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
962    }
963}
964
965fn InitializeH9<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
966    m16: &mut Alloc,
967    params: &BrotliEncoderParams,
968) -> H9<Alloc> {
969    H9 {
970        dict_search_stats_: Struct1 {
971            params: params.hasher,
972            is_prepared_: 1,
973            dict_num_lookups: 0,
974            dict_num_matches: 0,
975        },
976        num_: allocate::<u16, _>(m16, 1 << H9_BUCKET_BITS),
977        buckets_: allocate::<u32, _>(m16, H9_BLOCK_SIZE << H9_BUCKET_BITS),
978        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
979    }
980}
981
982fn InitializeH5<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
983    m16: &mut Alloc,
984    params: &BrotliEncoderParams,
985) -> UnionHasher<Alloc> {
986    let block_size = 1u64 << params.hasher.block_bits;
987    let bucket_size = 1u64 << params.hasher.bucket_bits;
988    let buckets: <Alloc as Allocator<u32>>::AllocatedMemory =
989        allocate::<u32, _>(m16, (bucket_size * block_size) as usize);
990    let num: <Alloc as Allocator<u16>>::AllocatedMemory =
991        allocate::<u16, _>(m16, bucket_size as usize);
992
993    if params.hasher.block_bits == (HQ5Sub {}).block_bits()
994        && (1 << params.hasher.bucket_bits) == (HQ5Sub {}).bucket_size()
995    {
996        return UnionHasher::H5q5(AdvHasher {
997            buckets,
998            h9_opts: super::backward_references::H9Opts::new(&params.hasher),
999            num,
1000            GetHasherCommon: Struct1 {
1001                params: params.hasher,
1002                is_prepared_: 1,
1003                dict_num_lookups: 0,
1004                dict_num_matches: 0,
1005            },
1006            specialization: HQ5Sub {},
1007        });
1008    }
1009    if params.hasher.block_bits == (HQ7Sub {}).block_bits()
1010        && (1 << params.hasher.bucket_bits) == (HQ7Sub {}).bucket_size()
1011    {
1012        return UnionHasher::H5q7(AdvHasher {
1013            buckets,
1014            h9_opts: super::backward_references::H9Opts::new(&params.hasher),
1015            num,
1016            GetHasherCommon: Struct1 {
1017                params: params.hasher,
1018                is_prepared_: 1,
1019                dict_num_lookups: 0,
1020                dict_num_matches: 0,
1021            },
1022            specialization: HQ7Sub {},
1023        });
1024    }
1025    UnionHasher::H5(AdvHasher {
1026        buckets,
1027        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
1028        num,
1029        GetHasherCommon: Struct1 {
1030            params: params.hasher,
1031            is_prepared_: 1,
1032            dict_num_lookups: 0,
1033            dict_num_matches: 0,
1034        },
1035        specialization: H5Sub {
1036            hash_shift_: 32i32 - params.hasher.bucket_bits,
1037            bucket_size_: bucket_size as u32,
1038            block_bits_: params.hasher.block_bits,
1039            block_mask_: block_size.wrapping_sub(1) as u32,
1040        },
1041    })
1042}
1043fn InitializeH6<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1044    m16: &mut Alloc,
1045    params: &BrotliEncoderParams,
1046) -> UnionHasher<Alloc> {
1047    let block_size = 1u64 << params.hasher.block_bits;
1048    let bucket_size = 1u64 << params.hasher.bucket_bits;
1049    let buckets: <Alloc as Allocator<u32>>::AllocatedMemory =
1050        allocate::<u32, _>(m16, (bucket_size * block_size) as usize);
1051    let num: <Alloc as Allocator<u16>>::AllocatedMemory =
1052        allocate::<u16, _>(m16, bucket_size as usize);
1053    UnionHasher::H6(AdvHasher {
1054        buckets,
1055        num,
1056        h9_opts: super::backward_references::H9Opts::new(&params.hasher),
1057        GetHasherCommon: Struct1 {
1058            params: params.hasher,
1059            is_prepared_: 1,
1060            dict_num_lookups: 0,
1061            dict_num_matches: 0,
1062        },
1063        specialization: H6Sub {
1064            bucket_size_: 1u32 << params.hasher.bucket_bits,
1065            block_bits_: params.hasher.block_bits,
1066            block_mask_: block_size.wrapping_sub(1) as u32,
1067            hash_mask: 0xffffffffffffffffu64 >> (64i32 - 8i32 * params.hasher.hash_len),
1068            hash_shift_: 64i32 - params.hasher.bucket_bits,
1069        },
1070    })
1071}
1072
1073fn BrotliMakeHasher<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1074    m: &mut Alloc,
1075    params: &BrotliEncoderParams,
1076    ringbuffer_break: Option<core::num::NonZeroUsize>,
1077) -> UnionHasher<Alloc> {
1078    let hasher_type: i32 = params.hasher.type_;
1079    if hasher_type == 2i32 {
1080        return UnionHasher::H2(InitializeH2(m, params));
1081    }
1082    if hasher_type == 3i32 {
1083        return UnionHasher::H3(InitializeH3(m, params));
1084    }
1085    if hasher_type == 4i32 {
1086        return UnionHasher::H4(InitializeH4(m, params));
1087    }
1088    if hasher_type == 5i32 {
1089        return InitializeH5(m, params);
1090    }
1091    if hasher_type == 6i32 {
1092        return InitializeH6(m, params);
1093    }
1094    if hasher_type == 9i32 {
1095        return UnionHasher::H9(InitializeH9(m, params));
1096    }
1097    /*
1098        if hasher_type == 40i32 {
1099          return InitializeH40(params);
1100        }
1101        if hasher_type == 41i32 {
1102          return InitializeH41(params);
1103        }
1104        if hasher_type == 42i32 {
1105          return InitializeH42(params);
1106        }
1107    */
1108    if hasher_type == 54i32 {
1109        return UnionHasher::H54(InitializeH54(m, params));
1110    }
1111    if hasher_type == 10i32 {
1112        return UnionHasher::H10(InitializeH10(m, false, params, ringbuffer_break, 0));
1113    }
1114    // since we don't support all of these, fall back to something sane
1115    InitializeH6(m, params)
1116
1117    //  return UnionHasher::Uninit;
1118}
1119fn HasherReset<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(t: &mut UnionHasher<Alloc>) {
1120    match t {
1121        &mut UnionHasher::Uninit => {}
1122        _ => (t.GetHasherCommon()).is_prepared_ = 0i32,
1123    };
1124}
1125
1126pub(crate) fn hasher_setup<Alloc: Allocator<u16> + Allocator<u32>>(
1127    m16: &mut Alloc,
1128    handle: &mut UnionHasher<Alloc>,
1129    params: &mut BrotliEncoderParams,
1130    ringbuffer_break: Option<core::num::NonZeroUsize>,
1131    data: &[u8],
1132    position: usize,
1133    input_size: usize,
1134    is_last: bool,
1135) {
1136    let one_shot = position == 0 && is_last;
1137    let is_uninit = match (handle) {
1138        &mut UnionHasher::Uninit => true,
1139        _ => false,
1140    };
1141    if is_uninit {
1142        //let alloc_size: usize;
1143        ChooseHasher(&mut (*params));
1144        //alloc_size = HasherSize(params, one_shot, input_size);
1145        //xself = BrotliAllocate(m, alloc_size.wrapping_mul(::core::mem::size_of::<u8>()))
1146        *handle = BrotliMakeHasher(m16, params, ringbuffer_break);
1147        handle.GetHasherCommon().params = params.hasher;
1148        HasherReset(handle); // this sets everything to zero, unlike in C
1149        handle.GetHasherCommon().is_prepared_ = 1;
1150    } else {
1151        match handle.Prepare(one_shot, input_size, data) {
1152            HowPrepared::ALREADY_PREPARED => {}
1153            HowPrepared::NEWLY_PREPARED => {
1154                if position == 0usize {
1155                    let common = handle.GetHasherCommon();
1156                    common.dict_num_lookups = 0usize;
1157                    common.dict_num_matches = 0usize;
1158                }
1159            }
1160        }
1161    }
1162}
1163
1164fn HasherPrependCustomDictionary<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1165    m: &mut Alloc,
1166    handle: &mut UnionHasher<Alloc>,
1167    params: &mut BrotliEncoderParams,
1168    ringbuffer_break: Option<core::num::NonZeroUsize>,
1169    size: usize,
1170    dict: &[u8],
1171) {
1172    hasher_setup(
1173        m,
1174        handle,
1175        params,
1176        ringbuffer_break,
1177        dict,
1178        0usize,
1179        size,
1180        false,
1181    );
1182    match handle {
1183        &mut UnionHasher::H2(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1184        &mut UnionHasher::H3(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1185        &mut UnionHasher::H4(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1186        &mut UnionHasher::H5(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1187        &mut UnionHasher::H5q7(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1188        &mut UnionHasher::H5q5(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1189        &mut UnionHasher::H6(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1190        &mut UnionHasher::H9(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1191        &mut UnionHasher::H54(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1192        &mut UnionHasher::H10(ref mut hasher) => StoreLookaheadThenStore(hasher, size, dict),
1193        &mut UnionHasher::Uninit => panic!("Uninitialized"),
1194    }
1195}
1196
1197impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1198    pub fn set_custom_dictionary(&mut self, size: usize, dict: &[u8]) {
1199        self.set_custom_dictionary_with_optional_precomputed_hasher(
1200            size,
1201            dict,
1202            UnionHasher::Uninit,
1203            false,
1204        )
1205    }
1206
1207    pub fn set_custom_dictionary_with_optional_precomputed_hasher(
1208        &mut self,
1209        size: usize,
1210        mut dict: &[u8],
1211        opt_hasher: UnionHasher<Alloc>,
1212        is_multithreading_file_continue: bool,
1213    ) {
1214        self.params.use_dictionary = false;
1215
1216        self.prev_byte_ = 0;
1217        self.prev_byte2_ = 0;
1218        if is_multithreading_file_continue {
1219            if size > 0 {
1220                self.prev_byte_ = dict[size.wrapping_sub(1)];
1221            }
1222            if size > 1 {
1223                self.prev_byte2_ = dict[size.wrapping_sub(2)];
1224            }
1225        }
1226
1227        let has_optional_hasher = if let UnionHasher::Uninit = opt_hasher {
1228            false
1229        } else {
1230            true
1231        };
1232        let max_dict_size: usize = (1usize << self.params.lgwin).wrapping_sub(16);
1233        self.hasher_ = opt_hasher;
1234        let mut dict_size: usize = size;
1235        if !self.ensure_initialized() {
1236            return;
1237        }
1238        if dict_size == 0 || self.params.quality == 0 || self.params.quality == 1 || size <= 1 {
1239            self.params.catable = true; // don't risk a too-short dictionary
1240            self.params.appendable = true; // don't risk a too-short dictionary
1241            return;
1242        }
1243        self.custom_dictionary = true;
1244        if size > max_dict_size {
1245            dict = &dict[size.wrapping_sub(max_dict_size)..];
1246            dict_size = max_dict_size;
1247        }
1248        self.custom_dictionary_size = core::num::NonZeroUsize::new(dict_size);
1249        self.copy_input_to_ring_buffer(dict_size, dict);
1250        self.last_flush_pos_ = dict_size as u64;
1251        self.last_processed_pos_ = dict_size as u64;
1252        let m16 = &mut self.m8;
1253        if cfg!(debug_assertions) || !has_optional_hasher {
1254            let mut orig_hasher = UnionHasher::Uninit;
1255            if has_optional_hasher {
1256                orig_hasher = core::mem::replace(&mut self.hasher_, UnionHasher::Uninit);
1257            }
1258            HasherPrependCustomDictionary(
1259                m16,
1260                &mut self.hasher_,
1261                &mut self.params,
1262                self.custom_dictionary_size,
1263                dict_size,
1264                dict,
1265            );
1266            if has_optional_hasher {
1267                debug_assert!(orig_hasher == self.hasher_);
1268                DestroyHasher(m16, &mut orig_hasher);
1269            }
1270        }
1271    }
1272}
1273
1274pub fn BrotliEncoderMaxCompressedSizeMulti(input_size: usize, num_threads: usize) -> usize {
1275    BrotliEncoderMaxCompressedSize(input_size) + num_threads * 8
1276}
1277
1278pub fn BrotliEncoderMaxCompressedSize(input_size: usize) -> usize {
1279    let magic_size = 16usize;
1280    let num_large_blocks: usize = input_size >> 14;
1281    let tail: usize = input_size.wrapping_sub(num_large_blocks << 24);
1282    let tail_overhead: usize = (if tail > (1i32 << 20) as usize {
1283        4i32
1284    } else {
1285        3i32
1286    }) as usize;
1287    let overhead: usize = (2usize)
1288        .wrapping_add((4usize).wrapping_mul(num_large_blocks))
1289        .wrapping_add(tail_overhead)
1290        .wrapping_add(1);
1291    let result: usize = input_size.wrapping_add(overhead);
1292    if input_size == 0usize {
1293        return 1 + magic_size;
1294    }
1295    if result < input_size {
1296        0usize
1297    } else {
1298        result + magic_size
1299    }
1300}
1301
1302fn InitOrStitchToPreviousBlock<Alloc: alloc::Allocator<u16> + alloc::Allocator<u32>>(
1303    m: &mut Alloc,
1304    handle: &mut UnionHasher<Alloc>,
1305    data: &[u8],
1306    mask: usize,
1307    ringbuffer_break: Option<core::num::NonZeroUsize>,
1308    params: &mut BrotliEncoderParams,
1309    position: usize,
1310    input_size: usize,
1311    is_last: bool,
1312) {
1313    hasher_setup(
1314        m,
1315        handle,
1316        params,
1317        ringbuffer_break,
1318        data,
1319        position,
1320        input_size,
1321        is_last,
1322    );
1323    handle.StitchToPreviousBlock(input_size, position, data, mask);
1324}
1325
1326fn should_compress(
1327    data: &[u8],
1328    mask: usize,
1329    last_flush_pos: u64,
1330    bytes: usize,
1331    num_literals: usize,
1332    num_commands: usize,
1333) -> bool {
1334    const K_SAMPLE_RATE: u32 = 13;
1335    const K_MIN_ENTROPY: floatX = 7.92;
1336
1337    if num_commands < (bytes >> 8) + 2 && num_literals as floatX > 0.99 * bytes as floatX {
1338        let mut literal_histo = [0u32; 256];
1339        let bit_cost_threshold = (bytes as floatX) * K_MIN_ENTROPY / (K_SAMPLE_RATE as floatX);
1340        let t = bytes
1341            .wrapping_add(K_SAMPLE_RATE as usize)
1342            .wrapping_sub(1)
1343            .wrapping_div(K_SAMPLE_RATE as usize);
1344        let mut pos = last_flush_pos as u32;
1345        for _ in 0..t {
1346            let value = &mut literal_histo[data[pos as usize & mask] as usize];
1347            *value = value.wrapping_add(1);
1348            pos = pos.wrapping_add(K_SAMPLE_RATE);
1349        }
1350        if BitsEntropy(&literal_histo[..], 256) > bit_cost_threshold {
1351            return false;
1352        }
1353    }
1354    true
1355}
1356
1357/* Chooses the literal context mode for a metablock */
1358fn ChooseContextMode(
1359    params: &BrotliEncoderParams,
1360    data: &[u8],
1361    pos: usize,
1362    mask: usize,
1363    length: usize,
1364) -> ContextType {
1365    /* We only do the computation for the option of something else than
1366    CONTEXT_UTF8 for the highest qualities */
1367    match params.mode {
1368        BrotliEncoderMode::BROTLI_FORCE_LSB_PRIOR => return ContextType::CONTEXT_LSB6,
1369        BrotliEncoderMode::BROTLI_FORCE_MSB_PRIOR => return ContextType::CONTEXT_MSB6,
1370        BrotliEncoderMode::BROTLI_FORCE_UTF8_PRIOR => return ContextType::CONTEXT_UTF8,
1371        BrotliEncoderMode::BROTLI_FORCE_SIGNED_PRIOR => return ContextType::CONTEXT_SIGNED,
1372        _ => {}
1373    }
1374    if (params.quality >= 10 && !is_mostly_utf8(data, pos, mask, length, kMinUTF8Ratio)) {
1375        return ContextType::CONTEXT_SIGNED;
1376    }
1377    ContextType::CONTEXT_UTF8
1378}
1379
1380#[derive(PartialEq, Eq, Copy, Clone)]
1381pub enum BrotliEncoderOperation {
1382    BROTLI_OPERATION_PROCESS = 0,
1383    BROTLI_OPERATION_FLUSH = 1,
1384    BROTLI_OPERATION_FINISH = 2,
1385    BROTLI_OPERATION_EMIT_METADATA = 3,
1386}
1387
1388#[allow(unused)]
1389fn MakeUncompressedStream(input: &[u8], input_size: usize, output: &mut [u8]) -> usize {
1390    let mut size: usize = input_size;
1391    let mut result: usize = 0usize;
1392    let mut offset: usize = 0usize;
1393    if input_size == 0usize {
1394        output[0] = 6u8;
1395        return 1;
1396    }
1397    output[result] = 0x21u8;
1398    result = result.wrapping_add(1);
1399    output[result] = 0x3u8;
1400    result = result.wrapping_add(1);
1401    while size > 0usize {
1402        let mut nibbles: u32 = 0u32;
1403
1404        let chunk_size: u32 = if size > (1u32 << 24) as usize {
1405            1u32 << 24
1406        } else {
1407            size as u32
1408        };
1409        if chunk_size > 1u32 << 16 {
1410            nibbles = if chunk_size > 1u32 << 20 { 2i32 } else { 1i32 } as u32;
1411        }
1412        let bits: u32 = nibbles << 1
1413            | chunk_size.wrapping_sub(1) << 3
1414            | 1u32 << (19u32).wrapping_add((4u32).wrapping_mul(nibbles));
1415        output[result] = bits as u8;
1416        result = result.wrapping_add(1);
1417        output[result] = (bits >> 8) as u8;
1418        result = result.wrapping_add(1);
1419        output[result] = (bits >> 16) as u8;
1420        result = result.wrapping_add(1);
1421        if nibbles == 2u32 {
1422            output[result] = (bits >> 24) as u8;
1423            result = result.wrapping_add(1);
1424        }
1425        output[result..(result + chunk_size as usize)]
1426            .clone_from_slice(&input[offset..(offset + chunk_size as usize)]);
1427        result = result.wrapping_add(chunk_size as usize);
1428        offset = offset.wrapping_add(chunk_size as usize);
1429        size = size.wrapping_sub(chunk_size as usize);
1430    }
1431    output[result] = 3u8;
1432    result = result.wrapping_add(1);
1433    result
1434}
1435
1436#[cfg_attr(not(feature = "ffi-api"), cfg(test))]
1437#[cfg_attr(feature = "hotpath", hotpath::measure)]
1438pub(crate) fn encoder_compress<
1439    Alloc: BrotliAlloc,
1440    MetablockCallback: FnMut(
1441        &mut interface::PredictionModeContextMap<InputReferenceMut>,
1442        &mut [interface::StaticCommand],
1443        interface::InputPair,
1444        &mut Alloc,
1445    ),
1446>(
1447    empty_m8: Alloc,
1448    m8: &mut Alloc,
1449    mut quality: i32,
1450    lgwin: i32,
1451    mode: BrotliEncoderMode,
1452    input_size: usize,
1453    input_buffer: &[u8],
1454    encoded_size: &mut usize,
1455    encoded_buffer: &mut [u8],
1456    metablock_callback: &mut MetablockCallback,
1457) -> bool {
1458    let out_size: usize = *encoded_size;
1459    let input_start = input_buffer;
1460    let output_start = encoded_buffer;
1461    let max_out_size: usize = BrotliEncoderMaxCompressedSize(input_size);
1462    if out_size == 0 {
1463        return false;
1464    }
1465    if input_size == 0 {
1466        *encoded_size = 1;
1467        output_start[0] = 6;
1468        return true;
1469    }
1470    let mut is_fallback = false;
1471    let mut is_9_5 = false;
1472    if quality == 10 {
1473        quality = 9;
1474        is_9_5 = true;
1475    }
1476    if !is_fallback {
1477        let mut s_orig = BrotliEncoderStateStruct::new(core::mem::replace(m8, empty_m8));
1478        if is_9_5 {
1479            let mut params = BrotliEncoderParams::default();
1480            params.q9_5 = true;
1481            params.quality = 10;
1482            ChooseHasher(&mut params);
1483            s_orig.hasher_ = BrotliMakeHasher(m8, &params, None /*no custom dict */);
1484        }
1485        let mut result: bool;
1486        {
1487            let s = &mut s_orig;
1488            let mut available_in: usize = input_size;
1489            let next_in_array: &[u8] = input_buffer;
1490            let mut next_in_offset: usize = 0;
1491            let mut available_out: usize = *encoded_size;
1492            let next_out_array: &mut [u8] = output_start;
1493            let mut next_out_offset: usize = 0;
1494            let mut total_out = Some(0);
1495            s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_QUALITY, quality as u32);
1496            s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LGWIN, lgwin as u32);
1497            s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_MODE, mode as u32);
1498            s.set_parameter(
1499                BrotliEncoderParameter::BROTLI_PARAM_SIZE_HINT,
1500                input_size as u32,
1501            );
1502            if lgwin > BROTLI_MAX_WINDOW_BITS as i32 {
1503                s.set_parameter(BrotliEncoderParameter::BROTLI_PARAM_LARGE_WINDOW, 1);
1504            }
1505            result = s.compress_stream(
1506                BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
1507                &mut available_in,
1508                next_in_array,
1509                &mut next_in_offset,
1510                &mut available_out,
1511                next_out_array,
1512                &mut next_out_offset,
1513                &mut total_out,
1514                metablock_callback,
1515            );
1516            if !s.is_finished() {
1517                result = false;
1518            }
1519
1520            *encoded_size = total_out.unwrap();
1521            BrotliEncoderDestroyInstance(s);
1522        }
1523        let _ = core::mem::replace(m8, s_orig.m8);
1524        if !result || max_out_size != 0 && (*encoded_size > max_out_size) {
1525            is_fallback = true;
1526        } else {
1527            return true;
1528        }
1529    }
1530    assert_ne!(is_fallback, false);
1531    *encoded_size = 0;
1532    if max_out_size == 0 {
1533        return false;
1534    }
1535    if out_size >= max_out_size {
1536        *encoded_size = MakeUncompressedStream(input_start, input_size, output_start);
1537        return true;
1538    }
1539    false
1540}
1541
1542impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1543    fn inject_byte_padding_block(&mut self) {
1544        let mut seal: u32 = self.last_bytes_ as u32;
1545        let mut seal_bits: usize = self.last_bytes_bits_ as usize;
1546        let destination: &mut [u8];
1547        self.last_bytes_ = 0;
1548        self.last_bytes_bits_ = 0;
1549        seal |= 0x6u32 << seal_bits;
1550
1551        seal_bits = seal_bits.wrapping_add(6);
1552        if !IsNextOutNull(&self.next_out_) {
1553            destination = &mut GetNextOut!(*self)[self.available_out_..];
1554        } else {
1555            destination = &mut self.tiny_buf_[..];
1556            self.next_out_ = NextOut::TinyBuf(0);
1557        }
1558        destination[0] = seal as u8;
1559        if seal_bits > 8usize {
1560            destination[1] = (seal >> 8) as u8;
1561        }
1562        if seal_bits > 16usize {
1563            destination[2] = (seal >> 16) as u8;
1564        }
1565        self.available_out_ = self
1566            .available_out_
1567            .wrapping_add(seal_bits.wrapping_add(7) >> 3);
1568    }
1569
1570    fn inject_flush_or_push_output(
1571        &mut self,
1572        available_out: &mut usize,
1573        next_out_array: &mut [u8],
1574        next_out_offset: &mut usize,
1575        total_out: &mut Option<usize>,
1576    ) -> bool {
1577        if self.stream_state_ as i32
1578            == BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED as i32
1579            && (self.last_bytes_bits_ as i32 != 0i32)
1580        {
1581            self.inject_byte_padding_block();
1582            return true;
1583        }
1584        if self.available_out_ != 0usize && (*available_out != 0usize) {
1585            let copy_output_size: usize = min(self.available_out_, *available_out);
1586            (*next_out_array)[(*next_out_offset)..(*next_out_offset + copy_output_size)]
1587                .clone_from_slice(&GetNextOut!(self)[..copy_output_size]);
1588            //memcpy(*next_out, s.next_out_, copy_output_size);
1589            *next_out_offset = next_out_offset.wrapping_add(copy_output_size);
1590            *available_out = available_out.wrapping_sub(copy_output_size);
1591            self.next_out_ = NextOutIncrement(&self.next_out_, (copy_output_size as i32));
1592            self.available_out_ = self.available_out_.wrapping_sub(copy_output_size);
1593            self.total_out_ = self.total_out_.wrapping_add(copy_output_size as u64);
1594            if let &mut Some(ref mut total_out_inner) = total_out {
1595                *total_out_inner = self.total_out_ as usize;
1596            }
1597            return true;
1598        }
1599        false
1600    }
1601
1602    fn unprocessed_input_size(&self) -> u64 {
1603        self.input_pos_.wrapping_sub(self.last_processed_pos_)
1604    }
1605
1606    fn update_size_hint(&mut self, available_in: usize) {
1607        if self.params.size_hint == 0usize {
1608            let delta: u64 = self.unprocessed_input_size();
1609            let tail: u64 = available_in as u64;
1610            let limit: u32 = 1u32 << 30;
1611            let total: u32;
1612            if delta >= u64::from(limit)
1613                || tail >= u64::from(limit)
1614                || delta.wrapping_add(tail) >= u64::from(limit)
1615            {
1616                total = limit;
1617            } else {
1618                total = delta.wrapping_add(tail) as u32;
1619            }
1620            self.params.size_hint = total as usize;
1621        }
1622    }
1623}
1624
1625fn WrapPosition(position: u64) -> u32 {
1626    let mut result: u32 = position as u32;
1627    let gb: u64 = position >> 30;
1628    if gb > 2 {
1629        result = result & (1u32 << 30).wrapping_sub(1)
1630            | ((gb.wrapping_sub(1) & 1) as u32).wrapping_add(1) << 30;
1631    }
1632    result
1633}
1634
1635impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1636    fn get_brotli_storage(&mut self, size: usize) {
1637        if self.storage_size_ < size {
1638            <Alloc as Allocator<u8>>::free_cell(&mut self.m8, core::mem::take(&mut self.storage_));
1639            self.storage_ = allocate::<u8, _>(&mut self.m8, size);
1640            self.storage_size_ = size;
1641        }
1642    }
1643}
1644
1645fn MaxHashTableSize(quality: i32) -> usize {
1646    (if quality == 0i32 {
1647        1i32 << 15
1648    } else {
1649        1i32 << 17
1650    }) as usize
1651}
1652
1653fn HashTableSize(max_table_size: usize, input_size: usize) -> usize {
1654    let mut htsize: usize = 256usize;
1655    while htsize < max_table_size && (htsize < input_size) {
1656        htsize <<= 1i32;
1657    }
1658    htsize
1659}
1660
1661macro_rules! GetHashTable {
1662    ($s : expr_2021, $quality: expr_2021, $input_size : expr_2021, $table_size : expr_2021) => {
1663        GetHashTableInternal(
1664            &mut $s.m8,
1665            &mut $s.small_table_,
1666            &mut $s.large_table_,
1667            $quality,
1668            $input_size,
1669            $table_size,
1670        )
1671    };
1672}
1673fn GetHashTableInternal<'a, AllocI32: alloc::Allocator<i32>>(
1674    mi32: &mut AllocI32,
1675    small_table_: &'a mut [i32; 1024],
1676    large_table_: &'a mut AllocI32::AllocatedMemory,
1677    quality: i32,
1678    input_size: usize,
1679    table_size: &mut usize,
1680) -> &'a mut [i32] {
1681    let max_table_size: usize = MaxHashTableSize(quality);
1682    let mut htsize: usize = HashTableSize(max_table_size, input_size);
1683    let table: &mut [i32];
1684    if quality == 0i32 && htsize & 0xaaaaausize == 0usize {
1685        htsize <<= 1i32;
1686    }
1687    if htsize <= small_table_.len() {
1688        table = &mut small_table_[..];
1689    } else {
1690        if htsize > large_table_.slice().len() {
1691            //s.large_table_size_ = htsize;
1692            {
1693                mi32.free_cell(core::mem::take(large_table_));
1694            }
1695            *large_table_ = mi32.alloc_cell(htsize);
1696        }
1697        table = large_table_.slice_mut();
1698    }
1699    *table_size = htsize;
1700    for item in table[..htsize].iter_mut() {
1701        *item = 0;
1702    }
1703    table // FIXME: probably need a macro to do this without borrowing the whole EncoderStateStruct
1704}
1705
1706impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
1707    fn update_last_processed_pos(&mut self) -> bool {
1708        let wrapped_last_processed_pos: u32 = WrapPosition(self.last_processed_pos_);
1709        let wrapped_input_pos: u32 = WrapPosition(self.input_pos_);
1710        self.last_processed_pos_ = self.input_pos_;
1711        wrapped_input_pos < wrapped_last_processed_pos
1712    }
1713}
1714
1715fn MaxMetablockSize(params: &BrotliEncoderParams) -> usize {
1716    1 << min(ComputeRbBits(params), 24)
1717}
1718
1719#[cfg_attr(feature = "hotpath", hotpath::measure)]
1720fn ChooseContextMap(
1721    quality: i32,
1722    bigram_histo: &mut [u32],
1723    num_literal_contexts: &mut usize,
1724    literal_context_map: &mut &[u32],
1725) {
1726    static kStaticContextMapContinuation: [u32; 64] = [
1727        1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1728        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1729        0, 0, 0, 0,
1730    ];
1731    static kStaticContextMapSimpleUTF8: [u32; 64] = [
1732        0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1733        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1734        0, 0, 0, 0,
1735    ];
1736    let mut monogram_histo = [0u32; 3];
1737    let mut two_prefix_histo = [0u32; 6];
1738
1739    let mut i: usize;
1740    let mut entropy = [0.0 as floatX; 4];
1741    i = 0usize;
1742    while i < 9usize {
1743        {
1744            {
1745                let _rhs = bigram_histo[i];
1746                let _lhs = &mut monogram_histo[i.wrapping_rem(3)];
1747                *_lhs = (*_lhs).wrapping_add(_rhs);
1748            }
1749            {
1750                let _rhs = bigram_histo[i];
1751                let _lhs = &mut two_prefix_histo[i.wrapping_rem(6)];
1752                *_lhs = (*_lhs).wrapping_add(_rhs);
1753            }
1754        }
1755        i = i.wrapping_add(1);
1756    }
1757    entropy[1] = shannon_entropy(&monogram_histo[..], 3).0;
1758    entropy[2] =
1759        shannon_entropy(&two_prefix_histo[..], 3).0 + shannon_entropy(&two_prefix_histo[3..], 3).0;
1760    entropy[3] = 0.0;
1761    for i in 0usize..3usize {
1762        entropy[3] += shannon_entropy(&bigram_histo[(3usize).wrapping_mul(i)..], 3).0;
1763    }
1764    let total: usize = monogram_histo[0]
1765        .wrapping_add(monogram_histo[1])
1766        .wrapping_add(monogram_histo[2]) as usize;
1767    entropy[0] = 1.0 / (total as floatX);
1768    entropy[1] *= entropy[0];
1769    entropy[2] *= entropy[0];
1770    entropy[3] *= entropy[0];
1771    if quality < 7i32 {
1772        entropy[3] = entropy[1] * 10.0;
1773    }
1774    if entropy[1] - entropy[2] < 0.2 && entropy[1] - entropy[3] < 0.2 {
1775        *num_literal_contexts = 1;
1776    } else if entropy[2] - entropy[3] < 0.02 {
1777        *num_literal_contexts = 2usize;
1778        *literal_context_map = &kStaticContextMapSimpleUTF8[..];
1779    } else {
1780        *num_literal_contexts = 3usize;
1781        *literal_context_map = &kStaticContextMapContinuation[..];
1782    }
1783}
1784
1785static kStaticContextMapComplexUTF8: [u32; 64] = [
1786    11, 11, 12, 12, /* 0 special */
1787    0, 0, 0, 0, /* 4 lf */
1788    1, 1, 9, 9, /* 8 space */
1789    2, 2, 2, 2, /* !, first after space/lf and after something else. */
1790    1, 1, 1, 1, /* " */
1791    8, 3, 3, 3, /* % */
1792    1, 1, 1, 1, /* ({[ */
1793    2, 2, 2, 2, /* }]) */
1794    8, 4, 4, 4, /* :; */
1795    8, 7, 4, 4, /* . */
1796    8, 0, 0, 0, /* > */
1797    3, 3, 3, 3, /* [0..9] */
1798    5, 5, 10, 5, /* [A-Z] */
1799    5, 5, 10, 5, 6, 6, 6, 6, /* [a-z] */
1800    6, 6, 6, 6,
1801];
1802/* Decide if we want to use a more complex static context map containing 13
1803context values, based on the entropy reduction of histograms over the
1804first 5 bits of literals. */
1805fn ShouldUseComplexStaticContextMap(
1806    input: &[u8],
1807    mut start_pos: usize,
1808    length: usize,
1809    mask: usize,
1810    quality: i32,
1811    size_hint: usize,
1812    num_literal_contexts: &mut usize,
1813    literal_context_map: &mut &[u32],
1814) -> bool {
1815    let _ = quality;
1816    //BROTLI_UNUSED(quality);
1817    /* Try the more complex static context map only for long data. */
1818    if (size_hint < (1 << 20)) {
1819        false
1820    } else {
1821        let end_pos = start_pos + length;
1822        /* To make entropy calculations faster and to fit on the stack, we collect
1823        histograms over the 5 most significant bits of literals. One histogram
1824        without context and 13 additional histograms for each context value. */
1825        let mut combined_histo: [u32; 32] = [0; 32];
1826        let mut context_histo: [[u32; 32]; 13] = [[0; 32]; 13];
1827        let mut total = 0u32;
1828        let mut entropy = [0.0 as floatX; 3];
1829        let utf8_lut = BROTLI_CONTEXT_LUT(ContextType::CONTEXT_UTF8);
1830        while start_pos + 64 <= end_pos {
1831            let stride_end_pos = start_pos + 64;
1832            let mut prev2 = input[start_pos & mask];
1833            let mut prev1 = input[(start_pos + 1) & mask];
1834
1835            /* To make the analysis of the data faster we only examine 64 byte long
1836            strides at every 4kB intervals. */
1837            for pos in start_pos + 2..stride_end_pos {
1838                let literal = input[pos & mask];
1839                let context = kStaticContextMapComplexUTF8
1840                    [BROTLI_CONTEXT(prev1, prev2, utf8_lut) as usize]
1841                    as u8;
1842                total += 1;
1843                combined_histo[(literal >> 3) as usize] += 1;
1844                context_histo[context as usize][(literal >> 3) as usize] += 1;
1845                prev2 = prev1;
1846                prev1 = literal;
1847            }
1848            start_pos += 4096;
1849        }
1850        entropy[1] = shannon_entropy(&combined_histo[..], 32).0;
1851        entropy[2] = 0.0;
1852        for i in 0..13 {
1853            assert!(i < 13);
1854            entropy[2] += shannon_entropy(&context_histo[i][..], 32).0;
1855        }
1856        entropy[0] = 1.0 / (total as floatX);
1857        entropy[1] *= entropy[0];
1858        entropy[2] *= entropy[0];
1859        /* The triggering heuristics below were tuned by compressing the individual
1860        files of the silesia corpus. If we skip this kind of context modeling
1861        for not very well compressible input (i.e. entropy using context modeling
1862        is 60% of maximal entropy) or if expected savings by symbol are less
1863        than 0.2 bits, then in every case when it triggers, the final compression
1864        ratio is improved. Note however that this heuristics might be too strict
1865        for some cases and could be tuned further. */
1866        if (entropy[2] > 3.0 || entropy[1] - entropy[2] < 0.2) {
1867            false
1868        } else {
1869            *num_literal_contexts = 13;
1870            *literal_context_map = &kStaticContextMapComplexUTF8;
1871            true
1872        }
1873    }
1874}
1875
1876#[cfg_attr(feature = "hotpath", hotpath::measure)]
1877fn DecideOverLiteralContextModeling(
1878    input: &[u8],
1879    mut start_pos: usize,
1880    length: usize,
1881    mask: usize,
1882    quality: i32,
1883    size_hint: usize,
1884    num_literal_contexts: &mut usize,
1885    literal_context_map: &mut &[u32],
1886) {
1887    if quality < 5i32 || length < 64usize {
1888    } else if ShouldUseComplexStaticContextMap(
1889        input,
1890        start_pos,
1891        length,
1892        mask,
1893        quality,
1894        size_hint,
1895        num_literal_contexts,
1896        literal_context_map,
1897    ) {
1898    } else {
1899        let end_pos: usize = start_pos.wrapping_add(length);
1900        let mut bigram_prefix_histo = [0u32; 9];
1901        while start_pos.wrapping_add(64) <= end_pos {
1902            {
1903                static lut: [i32; 4] = [0, 0, 1, 2];
1904                let stride_end_pos: usize = start_pos.wrapping_add(64);
1905                let mut prev: i32 = lut[(input[(start_pos & mask)] as i32 >> 6) as usize] * 3i32;
1906                let mut pos: usize;
1907                pos = start_pos.wrapping_add(1);
1908                while pos < stride_end_pos {
1909                    {
1910                        let literal: u8 = input[(pos & mask)];
1911                        {
1912                            let _rhs = 1;
1913                            let cur_ind = (prev + lut[(literal as i32 >> 6) as usize]);
1914                            let _lhs = &mut bigram_prefix_histo[cur_ind as usize];
1915                            *_lhs = (*_lhs).wrapping_add(_rhs as u32);
1916                        }
1917                        prev = lut[(literal as i32 >> 6) as usize] * 3i32;
1918                    }
1919                    pos = pos.wrapping_add(1);
1920                }
1921            }
1922            start_pos = start_pos.wrapping_add(4096);
1923        }
1924        ChooseContextMap(
1925            quality,
1926            &mut bigram_prefix_histo[..],
1927            num_literal_contexts,
1928            literal_context_map,
1929        );
1930    }
1931}
1932fn WriteEmptyLastBlocksInternal(
1933    params: &BrotliEncoderParams,
1934    storage_ix: &mut usize,
1935    storage: &mut [u8],
1936) {
1937    // insert empty block for byte alignment if required
1938    if params.byte_align {
1939        BrotliWritePaddingMetaBlock(storage_ix, storage);
1940    }
1941    if !params.bare_stream {
1942        BrotliWriteEmptyLastMetaBlock(storage_ix, storage)
1943    }
1944}
1945#[cfg_attr(feature = "hotpath", hotpath::measure)]
1946fn WriteMetaBlockInternal<Alloc: BrotliAlloc, Cb>(
1947    alloc: &mut Alloc,
1948    data: &[u8],
1949    mask: usize,
1950    last_flush_pos: u64,
1951    bytes: usize,
1952    mut is_last: bool,
1953    literal_context_mode: ContextType,
1954    params: &BrotliEncoderParams,
1955    lit_scratch_space: &mut <HistogramLiteral as CostAccessors>::i32vec,
1956    cmd_scratch_space: &mut <HistogramCommand as CostAccessors>::i32vec,
1957    dst_scratch_space: &mut <HistogramDistance as CostAccessors>::i32vec,
1958    prev_byte: u8,
1959    prev_byte2: u8,
1960    num_literals: usize,
1961    num_commands: usize,
1962    commands: &mut [Command],
1963    saved_dist_cache: &[i32; kNumDistanceCacheEntries],
1964    dist_cache: &mut [i32; 16],
1965    recoder_state: &mut RecoderState,
1966    storage_ix: &mut usize,
1967    storage: &mut [u8],
1968    cb: &mut Cb,
1969) where
1970    Cb: FnMut(
1971        &mut interface::PredictionModeContextMap<InputReferenceMut>,
1972        &mut [interface::StaticCommand],
1973        interface::InputPair,
1974        &mut Alloc,
1975    ),
1976{
1977    let actual_is_last = is_last;
1978    if params.appendable || params.byte_align {
1979        is_last = false;
1980    } else {
1981        assert!(!params.catable); // Sanitize Params senforces this constraint
1982    }
1983    let wrapped_last_flush_pos: u32 = WrapPosition(last_flush_pos);
1984
1985    let literal_context_lut = BROTLI_CONTEXT_LUT(literal_context_mode);
1986    let mut block_params = params.clone();
1987    if bytes == 0usize {
1988        WriteEmptyLastBlocksInternal(params, storage_ix, storage);
1989        return;
1990    }
1991    if !should_compress(
1992        data,
1993        mask,
1994        last_flush_pos,
1995        bytes,
1996        num_literals,
1997        num_commands,
1998    ) {
1999        dist_cache[..4].clone_from_slice(&saved_dist_cache[..4]);
2000        store_uncompressed_meta_block(
2001            alloc,
2002            is_last,
2003            data,
2004            wrapped_last_flush_pos as usize,
2005            mask,
2006            params,
2007            bytes,
2008            recoder_state,
2009            storage_ix,
2010            storage,
2011            false,
2012            cb,
2013        );
2014        if actual_is_last != is_last {
2015            WriteEmptyLastBlocksInternal(params, storage_ix, storage);
2016        }
2017        return;
2018    }
2019    let saved_byte_location = (*storage_ix) >> 3;
2020    let last_bytes: u16 =
2021        ((storage[saved_byte_location + 1] as u16) << 8) | storage[saved_byte_location] as u16;
2022    let last_bytes_bits: u8 = *storage_ix as u8;
2023    /*if params.dist.num_direct_distance_codes != 0 ||
2024                      params.dist.distance_postfix_bits != 0 {
2025      RecomputeDistancePrefixes(commands,
2026                                num_commands,
2027                                params.dist.num_direct_distance_codes,
2028                                params.dist.distance_postfix_bits);
2029    }*/
2030    // why was this removed??
2031    if params.quality <= 2 {
2032        store_meta_block_fast(
2033            alloc,
2034            data,
2035            wrapped_last_flush_pos as usize,
2036            bytes,
2037            mask,
2038            is_last,
2039            params,
2040            saved_dist_cache,
2041            commands,
2042            num_commands,
2043            recoder_state,
2044            storage_ix,
2045            storage,
2046            cb,
2047        );
2048    } else if params.quality < 4 {
2049        store_meta_block_trivial(
2050            alloc,
2051            data,
2052            wrapped_last_flush_pos as usize,
2053            bytes,
2054            mask,
2055            is_last,
2056            params,
2057            saved_dist_cache,
2058            commands,
2059            num_commands,
2060            recoder_state,
2061            storage_ix,
2062            storage,
2063            cb,
2064        );
2065    } else {
2066        //let mut literal_context_mode: ContextType = ContextType::CONTEXT_UTF8;
2067
2068        let mut mb = MetaBlockSplit::<Alloc>::new();
2069        if params.quality < 10i32 {
2070            let mut num_literal_contexts: usize = 1;
2071            let mut literal_context_map: &[u32] = &[];
2072            if params.disable_literal_context_modeling == 0 {
2073                DecideOverLiteralContextModeling(
2074                    data,
2075                    wrapped_last_flush_pos as usize,
2076                    bytes,
2077                    mask,
2078                    params.quality,
2079                    params.size_hint,
2080                    &mut num_literal_contexts,
2081                    &mut literal_context_map,
2082                );
2083            }
2084            BrotliBuildMetaBlockGreedy(
2085                alloc,
2086                data,
2087                wrapped_last_flush_pos as usize,
2088                mask,
2089                prev_byte,
2090                prev_byte2,
2091                literal_context_mode,
2092                literal_context_lut,
2093                num_literal_contexts,
2094                literal_context_map,
2095                commands,
2096                num_commands,
2097                &mut mb,
2098            );
2099        } else {
2100            BrotliBuildMetaBlock(
2101                alloc,
2102                data,
2103                wrapped_last_flush_pos as usize,
2104                mask,
2105                &mut block_params,
2106                prev_byte,
2107                prev_byte2,
2108                commands,
2109                num_commands,
2110                literal_context_mode,
2111                lit_scratch_space,
2112                cmd_scratch_space,
2113                dst_scratch_space,
2114                &mut mb,
2115            );
2116        }
2117        if params.quality >= 4i32 {
2118            let mut num_effective_dist_codes = block_params.dist.alphabet_size;
2119            if num_effective_dist_codes > BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS as u32 {
2120                num_effective_dist_codes = BROTLI_NUM_HISTOGRAM_DISTANCE_SYMBOLS as u32;
2121            }
2122            BrotliOptimizeHistograms(num_effective_dist_codes as usize, &mut mb);
2123        }
2124        store_meta_block(
2125            alloc,
2126            data,
2127            wrapped_last_flush_pos as usize,
2128            bytes,
2129            mask,
2130            prev_byte,
2131            prev_byte2,
2132            is_last,
2133            &block_params,
2134            literal_context_mode,
2135            saved_dist_cache,
2136            commands,
2137            num_commands,
2138            &mut mb,
2139            recoder_state,
2140            storage_ix,
2141            storage,
2142            cb,
2143        );
2144        mb.destroy(alloc);
2145    }
2146    if bytes + 4 + saved_byte_location < (*storage_ix >> 3) {
2147        dist_cache[..4].clone_from_slice(&saved_dist_cache[..4]);
2148        //memcpy(dist_cache,
2149        //     saved_dist_cache,
2150        //     (4usize).wrapping_mul(::core::mem::size_of::<i32>()));
2151        storage[saved_byte_location] = last_bytes as u8;
2152        storage[saved_byte_location + 1] = (last_bytes >> 8) as u8;
2153        *storage_ix = last_bytes_bits as usize;
2154        store_uncompressed_meta_block(
2155            alloc,
2156            is_last,
2157            data,
2158            wrapped_last_flush_pos as usize,
2159            mask,
2160            params,
2161            bytes,
2162            recoder_state,
2163            storage_ix,
2164            storage,
2165            true,
2166            cb,
2167        );
2168    }
2169    if actual_is_last != is_last {
2170        WriteEmptyLastBlocksInternal(params, storage_ix, storage);
2171    }
2172}
2173
2174fn ChooseDistanceParams(params: &mut BrotliEncoderParams) {
2175    let mut num_direct_distance_codes = 0u32;
2176    let mut distance_postfix_bits = 0u32;
2177
2178    if params.quality >= 4 {
2179        if params.mode == BrotliEncoderMode::BROTLI_MODE_FONT {
2180            distance_postfix_bits = 1;
2181            num_direct_distance_codes = 12;
2182        } else {
2183            distance_postfix_bits = params.dist.distance_postfix_bits;
2184            num_direct_distance_codes = params.dist.num_direct_distance_codes;
2185        }
2186        let ndirect_msb = (num_direct_distance_codes >> distance_postfix_bits) & 0x0f;
2187        if distance_postfix_bits > BROTLI_MAX_NPOSTFIX as u32
2188            || num_direct_distance_codes > BROTLI_MAX_NDIRECT as u32
2189            || (ndirect_msb << distance_postfix_bits) != num_direct_distance_codes
2190        {
2191            distance_postfix_bits = 0;
2192            num_direct_distance_codes = 0;
2193        }
2194    }
2195    BrotliInitDistanceParams(params, distance_postfix_bits, num_direct_distance_codes);
2196    /*(
2197    if (params.large_window) {
2198        max_distance = BROTLI_MAX_ALLOWED_DISTANCE;
2199        if (num_direct_distance_codes != 0 || distance_postfix_bits != 0) {
2200            max_distance = (3 << 29) - 4;
2201        }
2202        alphabet_size = BROTLI_DISTANCE_ALPHABET_SIZE(
2203            num_direct_distance_codes, distance_postfix_bits,
2204            BROTLI_LARGE_MAX_DISTANCE_BITS);
2205    } else {
2206        alphabet_size = BROTLI_DISTANCE_ALPHABET_SIZE(
2207            num_direct_distance_codes, distance_postfix_bits,
2208            BROTLI_MAX_DISTANCE_BITS);
2209
2210    }
2211
2212    params.dist.num_direct_distance_codes = num_direct_distance_codes;
2213    params.dist.distance_postfix_bits = distance_postfix_bits;
2214    params.dist.alphabet_size = alphabet_size;
2215    params.dist.max_distance = max_distance;*/
2216}
2217
2218impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2219    #[cfg_attr(feature = "hotpath", hotpath::measure)]
2220    fn encode_data<MetablockCallback>(
2221        &mut self,
2222        is_last: bool,
2223        force_flush: bool,
2224        out_size: &mut usize,
2225        callback: &mut MetablockCallback,
2226        // mut output: &'a mut &'a mut [u8]
2227    ) -> bool
2228    where
2229        MetablockCallback: FnMut(
2230            &mut interface::PredictionModeContextMap<InputReferenceMut>,
2231            &mut [interface::StaticCommand],
2232            interface::InputPair,
2233            &mut Alloc,
2234        ),
2235    {
2236        let mut delta: u64 = self.unprocessed_input_size();
2237        let mut bytes: u32 = delta as u32;
2238        let mask = self.ringbuffer_.mask_;
2239        if !self.ensure_initialized() {
2240            return false;
2241        }
2242        let dictionary = BrotliGetDictionary();
2243        if self.is_last_block_emitted_ {
2244            return false;
2245        }
2246        if is_last {
2247            self.is_last_block_emitted_ = true;
2248        }
2249        if delta > self.input_block_size() as u64 {
2250            return false;
2251        }
2252        let mut storage_ix: usize = usize::from(self.last_bytes_bits_);
2253        {
2254            let meta_size = max(
2255                bytes as usize,
2256                self.input_pos_.wrapping_sub(self.last_flush_pos_) as usize,
2257            );
2258            self.get_brotli_storage((2usize).wrapping_mul(meta_size).wrapping_add(503 + 24));
2259        }
2260        {
2261            self.storage_.slice_mut()[0] = self.last_bytes_ as u8;
2262            self.storage_.slice_mut()[1] = (self.last_bytes_ >> 8) as u8;
2263        }
2264        let mut catable_header_size = 0;
2265        if let IsFirst::NothingWritten = self.is_first_mb {
2266            if self.params.magic_number {
2267                BrotliWriteMetadataMetaBlock(
2268                    &self.params,
2269                    &mut storage_ix,
2270                    self.storage_.slice_mut(),
2271                );
2272                self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2273                    | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2274                self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2275                self.next_out_ = NextOut::DynamicStorage(0);
2276                catable_header_size = storage_ix >> 3;
2277                *out_size = catable_header_size;
2278                self.is_first_mb = IsFirst::HeaderWritten;
2279            }
2280            // fixup for empty stream - note: catable is always appendable
2281            if bytes == 0
2282                && self.params.byte_align
2283                && self.params.appendable
2284                && !self.params.catable
2285            {
2286                BrotliWritePaddingMetaBlock(&mut storage_ix, self.storage_.slice_mut());
2287            }
2288        }
2289        if let IsFirst::BothCatableBytesWritten = self.is_first_mb {
2290            // nothing to do here, move along
2291        } else if !self.params.catable {
2292            self.is_first_mb = IsFirst::BothCatableBytesWritten;
2293        } else if bytes != 0 {
2294            assert!(self.last_processed_pos_ < 2 || self.custom_dictionary);
2295            let num_bytes_to_write_uncompressed: usize = min(2, bytes as usize);
2296            {
2297                let data =
2298                    &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..];
2299                store_uncompressed_meta_block(
2300                    &mut self.m8,
2301                    false,
2302                    data,
2303                    self.last_flush_pos_ as usize,
2304                    mask as usize,
2305                    &self.params,
2306                    num_bytes_to_write_uncompressed,
2307                    &mut self.recoder_state,
2308                    &mut storage_ix,
2309                    self.storage_.slice_mut(),
2310                    false, /* suppress meta-block logging */
2311                    callback,
2312                );
2313                self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2314                    | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2315                self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2316                self.prev_byte2_ = self.prev_byte_;
2317                self.prev_byte_ = data[self.last_flush_pos_ as usize & mask as usize];
2318                if num_bytes_to_write_uncompressed == 2 {
2319                    self.prev_byte2_ = self.prev_byte_;
2320                    self.prev_byte_ = data[(self.last_flush_pos_ + 1) as usize & mask as usize];
2321                }
2322            }
2323            self.last_flush_pos_ += num_bytes_to_write_uncompressed as u64;
2324            bytes -= num_bytes_to_write_uncompressed as u32;
2325            self.last_processed_pos_ += num_bytes_to_write_uncompressed as u64;
2326            if num_bytes_to_write_uncompressed >= 2 {
2327                self.is_first_mb = IsFirst::BothCatableBytesWritten;
2328            } else if num_bytes_to_write_uncompressed == 1 {
2329                if let IsFirst::FirstCatableByteWritten = self.is_first_mb {
2330                    self.is_first_mb = IsFirst::BothCatableBytesWritten;
2331                } else {
2332                    self.is_first_mb = IsFirst::FirstCatableByteWritten;
2333                }
2334            }
2335            catable_header_size = storage_ix >> 3;
2336            self.next_out_ = NextOut::DynamicStorage(0);
2337            *out_size = catable_header_size;
2338            delta = self.unprocessed_input_size();
2339        }
2340        let mut wrapped_last_processed_pos: u32 = WrapPosition(self.last_processed_pos_);
2341        if self.params.quality == 1i32 && self.command_buf_.slice().is_empty() {
2342            let new_buf = allocate::<u32, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2343            self.command_buf_ = new_buf;
2344            let new_buf8 = allocate::<u8, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2345            self.literal_buf_ = new_buf8;
2346        }
2347
2348        if self.params.quality == 0i32 || self.params.quality == 1i32 {
2349            let mut table_size: usize = 0;
2350            {
2351                if delta == 0 && !is_last {
2352                    *out_size = catable_header_size;
2353                    return true;
2354                }
2355                let data =
2356                    &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..];
2357
2358                //s.storage_.slice_mut()[0] = (*s).last_bytes_ as u8;
2359                //        s.storage_.slice_mut()[1] = ((*s).last_bytes_ >> 8) as u8;
2360
2361                let table: &mut [i32] =
2362                    GetHashTable!(self, self.params.quality, bytes as usize, &mut table_size);
2363
2364                if self.params.quality == 0i32 {
2365                    compress_fragment_fast(
2366                        &mut self.m8,
2367                        &mut data[((wrapped_last_processed_pos & mask) as usize)..],
2368                        bytes as usize,
2369                        is_last,
2370                        table,
2371                        table_size,
2372                        &mut self.cmd_depths_[..],
2373                        &mut self.cmd_bits_[..],
2374                        &mut self.cmd_code_numbits_,
2375                        &mut self.cmd_code_[..],
2376                        &mut storage_ix,
2377                        self.storage_.slice_mut(),
2378                    );
2379                } else {
2380                    compress_fragment_two_pass(
2381                        &mut self.m8,
2382                        &mut data[((wrapped_last_processed_pos & mask) as usize)..],
2383                        bytes as usize,
2384                        is_last,
2385                        self.command_buf_.slice_mut(),
2386                        self.literal_buf_.slice_mut(),
2387                        table,
2388                        table_size,
2389                        &mut storage_ix,
2390                        self.storage_.slice_mut(),
2391                    );
2392                }
2393                self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2394                    | ((self.storage_.slice()[(storage_ix >> 3) + 1] as u16) << 8);
2395                self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2396            }
2397            self.update_last_processed_pos();
2398            // *output = &mut s.storage_.slice_mut();
2399            self.next_out_ = NextOut::DynamicStorage(0); // this always returns that
2400            *out_size = storage_ix >> 3;
2401            return true;
2402        }
2403        {
2404            let mut newsize: usize = self
2405                .num_commands_
2406                .wrapping_add(bytes.wrapping_div(2) as usize)
2407                .wrapping_add(1);
2408            if newsize > self.cmd_alloc_size_ {
2409                newsize = newsize.wrapping_add(bytes.wrapping_div(4).wrapping_add(16) as usize);
2410                self.cmd_alloc_size_ = newsize;
2411                let mut new_commands = allocate::<Command, _>(&mut self.m8, newsize);
2412                if !self.commands_.slice().is_empty() {
2413                    new_commands.slice_mut()[..self.num_commands_]
2414                        .clone_from_slice(&self.commands_.slice()[..self.num_commands_]);
2415                    <Alloc as Allocator<Command>>::free_cell(
2416                        &mut self.m8,
2417                        core::mem::take(&mut self.commands_),
2418                    );
2419                }
2420                self.commands_ = new_commands;
2421            }
2422        }
2423        InitOrStitchToPreviousBlock(
2424            &mut self.m8,
2425            &mut self.hasher_,
2426            &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2427            mask as usize,
2428            self.custom_dictionary_size,
2429            &mut self.params,
2430            wrapped_last_processed_pos as usize,
2431            bytes as usize,
2432            is_last,
2433        );
2434        let literal_context_mode = ChooseContextMode(
2435            &self.params,
2436            self.ringbuffer_.data_mo.slice(),
2437            WrapPosition(self.last_flush_pos_) as usize,
2438            mask as usize,
2439            (self.input_pos_.wrapping_sub(self.last_flush_pos_)) as usize,
2440        );
2441        if self.num_commands_ != 0 && self.last_insert_len_ == 0 {
2442            self.extend_last_command(&mut bytes, &mut wrapped_last_processed_pos);
2443        }
2444        BrotliCreateBackwardReferences(
2445            &mut self.m8,
2446            dictionary,
2447            bytes as usize,
2448            wrapped_last_processed_pos as usize,
2449            &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2450            mask as usize,
2451            self.custom_dictionary_size,
2452            &mut self.params,
2453            &mut self.hasher_,
2454            &mut self.dist_cache_,
2455            &mut self.last_insert_len_,
2456            &mut self.commands_.slice_mut()[self.num_commands_..],
2457            &mut self.num_commands_,
2458            &mut self.num_literals_,
2459        );
2460        {
2461            let max_length: usize = MaxMetablockSize(&mut self.params);
2462            let max_literals: usize = max_length.wrapping_div(8);
2463            let max_commands: usize = max_length.wrapping_div(8);
2464            let processed_bytes: usize =
2465                self.input_pos_.wrapping_sub(self.last_flush_pos_) as usize;
2466            let next_input_fits_metablock =
2467                processed_bytes.wrapping_add(self.input_block_size()) <= max_length;
2468            let should_flush = self.params.quality < 4
2469                && self.num_literals_.wrapping_add(self.num_commands_) >= 0x2fff;
2470            if !is_last
2471                && !force_flush
2472                && !should_flush
2473                && next_input_fits_metablock
2474                && self.num_literals_ < max_literals
2475                && self.num_commands_ < max_commands
2476            {
2477                if self.update_last_processed_pos() {
2478                    HasherReset(&mut self.hasher_);
2479                }
2480                *out_size = catable_header_size;
2481                return true;
2482            }
2483        }
2484        if self.last_insert_len_ > 0usize {
2485            self.commands_.slice_mut()[self.num_commands_].init_insert(self.last_insert_len_);
2486            self.num_commands_ = self.num_commands_.wrapping_add(1);
2487            self.num_literals_ = self.num_literals_.wrapping_add(self.last_insert_len_);
2488            self.last_insert_len_ = 0usize;
2489        }
2490        if !is_last && self.input_pos_ == self.last_flush_pos_ {
2491            *out_size = catable_header_size;
2492            return true;
2493        }
2494        {
2495            let metablock_size: u32 = self.input_pos_.wrapping_sub(self.last_flush_pos_) as u32;
2496            //let mut storage_ix: usize = s.last_bytes_bits_ as usize;
2497            //s.storage_.slice_mut()[0] = (*s).last_bytes_ as u8;
2498            //s.storage_.slice_mut()[1] = ((*s).last_bytes_ >> 8) as u8;
2499
2500            WriteMetaBlockInternal(
2501                &mut self.m8,
2502                &mut self.ringbuffer_.data_mo.slice_mut()[self.ringbuffer_.buffer_index..],
2503                mask as usize,
2504                self.last_flush_pos_,
2505                metablock_size as usize,
2506                is_last,
2507                literal_context_mode,
2508                &mut self.params,
2509                &mut self.literal_scratch_space,
2510                &mut self.command_scratch_space,
2511                &mut self.distance_scratch_space,
2512                self.prev_byte_,
2513                self.prev_byte2_,
2514                self.num_literals_,
2515                self.num_commands_,
2516                self.commands_.slice_mut(),
2517                &mut self.saved_dist_cache_,
2518                &mut self.dist_cache_,
2519                &mut self.recoder_state,
2520                &mut storage_ix,
2521                self.storage_.slice_mut(),
2522                callback,
2523            );
2524
2525            self.last_bytes_ = self.storage_.slice()[(storage_ix >> 3)] as u16
2526                | ((self.storage_.slice()[1 + (storage_ix >> 3)] as u16) << 8);
2527            self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2528            self.last_flush_pos_ = self.input_pos_;
2529            if self.update_last_processed_pos() {
2530                HasherReset(&mut self.hasher_);
2531            }
2532            let data = &self.ringbuffer_.data_mo.slice()[self.ringbuffer_.buffer_index..];
2533            if self.last_flush_pos_ > 0 {
2534                self.prev_byte_ =
2535                    data[(((self.last_flush_pos_ as u32).wrapping_sub(1) & mask) as usize)];
2536            }
2537            if self.last_flush_pos_ > 1 {
2538                self.prev_byte2_ =
2539                    data[((self.last_flush_pos_.wrapping_sub(2) as u32 & mask) as usize)];
2540            }
2541            self.num_commands_ = 0usize;
2542            self.num_literals_ = 0usize;
2543            self.saved_dist_cache_
2544                .clone_from_slice(self.dist_cache_.split_at(4).0);
2545            self.next_out_ = NextOut::DynamicStorage(0); // this always returns that
2546            *out_size = storage_ix >> 3;
2547            true
2548        }
2549    }
2550
2551    fn write_metadata_header(&mut self) -> usize {
2552        let block_size = self.remaining_metadata_bytes_ as usize;
2553        let header = GetNextOut!(*self);
2554        let mut storage_ix: usize;
2555        storage_ix = self.last_bytes_bits_ as usize;
2556        header[0] = self.last_bytes_ as u8;
2557        header[1] = (self.last_bytes_ >> 8) as u8;
2558        self.last_bytes_ = 0;
2559        self.last_bytes_bits_ = 0;
2560        BrotliWriteBits(1, 0, &mut storage_ix, header);
2561        BrotliWriteBits(2usize, 3, &mut storage_ix, header);
2562        BrotliWriteBits(1, 0, &mut storage_ix, header);
2563        if block_size == 0usize {
2564            BrotliWriteBits(2usize, 0, &mut storage_ix, header);
2565        } else {
2566            let nbits: u32 = if block_size == 1 {
2567                0u32
2568            } else {
2569                Log2FloorNonZero((block_size as u32).wrapping_sub(1) as (u64)).wrapping_add(1)
2570            };
2571            let nbytes: u32 = nbits.wrapping_add(7).wrapping_div(8);
2572            BrotliWriteBits(2usize, nbytes as (u64), &mut storage_ix, header);
2573            BrotliWriteBits(
2574                (8u32).wrapping_mul(nbytes) as usize,
2575                block_size.wrapping_sub(1) as u64,
2576                &mut storage_ix,
2577                header,
2578            );
2579        }
2580        storage_ix.wrapping_add(7u32 as usize) >> 3
2581    }
2582}
2583
2584impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2585    fn process_metadata<
2586        MetaBlockCallback: FnMut(
2587            &mut interface::PredictionModeContextMap<InputReferenceMut>,
2588            &mut [interface::StaticCommand],
2589            interface::InputPair,
2590            &mut Alloc,
2591        ),
2592    >(
2593        &mut self,
2594        available_in: &mut usize,
2595        next_in_array: &[u8],
2596        next_in_offset: &mut usize,
2597        available_out: &mut usize,
2598        next_out_array: &mut [u8],
2599        next_out_offset: &mut usize,
2600        total_out: &mut Option<usize>,
2601        metablock_callback: &mut MetaBlockCallback,
2602    ) -> bool {
2603        if *available_in > (1u32 << 24) as usize {
2604            return false;
2605        }
2606        if self.stream_state_ as i32 == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32 {
2607            self.remaining_metadata_bytes_ = *available_in as u32;
2608            self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD;
2609        }
2610        if self.stream_state_ as i32 != BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2611            && (self.stream_state_ as i32
2612                != BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY as i32)
2613        {
2614            return false;
2615        }
2616        loop {
2617            if self.inject_flush_or_push_output(
2618                available_out,
2619                next_out_array,
2620                next_out_offset,
2621                total_out,
2622            ) {
2623                continue;
2624            }
2625            if self.available_out_ != 0usize {
2626                break;
2627            }
2628            if self.input_pos_ != self.last_flush_pos_ {
2629                let mut avail_out: usize = self.available_out_;
2630                let result = self.encode_data(false, true, &mut avail_out, metablock_callback);
2631                self.available_out_ = avail_out;
2632                if !result {
2633                    return false;
2634                }
2635                continue;
2636            }
2637            if self.stream_state_ as i32
2638                == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2639            {
2640                self.next_out_ = NextOut::TinyBuf(0);
2641                self.available_out_ = self.write_metadata_header();
2642                self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY;
2643                {
2644                    continue;
2645                }
2646            } else {
2647                if self.remaining_metadata_bytes_ == 0u32 {
2648                    self.remaining_metadata_bytes_ = u32::MAX;
2649                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING;
2650                    {
2651                        break;
2652                    }
2653                }
2654                if *available_out != 0 {
2655                    let copy: u32 =
2656                        min(self.remaining_metadata_bytes_ as usize, *available_out) as u32;
2657                    next_out_array[*next_out_offset..(*next_out_offset + copy as usize)]
2658                        .clone_from_slice(
2659                            &next_in_array[*next_in_offset..(*next_in_offset + copy as usize)],
2660                        );
2661                    //memcpy(*next_out, *next_in, copy as usize);
2662                    // *next_in = next_in.offset(copy as isize);
2663                    *next_in_offset += copy as usize;
2664                    *available_in = available_in.wrapping_sub(copy as usize);
2665                    self.remaining_metadata_bytes_ =
2666                        self.remaining_metadata_bytes_.wrapping_sub(copy);
2667                    *next_out_offset += copy as usize;
2668                    // *next_out = next_out.offset(copy as isize);
2669                    *available_out = available_out.wrapping_sub(copy as usize);
2670                } else {
2671                    let copy: u32 = min(self.remaining_metadata_bytes_, 16u32);
2672                    self.next_out_ = NextOut::TinyBuf(0);
2673                    GetNextOut!(self)[..(copy as usize)].clone_from_slice(
2674                        &next_in_array[*next_in_offset..(*next_in_offset + copy as usize)],
2675                    );
2676                    //memcpy(s.next_out_, *next_in, copy as usize);
2677                    // *next_in = next_in.offset(copy as isize);
2678                    *next_in_offset += copy as usize;
2679                    *available_in = available_in.wrapping_sub(copy as usize);
2680                    self.remaining_metadata_bytes_ =
2681                        self.remaining_metadata_bytes_.wrapping_sub(copy);
2682                    self.available_out_ = copy as usize;
2683                }
2684                {
2685                    continue;
2686                }
2687            }
2688        }
2689        true
2690    }
2691}
2692fn CheckFlushCompleteInner(
2693    stream_state: &mut BrotliEncoderStreamState,
2694    available_out: usize,
2695    next_out: &mut NextOut,
2696) {
2697    if *stream_state == BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED
2698        && (available_out == 0)
2699    {
2700        *stream_state = BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING;
2701        *next_out = NextOut::None;
2702    }
2703}
2704
2705impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
2706    fn check_flush_complete(&mut self) {
2707        CheckFlushCompleteInner(
2708            &mut self.stream_state_,
2709            self.available_out_,
2710            &mut self.next_out_,
2711        );
2712    }
2713
2714    #[cfg_attr(feature = "hotpath", hotpath::measure)]
2715    fn compress_stream_fast(
2716        &mut self,
2717        op: BrotliEncoderOperation,
2718        available_in: &mut usize,
2719        next_in_array: &[u8],
2720        next_in_offset: &mut usize,
2721        available_out: &mut usize,
2722        next_out_array: &mut [u8],
2723        next_out_offset: &mut usize,
2724        total_out: &mut Option<usize>,
2725    ) -> bool {
2726        let block_size_limit: usize = 1 << self.params.lgwin;
2727        let buf_size: usize = min(
2728            kCompressFragmentTwoPassBlockSize,
2729            min(*available_in, block_size_limit),
2730        );
2731        let mut command_buf = alloc_default::<u32, Alloc>();
2732        let mut literal_buf = alloc_default::<u8, Alloc>();
2733        if self.params.quality != 0i32 && (self.params.quality != 1i32) {
2734            return false;
2735        }
2736        if self.params.quality == 1i32 {
2737            if self.command_buf_.slice().is_empty()
2738                && (buf_size == kCompressFragmentTwoPassBlockSize)
2739            {
2740                self.command_buf_ =
2741                    allocate::<u32, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2742                self.literal_buf_ =
2743                    allocate::<u8, _>(&mut self.m8, kCompressFragmentTwoPassBlockSize);
2744            }
2745            if !self.command_buf_.slice().is_empty() {
2746                command_buf = core::mem::take(&mut self.command_buf_);
2747                literal_buf = core::mem::take(&mut self.literal_buf_);
2748            } else {
2749                command_buf = allocate::<u32, _>(&mut self.m8, buf_size);
2750                literal_buf = allocate::<u8, _>(&mut self.m8, buf_size);
2751            }
2752        }
2753        loop {
2754            if self.inject_flush_or_push_output(
2755                available_out,
2756                next_out_array,
2757                next_out_offset,
2758                total_out,
2759            ) {
2760                continue;
2761            }
2762            if self.available_out_ == 0usize
2763                && (self.stream_state_ as i32
2764                    == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32)
2765                && (*available_in != 0usize
2766                    || op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_PROCESS as i32)
2767            {
2768                let block_size: usize = min(block_size_limit, *available_in);
2769                let is_last = *available_in == block_size
2770                    && op == BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
2771                let force_flush = *available_in == block_size
2772                    && op == BrotliEncoderOperation::BROTLI_OPERATION_FLUSH;
2773                let max_out_size: usize = (2usize).wrapping_mul(block_size).wrapping_add(503);
2774                let mut inplace: i32 = 1i32;
2775                let storage: &mut [u8];
2776                let mut storage_ix: usize = self.last_bytes_bits_ as usize;
2777                let mut table_size: usize = 0;
2778
2779                if force_flush && block_size == 0 {
2780                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2781                    {
2782                        continue;
2783                    }
2784                }
2785                if max_out_size <= *available_out {
2786                    storage = &mut next_out_array[*next_out_offset..]; //GetNextOut!(s);
2787                } else {
2788                    inplace = 0i32;
2789                    self.get_brotli_storage(max_out_size);
2790                    storage = self.storage_.slice_mut();
2791                }
2792                storage[0] = self.last_bytes_ as u8;
2793                storage[1] = (self.last_bytes_ >> 8) as u8;
2794                let table: &mut [i32] =
2795                    GetHashTable!(self, self.params.quality, block_size, &mut table_size);
2796                if self.params.quality == 0i32 {
2797                    compress_fragment_fast(
2798                        &mut self.m8,
2799                        &(next_in_array)[*next_in_offset..],
2800                        block_size,
2801                        is_last,
2802                        table,
2803                        table_size,
2804                        &mut self.cmd_depths_[..],
2805                        &mut self.cmd_bits_[..],
2806                        &mut self.cmd_code_numbits_,
2807                        &mut self.cmd_code_[..],
2808                        &mut storage_ix,
2809                        storage,
2810                    );
2811                } else {
2812                    compress_fragment_two_pass(
2813                        &mut self.m8,
2814                        &(next_in_array)[*next_in_offset..],
2815                        block_size,
2816                        is_last,
2817                        command_buf.slice_mut(),
2818                        literal_buf.slice_mut(),
2819                        table,
2820                        table_size,
2821                        &mut storage_ix,
2822                        storage,
2823                    );
2824                }
2825                *next_in_offset += block_size;
2826                *available_in = available_in.wrapping_sub(block_size);
2827                if inplace != 0 {
2828                    let out_bytes: usize = storage_ix >> 3;
2829                    *next_out_offset += out_bytes;
2830                    *available_out = available_out.wrapping_sub(out_bytes);
2831                    self.total_out_ = self.total_out_.wrapping_add(out_bytes as u64);
2832                    if let &mut Some(ref mut total_out_inner) = total_out {
2833                        *total_out_inner = self.total_out_ as usize;
2834                    }
2835                } else {
2836                    let out_bytes: usize = storage_ix >> 3;
2837                    self.next_out_ = NextOut::DynamicStorage(0);
2838                    self.available_out_ = out_bytes;
2839                }
2840                self.last_bytes_ = storage[(storage_ix >> 3)] as u16
2841                    | ((storage[1 + (storage_ix >> 3)] as u16) << 8);
2842                self.last_bytes_bits_ = (storage_ix & 7u32 as usize) as u8;
2843                if force_flush {
2844                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2845                }
2846                if is_last {
2847                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FINISHED;
2848                }
2849                {
2850                    continue;
2851                }
2852            }
2853            {
2854                break;
2855            }
2856        }
2857        if command_buf.slice().len() == kCompressFragmentTwoPassBlockSize
2858            && self.command_buf_.slice().is_empty()
2859        {
2860            // undo temporary aliasing of command_buf and literal_buf
2861            self.command_buf_ = core::mem::take(&mut command_buf);
2862            self.literal_buf_ = core::mem::take(&mut literal_buf);
2863        } else {
2864            <Alloc as Allocator<u32>>::free_cell(&mut self.m8, command_buf);
2865            <Alloc as Allocator<u8>>::free_cell(&mut self.m8, literal_buf);
2866        }
2867        self.check_flush_complete();
2868        true
2869    }
2870
2871    fn remaining_input_block_size(&mut self) -> usize {
2872        let delta: u64 = self.unprocessed_input_size();
2873        let block_size = self.input_block_size();
2874        if delta >= block_size as u64 {
2875            return 0usize;
2876        }
2877        (block_size as u64).wrapping_sub(delta) as usize
2878    }
2879
2880    pub fn compress_stream<
2881        MetablockCallback: FnMut(
2882            &mut interface::PredictionModeContextMap<InputReferenceMut>,
2883            &mut [interface::StaticCommand],
2884            interface::InputPair,
2885            &mut Alloc,
2886        ),
2887    >(
2888        &mut self,
2889        op: BrotliEncoderOperation,
2890        available_in: &mut usize,
2891        next_in_array: &[u8],
2892        next_in_offset: &mut usize,
2893        available_out: &mut usize,
2894        next_out_array: &mut [u8],
2895        next_out_offset: &mut usize,
2896        total_out: &mut Option<usize>,
2897        metablock_callback: &mut MetablockCallback,
2898    ) -> bool {
2899        if !self.ensure_initialized() {
2900            return false;
2901        }
2902        if self.remaining_metadata_bytes_ != u32::MAX {
2903            if *available_in != self.remaining_metadata_bytes_ as usize {
2904                return false;
2905            }
2906            if op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA as i32 {
2907                return false;
2908            }
2909        }
2910        if op as i32 == BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA as i32 {
2911            self.update_size_hint(0);
2912            return self.process_metadata(
2913                available_in,
2914                next_in_array,
2915                next_in_offset,
2916                available_out,
2917                next_out_array,
2918                next_out_offset,
2919                total_out,
2920                metablock_callback,
2921            );
2922        }
2923        if self.stream_state_ as i32 == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_HEAD as i32
2924            || self.stream_state_ as i32
2925                == BrotliEncoderStreamState::BROTLI_STREAM_METADATA_BODY as i32
2926        {
2927            return false;
2928        }
2929        if self.stream_state_ as i32 != BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32
2930            && (*available_in != 0usize)
2931        {
2932            return false;
2933        }
2934        if (self.params.quality == 0i32 || self.params.quality == 1i32) && !self.params.catable {
2935            // this part of the code does not support concatability
2936            return self.compress_stream_fast(
2937                op,
2938                available_in,
2939                next_in_array,
2940                next_in_offset,
2941                available_out,
2942                next_out_array,
2943                next_out_offset,
2944                total_out,
2945            );
2946        }
2947        loop {
2948            let remaining_block_size: usize = self.remaining_input_block_size();
2949            if remaining_block_size != 0usize && (*available_in != 0usize) {
2950                let copy_input_size: usize = min(remaining_block_size, *available_in);
2951                self.copy_input_to_ring_buffer(copy_input_size, &next_in_array[*next_in_offset..]);
2952                *next_in_offset += copy_input_size;
2953                *available_in = available_in.wrapping_sub(copy_input_size);
2954                {
2955                    continue;
2956                }
2957            }
2958            if self.inject_flush_or_push_output(
2959                available_out,
2960                next_out_array,
2961                next_out_offset,
2962                total_out,
2963            ) {
2964                continue;
2965            }
2966            if self.available_out_ == 0usize
2967                && (self.stream_state_ as i32
2968                    == BrotliEncoderStreamState::BROTLI_STREAM_PROCESSING as i32)
2969                && (remaining_block_size == 0usize
2970                    || op as i32 != BrotliEncoderOperation::BROTLI_OPERATION_PROCESS as i32)
2971            {
2972                let is_last =
2973                    *available_in == 0 && op == BrotliEncoderOperation::BROTLI_OPERATION_FINISH;
2974                let force_flush =
2975                    *available_in == 0 && op == BrotliEncoderOperation::BROTLI_OPERATION_FLUSH;
2976
2977                self.update_size_hint(*available_in);
2978                let mut avail_out = self.available_out_;
2979                let result =
2980                    self.encode_data(is_last, force_flush, &mut avail_out, metablock_callback);
2981                self.available_out_ = avail_out;
2982                //this function set next_out to &storage[0]
2983                if !result {
2984                    return false;
2985                }
2986                if force_flush {
2987                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FLUSH_REQUESTED;
2988                }
2989                if is_last {
2990                    self.stream_state_ = BrotliEncoderStreamState::BROTLI_STREAM_FINISHED;
2991                }
2992                {
2993                    continue;
2994                }
2995            }
2996            {
2997                break;
2998            }
2999        }
3000        self.check_flush_complete();
3001        true
3002    }
3003
3004    pub fn is_finished(&self) -> bool {
3005        self.stream_state_ == BrotliEncoderStreamState::BROTLI_STREAM_FINISHED
3006            && !self.has_more_output()
3007    }
3008
3009    pub fn has_more_output(&self) -> bool {
3010        self.available_out_ != 0
3011    }
3012
3013    pub fn take_output(&mut self, size: &mut usize) -> &[u8] {
3014        let mut consumed_size: usize = self.available_out_;
3015        let mut result: &[u8] = GetNextOut!(*self);
3016        if *size != 0 {
3017            consumed_size = min(*size, self.available_out_);
3018        }
3019        if consumed_size != 0 {
3020            self.next_out_ = NextOutIncrement(&self.next_out_, consumed_size as i32);
3021            self.available_out_ = self.available_out_.wrapping_sub(consumed_size);
3022            self.total_out_ = self.total_out_.wrapping_add(consumed_size as u64);
3023            CheckFlushCompleteInner(
3024                &mut self.stream_state_,
3025                self.available_out_,
3026                &mut self.next_out_,
3027            );
3028            *size = consumed_size;
3029        } else {
3030            *size = 0usize;
3031            result = &[];
3032        }
3033        result
3034    }
3035}
3036
3037pub fn BrotliEncoderVersion() -> u32 {
3038    0x0100_0f01
3039}
3040
3041impl<Alloc: BrotliAlloc> BrotliEncoderStateStruct<Alloc> {
3042    pub fn input_block_size(&mut self) -> usize {
3043        if !self.ensure_initialized() {
3044            return 0;
3045        }
3046        1 << self.params.lgblock
3047    }
3048
3049    pub fn write_data<
3050        'a,
3051        MetablockCallback: FnMut(
3052            &mut interface::PredictionModeContextMap<InputReferenceMut>,
3053            &mut [interface::StaticCommand],
3054            interface::InputPair,
3055            &mut Alloc,
3056        ),
3057    >(
3058        &'a mut self,
3059        // FIXME: this should be bool
3060        is_last: i32,
3061        // FIXME: this should be bool
3062        force_flush: i32,
3063        // FIXME: this should probably be removed because the slice already contains the size
3064        out_size: &mut usize,
3065        // FIXME: this should be part of the fn return value
3066        output: &'a mut &'a mut [u8],
3067        metablock_callback: &mut MetablockCallback,
3068    ) -> bool {
3069        let ret = self.encode_data(is_last != 0, force_flush != 0, out_size, metablock_callback);
3070        *output = self.storage_.slice_mut();
3071        ret
3072    }
3073}
3074
3075#[cfg(feature = "std")]
3076mod test {
3077    #[cfg(test)]
3078    use alloc_stdlib::StandardAlloc;
3079
3080    #[test]
3081    fn test_encoder_compress() {
3082        let input = include_bytes!("../../testdata/alice29.txt");
3083        let mut output_buffer = [0; 100000];
3084        let mut output_len = output_buffer.len();
3085        let ret = super::encoder_compress(
3086            StandardAlloc::default(),
3087            &mut StandardAlloc::default(),
3088            9,
3089            16,
3090            super::BrotliEncoderMode::BROTLI_MODE_GENERIC,
3091            input.len(),
3092            input,
3093            &mut output_len,
3094            &mut output_buffer,
3095            &mut |_, _, _, _| (),
3096        );
3097        assert!(ret);
3098        assert_eq!(output_len, 51737);
3099        let mut roundtrip = [0u8; 200000];
3100        let (_, s, t) = super::super::test::oneshot_decompress(
3101            &output_buffer[..output_len],
3102            &mut roundtrip[..],
3103        );
3104        assert_eq!(roundtrip[..t], input[..]);
3105        assert_eq!(s, output_len);
3106    }
3107}