1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! `brotli` interface implementation

use core::{slice, ptr};
use crate::mem::Box;
use crate::mem::brotli_rust::BrotliAllocator;
use super::{Interface, Encoder, Encode, EncodeStatus, EncodeOp};
use super::brotli_common::BrotliOptions;

pub(crate) type Instance = brotli::enc::encode::BrotliEncoderStateStruct<BrotliAllocator>;

static BROTLI_RUST: Interface = Interface::new(
    reset_fn,
    encode_fn,
    drop_fn,
);

impl Interface {
    #[inline]
    ///Creates decoder with `brotli-rust` interface
    ///
    ///Never returns `None` (probably panics on OOM)
    pub fn brotli_rust(options: BrotliOptions) -> Encoder {
        let mut state = Box::new(instance());

        options.apply_rust(&mut state);

        let ptr = unsafe {
            ptr::NonNull::new_unchecked(Box::into_raw(state) as *mut u8)
        };
        BROTLI_RUST.inner_encoder(ptr.cast(), options.inner)
    }
}

impl EncodeOp {
    #[inline(always)]
    const fn into_rust_brotli(self) -> brotli::enc::encode::BrotliEncoderOperation {
        match self {
            Self::Process => brotli::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_PROCESS,
            Self::Flush => brotli::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FLUSH,
            Self::Finish => brotli::enc::encode::BrotliEncoderOperation::BROTLI_OPERATION_FINISH,
        }
    }
}

fn instance() -> Instance {
    brotli::enc::encode::BrotliEncoderCreateInstance(Default::default())
}

unsafe fn encode_fn(state: ptr::NonNull<u8>, input: *const u8, mut input_remain: usize, output: *mut u8, mut output_remain: usize, op: EncodeOp) -> Encode {
    let state = unsafe {
        &mut *(state.as_ptr() as *mut Instance)
    };

    let input = unsafe {
        slice::from_raw_parts(input, input_remain)
    };
    //Potential UB but it is non-issue
    //Complain here
    //https://github.com/dropbox/rust-brotli/issues/177
    let output = unsafe {
        slice::from_raw_parts_mut(output, output_remain)
    };

    let mut result = brotli::enc::encode::BrotliEncoderCompressStream(
        state,
        op.into_rust_brotli(),
        &mut input_remain, input, &mut 0,
        &mut output_remain, output, &mut 0,
        &mut None,
        &mut |_a, _b, _c, _d| (),
    );
    Encode {
        input_remain,
        output_remain,
        status: match result {
            0 => EncodeStatus::Error,
            _ => {
                result = brotli::enc::encode::BrotliEncoderHasMoreOutput(state);
                if result == 1 {
                    EncodeStatus::NeedOutput
                } else if op == EncodeOp::Finish {
                    EncodeStatus::Finished
                } else {
                    EncodeStatus::Continue
                }
            }
        }
    }
}

#[inline]
fn reset_fn(state: ptr::NonNull<u8>, opts: [u8; 2]) -> Option<ptr::NonNull<u8>> {
    let options = BrotliOptions::from_raw(opts);
    let mut state = unsafe {
        Box::from_raw(state.as_ptr() as *mut Instance)
    };

    *state = instance();
    options.apply_rust(&mut state);

    let ptr = Box::into_raw(state);

    unsafe {
        Some(ptr::NonNull::new_unchecked(ptr as *mut u8))
    }
}

#[inline]
fn drop_fn(state: ptr::NonNull<u8>) {
    let _ = unsafe {
        Box::from_raw(state.as_ptr() as *mut Instance)
    };
}