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 ¶ms,
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); 0 });
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 ¶ms,
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); 0 })
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) {}