brotli/ffi/multicompress/
mod.rs

1#![cfg(not(feature = "safe"))]
2#[cfg(feature = "std")]
3use std::io::Write;
4#[cfg(feature = "std")]
5use std::{io, panic, thread};
6mod test;
7use super::compressor;
8#[allow(unused_imports)]
9use brotli_decompressor;
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::{
17    BrotliEncoderCompressStream, BrotliEncoderCreateInstance, BrotliEncoderDestroyInstance,
18    BrotliEncoderIsFinished, BrotliEncoderOperation, BrotliEncoderSetParameter,
19};
20
21use super::alloc_util::BrotliSubclassableAllocator;
22use alloc::SliceWrapper;
23use enc;
24use enc::backward_references::{BrotliEncoderParams, UnionHasher};
25use enc::encode::{set_parameter, BrotliEncoderParameter};
26use enc::threading::{Owned, SendAlloc};
27pub const MAX_THREADS: usize = 16;
28
29struct SliceRef<'a>(&'a [u8]);
30impl<'a> SliceWrapper<u8> for SliceRef<'a> {
31    fn slice(&self) -> &[u8] {
32        self.0
33    }
34}
35
36macro_rules! make_send_alloc {
37    ($alloc_func: expr, $free_func: expr, $opaque: expr) => {
38        SendAlloc::new(
39            BrotliSubclassableAllocator::new(SubclassableAllocator::new(CAllocator {
40                alloc_func: $alloc_func,
41                free_func: $free_func,
42                opaque: $opaque,
43            })),
44            UnionHasher::Uninit,
45        )
46    };
47}
48#[no_mangle]
49pub extern "C" fn BrotliEncoderMaxCompressedSizeMulti(
50    input_size: usize,
51    num_threads: usize,
52) -> usize {
53    ::enc::encode::BrotliEncoderMaxCompressedSizeMulti(input_size, num_threads)
54}
55
56fn help_brotli_encoder_compress_single(
57    param_keys: &[BrotliEncoderParameter],
58    param_values: &[u32],
59    input: &[u8],
60    output: &mut [u8],
61    encoded_size: &mut usize,
62    m8: BrotliSubclassableAllocator,
63) -> i32 {
64    let mut encoder = BrotliEncoderCreateInstance(m8);
65    for (p, v) in param_keys.iter().zip(param_values.iter()) {
66        BrotliEncoderSetParameter(&mut encoder, *p, *v);
67    }
68    let mut result;
69    let mut available_in = input.len();
70    let mut next_in_offset = 0usize;
71    let mut available_out = output.len();
72    let mut next_out_offset = 0usize;
73    let mut total_out = Some(0);
74    result = BrotliEncoderCompressStream(
75        &mut encoder,
76        BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
77        &mut available_in,
78        input,
79        &mut next_in_offset,
80        &mut available_out,
81        output,
82        &mut next_out_offset,
83        &mut total_out,
84        &mut |_a, _b, _c, _d| (),
85    );
86    if BrotliEncoderIsFinished(&encoder) == 0 {
87        result = 0i32;
88    }
89    *encoded_size = total_out.unwrap();
90    BrotliEncoderDestroyInstance(&mut encoder);
91    result
92}
93
94#[no_mangle]
95pub unsafe extern "C" fn BrotliEncoderCompressMulti(
96    num_params: usize,
97    param_keys: *const BrotliEncoderParameter,
98    param_values: *const u32,
99    input_size: usize,
100    input: *const u8,
101    encoded_size: *mut usize,
102    encoded: *mut u8,
103    desired_num_threads: usize,
104    alloc_func: brotli_alloc_func,
105    free_func: brotli_free_func,
106    alloc_opaque_per_thread: *mut *mut c_void,
107) -> i32 {
108    if desired_num_threads == 0 {
109        return 0;
110    }
111    let num_threads = core::cmp::min(desired_num_threads, MAX_THREADS);
112    compressor::catch_panic(|| {
113        let param_keys_slice = slice_from_raw_parts_or_nil(param_keys, num_params);
114        let param_values_slice = slice_from_raw_parts_or_nil(param_values, num_params);
115        let input_slice = slice_from_raw_parts_or_nil(input, input_size);
116        let output_slice = slice_from_raw_parts_or_nil_mut(encoded, *encoded_size);
117        if num_threads == 1 {
118            let allocators = CAllocator {
119                alloc_func,
120                free_func,
121                opaque: if alloc_opaque_per_thread.is_null() {
122                    core::ptr::null_mut()
123                } else {
124                    *alloc_opaque_per_thread
125                },
126            };
127            let m8 =
128                BrotliSubclassableAllocator::new(SubclassableAllocator::new(allocators.clone()));
129            return help_brotli_encoder_compress_single(
130                param_keys_slice,
131                param_values_slice,
132                input_slice,
133                output_slice,
134                &mut *encoded_size,
135                m8,
136            );
137        }
138        let null_opaques = [core::ptr::null_mut::<c_void>(); MAX_THREADS];
139        let alloc_opaque = if alloc_opaque_per_thread.is_null() {
140            &null_opaques[..]
141        } else {
142            slice_from_raw_parts_or_nil(alloc_opaque_per_thread, desired_num_threads)
143        };
144        let mut params = BrotliEncoderParams::default();
145        for (k, v) in param_keys_slice.iter().zip(param_values_slice.iter()) {
146            if set_parameter(&mut params, *k, *v) == 0 {
147                return 0;
148            }
149        }
150        let mut alloc_array: [_; MAX_THREADS] = [
151            make_send_alloc!(alloc_func, free_func, alloc_opaque[0]),
152            make_send_alloc!(alloc_func, free_func, alloc_opaque[1 % desired_num_threads]),
153            make_send_alloc!(alloc_func, free_func, alloc_opaque[2 % desired_num_threads]),
154            make_send_alloc!(alloc_func, free_func, alloc_opaque[3 % desired_num_threads]),
155            make_send_alloc!(alloc_func, free_func, alloc_opaque[4 % desired_num_threads]),
156            make_send_alloc!(alloc_func, free_func, alloc_opaque[5 % desired_num_threads]),
157            make_send_alloc!(alloc_func, free_func, alloc_opaque[6 % desired_num_threads]),
158            make_send_alloc!(alloc_func, free_func, alloc_opaque[7 % desired_num_threads]),
159            make_send_alloc!(alloc_func, free_func, alloc_opaque[8 % desired_num_threads]),
160            make_send_alloc!(alloc_func, free_func, alloc_opaque[9 % desired_num_threads]),
161            make_send_alloc!(
162                alloc_func,
163                free_func,
164                alloc_opaque[10 % desired_num_threads]
165            ),
166            make_send_alloc!(
167                alloc_func,
168                free_func,
169                alloc_opaque[11 % desired_num_threads]
170            ),
171            make_send_alloc!(
172                alloc_func,
173                free_func,
174                alloc_opaque[12 % desired_num_threads]
175            ),
176            make_send_alloc!(
177                alloc_func,
178                free_func,
179                alloc_opaque[13 % desired_num_threads]
180            ),
181            make_send_alloc!(
182                alloc_func,
183                free_func,
184                alloc_opaque[14 % desired_num_threads]
185            ),
186            make_send_alloc!(
187                alloc_func,
188                free_func,
189                alloc_opaque[15 % desired_num_threads]
190            ),
191        ];
192
193        let owned_input = &mut Owned::new(SliceRef(input_slice));
194        let res = enc::compress_multi_no_threadpool(
195            &params,
196            owned_input,
197            output_slice,
198            &mut alloc_array[..num_threads],
199        );
200        match res {
201            Ok(size) => {
202                *encoded_size = size;
203                1
204            }
205            Err(_err) => 0,
206        }
207    })
208    .unwrap_or_else(|panic_err| {
209        error_print(panic_err);
210        0
211    })
212}
213
214#[repr(C)]
215pub struct BrotliEncoderWorkPool {
216    custom_allocator: CAllocator,
217    work_pool: enc::WorkerPool<
218        enc::CompressionThreadResult<BrotliSubclassableAllocator>,
219        UnionHasher<BrotliSubclassableAllocator>,
220        BrotliSubclassableAllocator,
221        (SliceRef<'static>, BrotliEncoderParams),
222    >,
223}
224
225#[cfg(not(feature = "std"))]
226fn brotli_new_work_pool_without_custom_alloc(
227    _to_box: BrotliEncoderWorkPool,
228) -> *mut BrotliEncoderWorkPool {
229    panic!("Must supply allocators if calling divans when compiled without features=std");
230}
231
232#[cfg(feature = "std")]
233fn brotli_new_work_pool_without_custom_alloc(
234    to_box: BrotliEncoderWorkPool,
235) -> *mut BrotliEncoderWorkPool {
236    brotli_decompressor::ffi::alloc_util::Box::<BrotliEncoderWorkPool>::into_raw(
237        brotli_decompressor::ffi::alloc_util::Box::<BrotliEncoderWorkPool>::new(to_box),
238    )
239}
240#[no_mangle]
241pub unsafe extern "C" fn BrotliEncoderCreateWorkPool(
242    num_threads: usize,
243    alloc_func: brotli_alloc_func,
244    free_func: brotli_free_func,
245    opaque: *mut c_void,
246) -> *mut BrotliEncoderWorkPool {
247    catch_panic_wstate(|| {
248        let allocators = CAllocator {
249            alloc_func,
250            free_func,
251            opaque,
252        };
253        let to_box = BrotliEncoderWorkPool {
254            custom_allocator: allocators.clone(),
255            work_pool: enc::new_work_pool(core::cmp::min(num_threads, MAX_THREADS)),
256        };
257        if let Some(alloc) = alloc_func {
258            if free_func.is_none() {
259                panic!("either both alloc and free must exist or neither");
260            }
261            let ptr = alloc(
262                allocators.opaque,
263                core::mem::size_of::<BrotliEncoderWorkPool>(),
264            );
265            let brotli_work_pool_ptr =
266                core::mem::transmute::<*mut c_void, *mut BrotliEncoderWorkPool>(ptr);
267            core::ptr::write(brotli_work_pool_ptr, to_box);
268            brotli_work_pool_ptr
269        } else {
270            brotli_new_work_pool_without_custom_alloc(to_box)
271        }
272    })
273    .unwrap_or_else(|err| {
274        error_print(err);
275        core::ptr::null_mut()
276    })
277}
278#[cfg(feature = "std")]
279unsafe fn free_work_pool_no_custom_alloc(_work_pool: *mut BrotliEncoderWorkPool) {
280    let _state = brotli_decompressor::ffi::alloc_util::Box::from_raw(_work_pool);
281}
282
283#[cfg(not(feature = "std"))]
284unsafe fn free_work_pool_no_custom_alloc(_work_pool: *mut BrotliEncoderWorkPool) {
285    unreachable!();
286}
287struct UnsafeUnwindBox(*mut BrotliEncoderWorkPool);
288#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
289impl panic::RefUnwindSafe for UnsafeUnwindBox {}
290
291#[no_mangle]
292pub unsafe extern "C" fn BrotliEncoderDestroyWorkPool(work_pool_ptr: *mut BrotliEncoderWorkPool) {
293    let wpp = UnsafeUnwindBox(work_pool_ptr);
294    if let Err(panic_err) = compressor::catch_panic(|| {
295        if (*wpp.0).custom_allocator.alloc_func.is_some() {
296            if let Some(free_fn) = (*wpp.0).custom_allocator.free_func {
297                let _to_free = core::ptr::read(wpp.0);
298                let ptr = core::mem::transmute::<*mut BrotliEncoderWorkPool, *mut c_void>(wpp.0);
299                free_fn((*wpp.0).custom_allocator.opaque, ptr);
300            }
301        } else {
302            free_work_pool_no_custom_alloc(wpp.0);
303        }
304        0
305    }) {
306        error_print(panic_err);
307    }
308}
309#[no_mangle]
310pub unsafe extern "C" fn BrotliEncoderCompressWorkPool(
311    work_pool: *mut BrotliEncoderWorkPool,
312    num_params: usize,
313    param_keys: *const BrotliEncoderParameter,
314    param_values: *const u32,
315    input_size: usize,
316    input: *const u8,
317    encoded_size: *mut usize,
318    encoded: *mut u8,
319    desired_num_threads: usize,
320    alloc_func: brotli_alloc_func,
321    free_func: brotli_free_func,
322    alloc_opaque_per_thread: *mut *mut c_void,
323) -> i32 {
324    if desired_num_threads == 0 {
325        return 0;
326    }
327    if work_pool.is_null() {
328        return compressor::catch_panic(|| {
329            BrotliEncoderCompressMulti(
330                num_params,
331                param_keys,
332                param_values,
333                input_size,
334                input,
335                encoded_size,
336                encoded,
337                desired_num_threads,
338                alloc_func,
339                free_func,
340                alloc_opaque_per_thread,
341            )
342        })
343        .unwrap_or_else(|panic_err| {
344            error_print(panic_err); // print panic
345            0 // fail
346        });
347    }
348    let work_pool_wrapper = UnsafeUnwindBox(work_pool);
349    compressor::catch_panic(|| {
350        let null_opaques = [core::ptr::null_mut::<c_void>(); MAX_THREADS];
351        let alloc_opaque = if alloc_opaque_per_thread.is_null() {
352            &null_opaques[..]
353        } else {
354            slice_from_raw_parts_or_nil(alloc_opaque_per_thread, desired_num_threads)
355        };
356        let param_keys_slice = slice_from_raw_parts_or_nil(param_keys, num_params);
357        let param_values_slice = slice_from_raw_parts_or_nil(param_values, num_params);
358        let mut params = BrotliEncoderParams::default();
359        for (k, v) in param_keys_slice.iter().zip(param_values_slice.iter()) {
360            if set_parameter(&mut params, *k, *v) == 0 {
361                return 0;
362            }
363        }
364        let num_threads = core::cmp::min(desired_num_threads, MAX_THREADS);
365        let mut alloc_array: [_; MAX_THREADS] = [
366            make_send_alloc!(alloc_func, free_func, alloc_opaque[0]),
367            make_send_alloc!(alloc_func, free_func, alloc_opaque[1 % desired_num_threads]),
368            make_send_alloc!(alloc_func, free_func, alloc_opaque[2 % desired_num_threads]),
369            make_send_alloc!(alloc_func, free_func, alloc_opaque[3 % desired_num_threads]),
370            make_send_alloc!(alloc_func, free_func, alloc_opaque[4 % desired_num_threads]),
371            make_send_alloc!(alloc_func, free_func, alloc_opaque[5 % desired_num_threads]),
372            make_send_alloc!(alloc_func, free_func, alloc_opaque[6 % desired_num_threads]),
373            make_send_alloc!(alloc_func, free_func, alloc_opaque[7 % desired_num_threads]),
374            make_send_alloc!(alloc_func, free_func, alloc_opaque[8 % desired_num_threads]),
375            make_send_alloc!(alloc_func, free_func, alloc_opaque[9 % desired_num_threads]),
376            make_send_alloc!(
377                alloc_func,
378                free_func,
379                alloc_opaque[10 % desired_num_threads]
380            ),
381            make_send_alloc!(
382                alloc_func,
383                free_func,
384                alloc_opaque[11 % desired_num_threads]
385            ),
386            make_send_alloc!(
387                alloc_func,
388                free_func,
389                alloc_opaque[12 % desired_num_threads]
390            ),
391            make_send_alloc!(
392                alloc_func,
393                free_func,
394                alloc_opaque[13 % desired_num_threads]
395            ),
396            make_send_alloc!(
397                alloc_func,
398                free_func,
399                alloc_opaque[14 % desired_num_threads]
400            ),
401            make_send_alloc!(
402                alloc_func,
403                free_func,
404                alloc_opaque[15 % desired_num_threads]
405            ),
406        ];
407        let res = enc::compress_worker_pool(
408            &params,
409            &mut Owned::new(SliceRef(slice_from_raw_parts_or_nil(input, input_size))),
410            slice_from_raw_parts_or_nil_mut(encoded, *encoded_size),
411            &mut alloc_array[..num_threads],
412            &mut (*work_pool_wrapper.0).work_pool,
413        );
414        match res {
415            Ok(size) => {
416                *encoded_size = size;
417                1
418            }
419            Err(_err) => 0,
420        }
421    })
422    .unwrap_or_else(|panic_err| {
423        error_print(panic_err); // print panic
424        0 // fail
425    })
426}
427
428#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
429fn catch_panic_wstate<F: FnOnce() -> *mut BrotliEncoderWorkPool + panic::UnwindSafe>(
430    f: F,
431) -> thread::Result<*mut BrotliEncoderWorkPool> {
432    panic::catch_unwind(f)
433}
434
435#[cfg(all(feature = "std", not(feature = "pass-through-ffi-panics")))]
436fn error_print<Err: core::fmt::Debug>(err: Err) {
437    let _ign = writeln!(&mut io::stderr(), "Internal Error {:?}", err);
438}
439
440#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
441fn catch_panic_wstate<F: FnOnce() -> *mut BrotliEncoderWorkPool>(
442    f: F,
443) -> Result<*mut BrotliEncoderWorkPool, ()> {
444    Ok(f())
445}
446
447#[cfg(any(not(feature = "std"), feature = "pass-through-ffi-panics"))]
448fn error_print<Err>(_err: Err) {}