use super::config::BlockBits;
use super::shared::SharedBrotliError;
use thiserror::Error;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct CompressParams {
#[cfg(feature = "experimental")]
pub(crate) stream_offset: usize,
pub(crate) quality: QualityLevel,
pub(crate) lgwin: WindowBits,
pub(crate) lgblock: Option<BlockBits>,
pub(crate) mode: CompressMode,
pub(crate) size_hint: Option<usize>,
pub(crate) distance_codes: DistanceCodes,
pub(crate) literal_context_modeling: bool,
}
impl CompressParams {
pub(crate) const fn new(quality: QualityLevel, lgwin: WindowBits) -> Self {
Self {
#[cfg(feature = "experimental")]
stream_offset: 0,
quality,
lgwin,
lgblock: None,
mode: CompressMode::Generic,
size_hint: None,
distance_codes: DistanceCodes::DEFAULT,
literal_context_modeling: true,
}
}
pub(crate) const fn quality(&self) -> QualityLevel {
self.quality
}
pub(crate) const fn lgwin(&self) -> WindowBits {
self.lgwin
}
#[must_use]
#[cfg(test)]
pub(crate) const fn with_block_bits(mut self, lgblock: Option<BlockBits>) -> Self {
self.lgblock = lgblock;
self
}
pub(crate) const fn lgblock(&self) -> Option<BlockBits> {
self.lgblock
}
#[must_use]
#[cfg(test)]
pub(crate) const fn with_mode(mut self, mode: CompressMode) -> Self {
self.mode = mode;
self
}
pub(crate) const fn mode(&self) -> CompressMode {
self.mode
}
#[must_use]
#[cfg(test)]
pub(crate) const fn with_size_hint(mut self, size_hint: Option<usize>) -> Self {
self.size_hint = size_hint;
self
}
pub(crate) const fn size_hint(&self) -> Option<usize> {
self.size_hint
}
pub(crate) const fn distance_codes(&self) -> DistanceCodes {
self.distance_codes
}
#[must_use]
#[cfg(test)]
pub(crate) const fn with_literal_context_modeling(mut self, enabled: bool) -> Self {
self.literal_context_modeling = enabled;
self
}
pub(crate) const fn literal_context_modeling(&self) -> bool {
self.literal_context_modeling
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
pub(crate) enum CompressMode {
#[default]
Generic,
Text,
Font,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct DistanceCodes {
postfix_bits: u32,
direct_codes: u32,
}
impl DistanceCodes {
pub(crate) const DEFAULT: Self = Self {
postfix_bits: 0,
direct_codes: 0,
};
pub(crate) const fn postfix_bits(&self) -> u32 {
self.postfix_bits
}
pub(crate) const fn direct_codes(&self) -> u32 {
self.direct_codes
}
pub(crate) const fn from_raw(postfix_bits: u32, direct_codes: u32) -> Self {
Self {
postfix_bits,
direct_codes,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub(crate) struct WindowBits(WindowKind);
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
enum WindowKind {
Standard(u8),
Large(u8),
}
impl WindowBits {
pub(crate) const MIN: Self = Self(WindowKind::Standard(10));
#[cfg(test)]
pub(crate) const MAX: Self = Self(WindowKind::Standard(24));
pub(crate) const DEFAULT: Self = Self(WindowKind::Standard(22));
const MIN_BITS: u8 = 10;
const MAX_STANDARD_BITS: u8 = 24;
const MAX_LARGE_BITS: u8 = 62;
pub(crate) const fn standard(bits: u8) -> Result<Self, WindowOutOfRange> {
if bits < Self::MIN_BITS || bits > Self::MAX_STANDARD_BITS {
return Err(WindowOutOfRange);
}
Ok(Self(WindowKind::Standard(bits)))
}
pub(crate) const fn large(bits: u8) -> Result<Self, WindowOutOfRange> {
if bits < Self::MIN_BITS || bits > Self::MAX_LARGE_BITS {
return Err(WindowOutOfRange);
}
Ok(Self(WindowKind::Large(bits)))
}
pub(crate) const fn bits(self) -> u8 {
match self.0 {
WindowKind::Standard(bits) | WindowKind::Large(bits) => bits,
}
}
pub(crate) const fn is_large(self) -> bool {
matches!(self.0, WindowKind::Large(_))
}
}
#[derive(Error, Debug, Copy, Clone, Eq, PartialEq)]
#[error("the window size is outside the range its header can express")]
pub(crate) struct WindowOutOfRange;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub(crate) enum QualityLevel {
Q0,
Q1,
Q2,
Q3,
Q4,
Q5,
Q6,
Q7,
Q8,
Q9,
Q10,
Q11,
}
impl From<QualityLevel> for usize {
fn from(value: QualityLevel) -> Self {
match value {
QualityLevel::Q0 => 0,
QualityLevel::Q1 => 1,
QualityLevel::Q2 => 2,
QualityLevel::Q3 => 3,
QualityLevel::Q4 => 4,
QualityLevel::Q5 => 5,
QualityLevel::Q6 => 6,
QualityLevel::Q7 => 7,
QualityLevel::Q8 => 8,
QualityLevel::Q9 => 9,
QualityLevel::Q10 => 10,
QualityLevel::Q11 => 11,
}
}
}
#[derive(Error, Debug)]
#[non_exhaustive]
pub(crate) enum BrotliCompressError {
#[error("IO error: {0}")]
#[cfg(not(feature = "no_std"))]
IOError(#[from] std::io::Error),
#[error("Quality level {0} is not implemented")]
UnsupportedQuality(usize),
#[error("The output buffer is too small for the compressed stream")]
OutputTooSmall,
#[error("The encoder output buffer overflowed")]
BufferOverflow,
#[error("The compressed-size bound overflows the address space")]
BoundOverflow,
#[error(transparent)]
Shared(#[from] SharedBrotliError),
}
pub(crate) type BrotliResult<T> = Result<T, BrotliCompressError>;