brotli/ffi/
compressor.rs

1#![cfg(not(feature = "safe"))]
2
3#[cfg(feature = "std")]
4use std::io::Write;
5#[cfg(feature = "std")]
6use std::{io, panic, thread};
7
8use super::alloc_util::BrotliSubclassableAllocator;
9use brotli_decompressor::ffi::alloc_util;
10use brotli_decompressor::ffi::alloc_util::SubclassableAllocator;
11use brotli_decompressor::ffi::interface::{
12    brotli_alloc_func, brotli_free_func, c_void, CAllocator,
13};
14use brotli_decompressor::ffi::{slice_from_raw_parts_or_nil, slice_from_raw_parts_or_nil_mut};
15use core;
16use enc::encode::BrotliEncoderStateStruct;
17
18#[repr(C)]
19pub enum BrotliEncoderOperation {
20    BROTLI_OPERATION_PROCESS = 0,
21    BROTLI_OPERATION_FLUSH = 1,
22    BROTLI_OPERATION_FINISH = 2,
23    BROTLI_OPERATION_EMIT_METADATA = 3,
24}
25
26#[repr(C)]
27pub enum BrotliEncoderMode {
28    BROTLI_MODE_GENERIC = 0,
29    BROTLI_MODE_TEXT = 1,
30    BROTLI_MODE_FONT = 2,
31    BROTLI_MODE_FORCE_LSB_PRIOR = 3,
32    BROTLI_MODE_FORCE_MSB_PRIOR = 4,
33    BROTLI_MODE_FORCE_UTF8_PRIOR = 5,
34    BROTLI_MODE_FORCE_SIGNED_PRIOR = 6,
35}
36
37#[repr(C)]
38pub struct BrotliEncoderState {
39    pub custom_allocator: CAllocator,
40    pub compressor: BrotliEncoderStateStruct<BrotliSubclassableAllocator>,
41}
42
43#[cfg(not(feature = "std"))]
44fn brotli_new_compressor_without_custom_alloc(
45    _to_box: BrotliEncoderState,
46) -> *mut BrotliEncoderState {
47    panic!("Must supply allocators if calling divans when compiled without features=std");
48}
49
50#[cfg(feature = "std")]
51fn brotli_new_compressor_without_custom_alloc(
52    to_box: BrotliEncoderState,
53) -> *mut BrotliEncoderState {
54    alloc_util::Box::<BrotliEncoderState>::into_raw(alloc_util::Box::<BrotliEncoderState>::new(
55        to_box,
56    ))
57}
58#[cfg(feature = "std")]
59unsafe fn free_compressor_no_custom_alloc(state_ptr: *mut BrotliEncoderState) {
60    let _state = alloc_util::Box::from_raw(state_ptr);
61}
62
63#[cfg(not(feature = "std"))]
64unsafe fn free_compressor_no_custom_alloc(_state_ptr: *mut BrotliEncoderState) {
65    unreachable!();
66}
67
68#[no_mangle]
69pub unsafe extern "C" fn BrotliEncoderCreateInstance(
70    alloc_func: brotli_alloc_func,
71    free_func: brotli_free_func,
72    opaque: *mut c_void,
73) -> *mut BrotliEncoderState {
74    catch_panic_cstate(|| {
75        let allocators = CAllocator {
76            alloc_func,
77            free_func,
78            opaque,
79        };
80        let to_box = BrotliEncoderState {
81            custom_allocator: allocators.clone(),
82            compressor: ::enc::encode::BrotliEncoderCreateInstance(
83                BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone())),
84            ),
85        };
86        if let Some(alloc) = alloc_func {
87            if free_func.is_none() {
88                panic!("either both alloc and free must exist or neither");
89            }
90            let ptr = alloc(
91                allocators.opaque,
92                core::mem::size_of::<BrotliEncoderState>(),
93            );
94            let brotli_decoder_state_ptr =
95                core::mem::transmute::<*mut c_void, *mut BrotliEncoderState>(ptr);
96            core::ptr::write(brotli_decoder_state_ptr, to_box);
97            brotli_decoder_state_ptr
98        } else {
99            brotli_new_compressor_without_custom_alloc(to_box)
100        }
101    })
102    .unwrap_or_else(|err| {
103        error_print(err);
104        core::ptr::null_mut()
105    })
106}
107
108#[no_mangle]
109pub unsafe extern "C" fn BrotliEncoderSetParameter(
110    state_ptr: *mut BrotliEncoderState,
111    param: ::enc::encode::BrotliEncoderParameter,
112    value: u32,
113) -> i32 {
114    ::enc::encode::BrotliEncoderSetParameter(&mut (*state_ptr).compressor, param, value)
115}
116
117#[no_mangle]
118pub unsafe extern "C" fn BrotliEncoderDestroyInstance(state_ptr: *mut BrotliEncoderState) {
119    if state_ptr.is_null() {
120        return;
121    }
122    ::enc::encode::BrotliEncoderDestroyInstance(&mut (*state_ptr).compressor);
123    if (*state_ptr).custom_allocator.alloc_func.is_some() {
124        if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
125            let _to_free = core::ptr::read(state_ptr);
126            let ptr = core::mem::transmute::<*mut BrotliEncoderState, *mut c_void>(state_ptr);
127            free_fn((*state_ptr).custom_allocator.opaque, ptr);
128        }
129    } else {
130        free_compressor_no_custom_alloc(state_ptr);
131    }
132}
133#[no_mangle]
134pub unsafe extern "C" fn BrotliEncoderIsFinished(state_ptr: *mut BrotliEncoderState) -> i32 {
135    ::enc::encode::BrotliEncoderIsFinished(&mut (*state_ptr).compressor)
136}
137
138#[no_mangle]
139pub unsafe extern "C" fn BrotliEncoderHasMoreOutput(state_ptr: *mut BrotliEncoderState) -> i32 {
140    ::enc::encode::BrotliEncoderHasMoreOutput(&mut (*state_ptr).compressor)
141}
142
143#[no_mangle]
144pub unsafe extern "C" fn BrotliEncoderSetCustomDictionary(
145    state_ptr: *mut BrotliEncoderState,
146    size: usize,
147    dict: *const u8,
148) {
149    if let Err(panic_err) = catch_panic(|| {
150        let dict_slice = slice_from_raw_parts_or_nil(dict, size);
151        ::enc::encode::BrotliEncoderSetCustomDictionary(
152            &mut (*state_ptr).compressor,
153            size,
154            dict_slice,
155        );
156        0
157    }) {
158        error_print(panic_err);
159    }
160}
161
162#[no_mangle]
163pub unsafe extern "C" fn BrotliEncoderTakeOutput(
164    state_ptr: *mut BrotliEncoderState,
165    size: *mut usize,
166) -> *const u8 {
167    ::enc::encode::BrotliEncoderTakeOutput(&mut (*state_ptr).compressor, &mut *size).as_ptr()
168}
169#[no_mangle]
170pub extern "C" fn BrotliEncoderVersion() -> u32 {
171    ::enc::encode::BrotliEncoderVersion()
172}
173#[no_mangle]
174pub extern "C" fn BrotliEncoderMaxCompressedSize(input_size: usize) -> usize {
175    ::enc::encode::BrotliEncoderMaxCompressedSize(input_size)
176}
177#[no_mangle]
178pub unsafe extern "C" fn BrotliEncoderCompress(
179    quality: i32,
180    lgwin: i32,
181    mode: BrotliEncoderMode,
182    input_size: usize,
183    input_buffer: *const u8,
184    encoded_size: *mut usize,
185    encoded_buffer: *mut u8,
186) -> i32 {
187    catch_panic(|| {
188        let input_buf = slice_from_raw_parts_or_nil(input_buffer, input_size);
189        let encoded_buf = slice_from_raw_parts_or_nil_mut(encoded_buffer, *encoded_size);
190        let allocators = CAllocator {
191            alloc_func: None,
192            free_func: None,
193            opaque: core::ptr::null_mut(),
194        };
195        let translated_mode = match mode {
196            BrotliEncoderMode::BROTLI_MODE_GENERIC => {
197                ::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_GENERIC
198            }
199            BrotliEncoderMode::BROTLI_MODE_TEXT => {
200                ::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_TEXT
201            }
202            BrotliEncoderMode::BROTLI_MODE_FONT => {
203                ::enc::backward_references::BrotliEncoderMode::BROTLI_MODE_FONT
204            }
205            BrotliEncoderMode::BROTLI_MODE_FORCE_LSB_PRIOR => {
206                ::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_LSB_PRIOR
207            }
208            BrotliEncoderMode::BROTLI_MODE_FORCE_MSB_PRIOR => {
209                ::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_MSB_PRIOR
210            }
211            BrotliEncoderMode::BROTLI_MODE_FORCE_UTF8_PRIOR => {
212                ::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_UTF8_PRIOR
213            }
214            BrotliEncoderMode::BROTLI_MODE_FORCE_SIGNED_PRIOR => {
215                ::enc::backward_references::BrotliEncoderMode::BROTLI_FORCE_SIGNED_PRIOR
216            }
217        };
218        let mut m8 =
219            BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
220        let empty_m8 =
221            BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
222
223        ::enc::encode::BrotliEncoderCompress(
224            empty_m8,
225            &mut m8,
226            quality,
227            lgwin,
228            translated_mode,
229            input_size,
230            input_buf,
231            &mut *encoded_size,
232            encoded_buf,
233            &mut |_a, _b, _c, _d| (),
234        )
235    })
236    .unwrap_or_else(|panic_err| {
237        error_print(panic_err);
238        0
239    })
240}
241
242#[no_mangle]
243pub unsafe extern "C" fn BrotliEncoderCompressStreaming(
244    state_ptr: *mut BrotliEncoderState,
245    op: BrotliEncoderOperation,
246    available_in: *mut usize,
247    mut input_buf: *const u8,
248    available_out: *mut usize,
249    mut output_buf: *mut u8,
250) -> i32 {
251    BrotliEncoderCompressStream(
252        state_ptr,
253        op,
254        available_in,
255        &mut input_buf,
256        available_out,
257        &mut output_buf,
258        core::ptr::null_mut(),
259    )
260}
261
262#[no_mangle]
263pub unsafe extern "C" fn BrotliEncoderCompressStream(
264    state_ptr: *mut BrotliEncoderState,
265    op: BrotliEncoderOperation,
266    available_in: *mut usize,
267    input_buf_ptr: *mut *const u8,
268    available_out: *mut usize,
269    output_buf_ptr: *mut *mut u8,
270    total_out: *mut usize,
271) -> i32 {
272    catch_panic(|| {
273        let mut input_offset = 0usize;
274        let mut output_offset = 0usize;
275        let result;
276        let translated_op = match op {
277            BrotliEncoderOperation::BROTLI_OPERATION_PROCESS => {
278                ::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_PROCESS
279            }
280            BrotliEncoderOperation::BROTLI_OPERATION_FLUSH => {
281                ::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FLUSH
282            }
283            BrotliEncoderOperation::BROTLI_OPERATION_FINISH => {
284                ::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FINISH
285            }
286            BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA => {
287                ::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_EMIT_METADATA
288            }
289        };
290        {
291            let (input_buf, input_any): (&[u8], bool) = if *available_in != 0 {
292                (
293                    slice_from_raw_parts_or_nil(*input_buf_ptr, *available_in),
294                    true,
295                )
296            } else {
297                (&[], false)
298            };
299            let (output_buf, output_any): (&mut [u8], bool) = if *available_out != 0 {
300                (
301                    slice_from_raw_parts_or_nil_mut(*output_buf_ptr, *available_out),
302                    true,
303                )
304            } else {
305                (&mut [], false)
306            };
307            let mut to = Some(0);
308            result = ::enc::encode::BrotliEncoderCompressStream(
309                &mut (*state_ptr).compressor,
310                translated_op,
311                &mut *available_in,
312                input_buf,
313                &mut input_offset,
314                &mut *available_out,
315                output_buf,
316                &mut output_offset,
317                &mut to,
318                &mut |_a, _b, _c, _d| (),
319            );
320            if !total_out.is_null() {
321                *total_out = to.unwrap_or(0);
322            }
323            if input_any {
324                *input_buf_ptr = (*input_buf_ptr).add(input_offset);
325            }
326            if output_any {
327                *output_buf_ptr = (*output_buf_ptr).add(output_offset);
328            }
329        }
330        result
331    })
332    .unwrap_or_else(|panic_err| {
333        error_print(panic_err);
334        0
335    })
336}
337
338#[no_mangle]
339pub unsafe extern "C" fn BrotliEncoderMallocU8(
340    state_ptr: *mut BrotliEncoderState,
341    size: usize,
342) -> *mut u8 {
343    if let Some(alloc_fn) = (*state_ptr).custom_allocator.alloc_func {
344        core::mem::transmute::<*mut c_void, *mut u8>(alloc_fn(
345            (*state_ptr).custom_allocator.opaque,
346            size,
347        ))
348    } else {
349        alloc_util::alloc_stdlib(size)
350    }
351}
352
353#[no_mangle]
354pub unsafe extern "C" fn BrotliEncoderFreeU8(
355    state_ptr: *mut BrotliEncoderState,
356    data: *mut u8,
357    size: usize,
358) {
359    if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
360        free_fn(
361            (*state_ptr).custom_allocator.opaque,
362            core::mem::transmute::<*mut u8, *mut c_void>(data),
363        );
364    } else {
365        alloc_util::free_stdlib(data, size);
366    }
367}
368
369#[no_mangle]
370pub unsafe extern "C" fn BrotliEncoderMallocUsize(
371    state_ptr: *mut BrotliEncoderState,
372    size: usize,
373) -> *mut usize {
374    if let Some(alloc_fn) = (*state_ptr).custom_allocator.alloc_func {
375        core::mem::transmute::<*mut c_void, *mut usize>(alloc_fn(
376            (*state_ptr).custom_allocator.opaque,
377            size * core::mem::size_of::<usize>(),
378        ))
379    } else {
380        alloc_util::alloc_stdlib(size)
381    }
382}
383#[no_mangle]
384pub unsafe extern "C" fn BrotliEncoderFreeUsize(
385    state_ptr: *mut BrotliEncoderState,
386    data: *mut usize,
387    size: usize,
388) {
389    if let Some(free_fn) = (*state_ptr).custom_allocator.free_func {
390        free_fn(
391            (*state_ptr).custom_allocator.opaque,
392            core::mem::transmute::<*mut usize, *mut c_void>(data),
393        );
394    } else {
395        alloc_util::free_stdlib(data, size);
396    }
397}
398
399#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
400pub fn catch_panic<F: FnOnce() -> i32 + panic::UnwindSafe>(f: F) -> thread::Result<i32> {
401    panic::catch_unwind(f)
402}
403
404#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
405fn catch_panic_cstate<F: FnOnce() -> *mut BrotliEncoderState + panic::UnwindSafe>(
406    f: F,
407) -> thread::Result<*mut BrotliEncoderState> {
408    panic::catch_unwind(f)
409}
410
411#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
412fn error_print<Err: core::fmt::Debug>(err: Err) {
413    let _ign = writeln!(&mut io::stderr(), "Internal Error {:?}", err);
414}
415
416// can't catch panics in a reliable way without std:: configure with panic=abort. These shouldn't happen
417#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
418pub fn catch_panic<F: FnOnce() -> i32>(f: F) -> Result<i32, ()> {
419    Ok(f())
420}
421
422#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
423fn catch_panic_cstate<F: FnOnce() -> *mut BrotliEncoderState>(
424    f: F,
425) -> Result<*mut BrotliEncoderState, ()> {
426    Ok(f())
427}
428
429#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
430fn error_print<Err>(_err: Err) {}