blob: f5ddb46253f2f8faedbc987ba5d3655983768ab9 [file] [log] [blame] [edit]
use crate::decode_error::DecodeError;
use brotlic_sys::{
BrotliDecoderAttachDictionary, BrotliDecoderCreateInstance, BrotliDecoderDecompressStream,
BrotliDecoderDestroyInstance, BrotliDecoderResult_BROTLI_DECODER_RESULT_ERROR,
BrotliDecoderResult_BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT,
BrotliDecoderResult_BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT,
BrotliDecoderResult_BROTLI_DECODER_RESULT_SUCCESS,
BrotliSharedDictionaryType_BROTLI_SHARED_DICTIONARY_RAW, BROTLI_FALSE,
};
use core::ptr;
pub fn shared_brotli_decode_c(
encoded: &[u8],
shared_dictionary: Option<&[u8]>,
max_uncompressed_length: usize,
) -> Result<Vec<u8>, DecodeError> {
let decoder = unsafe { BrotliDecoderCreateInstance(None, None, ptr::null_mut()) };
if decoder.is_null() {
return Err(DecodeError::InitFailure);
}
if let Some(shared_dictionary) = shared_dictionary {
if unsafe {
BrotliDecoderAttachDictionary(
decoder,
BrotliSharedDictionaryType_BROTLI_SHARED_DICTIONARY_RAW,
shared_dictionary.len(),
shared_dictionary.as_ptr(),
)
} == BROTLI_FALSE
{
unsafe {
BrotliDecoderDestroyInstance(decoder);
}
return Err(DecodeError::InvalidDictionary);
}
}
let mut sink = vec![0u8; max_uncompressed_length];
let mut next_in = encoded.as_ptr();
let mut available_in = encoded.len();
let mut next_out = sink.as_mut_ptr();
let mut available_out = sink.len();
let mut total_out = 0;
let mut error: Option<DecodeError> = None;
loop {
let result = unsafe {
BrotliDecoderDecompressStream(
decoder,
&mut available_in,
&mut next_in,
&mut available_out,
&mut next_out,
&mut total_out,
)
};
#[allow(non_upper_case_globals)]
match result {
BrotliDecoderResult_BROTLI_DECODER_RESULT_SUCCESS => break,
BrotliDecoderResult_BROTLI_DECODER_RESULT_ERROR => {
error = Some(DecodeError::InvalidStream);
break;
}
BrotliDecoderResult_BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT if available_in == 0 => {
// Needs more input and none is available.
error = Some(DecodeError::InvalidStream);
break;
}
BrotliDecoderResult_BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT if available_out == 0 => {
// Needs more output space, but none is available.
error = Some(DecodeError::MaxSizeExceeded);
break;
}
_ => continue,
}
}
unsafe {
BrotliDecoderDestroyInstance(decoder);
}
if let Some(error) = error {
return Err(error);
}
if available_in > 0 {
// There's is data left in the input stream, which is not allowed
return Err(DecodeError::ExcessInputData);
}
if total_out > sink.len() {
return Err(DecodeError::MaxSizeExceeded);
}
sink.resize(total_out, 0);
Ok(sink)
}