#![deny(warnings)]
#![deny(missing_docs)]
pub mod decode;
pub mod encode;
use std::error::Error;
use std::os::raw::c_int;
use std::{fmt, io};
use brotlic_sys::*;
pub use decode::{BrotliDecoder, BrotliDecoderOptions, DecompressorReader, DecompressorWriter};
pub use encode::{BrotliEncoder, BrotliEncoderOptions, CompressorReader, CompressorWriter};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Quality(u8);
impl Quality {
pub const fn new(level: u8) -> Result<Quality, SetParameterError> {
match level {
BROTLI_MIN_QUALITY..=BROTLI_MAX_QUALITY => Ok(Quality(level)),
_ => Err(SetParameterError::InvalidQuality),
}
}
pub const unsafe fn new_unchecked(level: u8) -> Quality {
Quality(level)
}
pub const fn best() -> Quality {
Quality(BROTLI_MAX_QUALITY)
}
pub const fn default() -> Quality {
Quality(BROTLI_DEFAULT_QUALITY)
}
pub const fn worst() -> Quality {
Quality(BROTLI_MIN_QUALITY)
}
pub const fn level(&self) -> u8 {
self.0
}
}
impl Default for Quality {
fn default() -> Self {
Quality::default()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct WindowSize(u8);
impl WindowSize {
pub const fn new(bits: u8) -> Result<WindowSize, SetParameterError> {
match bits {
BROTLI_MIN_WINDOW_BITS..=BROTLI_MAX_WINDOW_BITS => Ok(WindowSize(bits)),
_ => Err(SetParameterError::InvalidWindowSize),
}
}
pub const unsafe fn new_unchecked(bits: u8) -> WindowSize {
WindowSize(bits)
}
pub const fn best() -> WindowSize {
WindowSize(BROTLI_MAX_WINDOW_BITS)
}
pub const fn default() -> WindowSize {
WindowSize(BROTLI_DEFAULT_WINDOW)
}
pub const fn worst() -> WindowSize {
WindowSize(BROTLI_MIN_WINDOW_BITS)
}
pub const fn bits(&self) -> u8 {
self.0
}
}
impl Default for WindowSize {
fn default() -> Self {
WindowSize::default()
}
}
impl TryFrom<LargeWindowSize> for WindowSize {
type Error = SetParameterError;
fn try_from(large_window_size: LargeWindowSize) -> Result<Self, Self::Error> {
WindowSize::new(large_window_size.0)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct LargeWindowSize(u8);
impl LargeWindowSize {
pub const fn new(bits: u8) -> Result<LargeWindowSize, SetParameterError> {
match bits {
BROTLI_MIN_WINDOW_BITS..=BROTLI_LARGE_MAX_WINDOW_BITS => Ok(LargeWindowSize(bits)),
_ => Err(SetParameterError::InvalidWindowSize),
}
}
pub const unsafe fn new_unchecked(bits: u8) -> LargeWindowSize {
LargeWindowSize(bits)
}
pub const fn best() -> LargeWindowSize {
LargeWindowSize(BROTLI_LARGE_MAX_WINDOW_BITS)
}
pub const fn default() -> LargeWindowSize {
LargeWindowSize(BROTLI_DEFAULT_WINDOW)
}
pub const fn worst() -> LargeWindowSize {
LargeWindowSize(BROTLI_MIN_WINDOW_BITS)
}
pub const fn bits(&self) -> u8 {
self.0
}
}
impl Default for LargeWindowSize {
fn default() -> Self {
LargeWindowSize::default()
}
}
impl From<WindowSize> for LargeWindowSize {
fn from(window_size: WindowSize) -> Self {
LargeWindowSize(window_size.0)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct BlockSize(u8);
impl BlockSize {
pub const fn new(bits: u8) -> Result<BlockSize, SetParameterError> {
match bits {
BROTLI_MIN_INPUT_BLOCK_BITS..=BROTLI_MAX_INPUT_BLOCK_BITS => Ok(BlockSize(bits)),
_ => Err(SetParameterError::InvalidBlockSize),
}
}
pub const fn new_unchecked(bits: u8) -> BlockSize {
BlockSize(bits)
}
pub const fn best() -> BlockSize {
BlockSize(BROTLI_MAX_INPUT_BLOCK_BITS)
}
pub const fn worst() -> BlockSize {
BlockSize(BROTLI_MIN_INPUT_BLOCK_BITS)
}
pub const fn bits(&self) -> u8 {
self.0
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CompressionMode {
Generic = BrotliEncoderMode_BROTLI_MODE_GENERIC as isize,
Text = BrotliEncoderMode_BROTLI_MODE_TEXT as isize,
Font = BrotliEncoderMode_BROTLI_MODE_FONT as isize,
}
impl Default for CompressionMode {
fn default() -> Self {
CompressionMode::Generic
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct CompressError;
impl fmt::Display for CompressError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("buffer was too small or compression error occurred")
}
}
impl Error for CompressError {}
impl From<CompressError> for io::Error {
fn from(err: CompressError) -> Self {
io::Error::new(io::ErrorKind::Other, err)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DecompressError;
impl fmt::Display for DecompressError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("buffer was too small or decompression error occurred")
}
}
impl Error for DecompressError {}
impl From<DecompressError> for io::Error {
fn from(err: DecompressError) -> Self {
io::Error::new(io::ErrorKind::Other, err)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum SetParameterError {
Generic,
InvalidPostfix,
InvalidDirectDistanceCodes,
InvalidStreamOffset,
InvalidQuality,
InvalidWindowSize,
InvalidBlockSize,
}
impl fmt::Display for SetParameterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SetParameterError::Generic => f.write_str("invalid parameter"),
SetParameterError::InvalidPostfix => f.write_str("invalid number of postfix bits"),
SetParameterError::InvalidDirectDistanceCodes => {
f.write_str("invalid number of direct distance codes")
}
SetParameterError::InvalidStreamOffset => f.write_str("stream offset was out of range"),
SetParameterError::InvalidQuality => f.write_str("quality out of range"),
SetParameterError::InvalidWindowSize => f.write_str("window size out of range"),
SetParameterError::InvalidBlockSize => f.write_str("block size out of range"),
}
}
}
impl Error for SetParameterError {}
#[doc(alias = "BrotliEncoderCompress")]
pub fn compress(
input: &[u8],
output: &mut [u8],
quality: Quality,
window_size: WindowSize,
mode: CompressionMode,
) -> Result<usize, CompressError> {
let mut output_size = output.len();
let res = unsafe {
BrotliEncoderCompress(
quality.0 as c_int,
window_size.0 as c_int,
mode as BrotliEncoderMode,
input.len(),
input.as_ptr(),
&mut output_size as *mut usize,
output.as_mut_ptr(),
)
};
if res != 0 {
Ok(output_size)
} else {
Err(CompressError)
}
}
#[doc(alias = "BrotliEncoderMaxCompressedSize")]
pub fn compress_bound(input_size: usize, quality: Quality) -> Option<usize> {
if quality.0 >= 2 {
Some(unsafe { BrotliEncoderMaxCompressedSize(input_size) })
} else {
None
}
}
#[doc(alias = "BrotliEncoderEstimatePeakMemoryUsage")]
pub fn compress_estimate_max_mem_usage(
input_size: usize,
quality: Quality,
window_size: impl Into<LargeWindowSize>,
) -> usize {
unsafe {
BrotliEncoderEstimatePeakMemoryUsage(quality.0 as _, window_size.into().0 as _, input_size)
}
}
#[doc(alias = "BrotliDecoderDecompress")]
pub fn decompress(input: &[u8], output: &mut [u8]) -> Result<usize, DecompressError> {
let mut output_size = output.len();
let res = unsafe {
BrotliDecoderDecompress(
input.len(),
input.as_ptr(),
&mut output_size as *mut usize,
output.as_mut_ptr(),
)
};
if res == BrotliDecoderResult_BROTLI_DECODER_RESULT_SUCCESS {
Ok(output_size)
} else {
Err(DecompressError)
}
}
#[derive(Debug)]
pub struct IntoInnerError<I>(I, io::Error);
impl<I> IntoInnerError<I> {
fn new(inner: I, error: io::Error) -> Self {
Self(inner, error)
}
pub fn error(&self) -> &io::Error {
&self.1
}
pub fn into_inner(self) -> I {
self.0
}
pub fn into_error(self) -> io::Error {
self.1
}
pub fn into_parts(self) -> (io::Error, I) {
(self.1, self.0)
}
}
impl<I> From<IntoInnerError<I>> for io::Error {
fn from(iie: IntoInnerError<I>) -> io::Error {
iie.1
}
}
impl<I: fmt::Debug + Send> Error for IntoInnerError<I> {}
impl<I> fmt::Display for IntoInnerError<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.error().fmt(f)
}
}