use crate::compressor::core::rfc9841::window::MAX_ENCODER_WINDOW_BITS;
use crate::compressor::core::shared::constants::{OUTPUT_RESERVE_CONST, OUTPUT_SLACK};
use crate::compressor::{BrotliCompressError, BrotliResult, CompressParams, QualityLevel};
pub(crate) const fn bound(params: &CompressParams, input_size: usize) -> BrotliResult<usize> {
let fragment = 1usize << fragment_bits(params);
let fragments = if input_size == 0 {
1
} else {
(input_size - 1) / fragment + 1
};
let Some(overhead) = fragments.checked_mul(OUTPUT_RESERVE_CONST + OUTPUT_SLACK) else {
return Err(BrotliCompressError::BoundOverflow);
};
let Some(payload) = input_size.checked_mul(2) else {
return Err(BrotliCompressError::BoundOverflow);
};
let Some(total) = payload.checked_add(overhead) else {
return Err(BrotliCompressError::BoundOverflow);
};
match total.checked_add(2) {
Some(total) => Ok(total),
None => Err(BrotliCompressError::BoundOverflow),
}
}
const fn fragment_bits(params: &CompressParams) -> usize {
match params.quality {
QualityLevel::Q3 => 14,
QualityLevel::Q4 | QualityLevel::Q5 => match params.lgblock {
Some(lgblock) => lgblock.0,
None => 16,
},
_ => {
let bits = params.lgwin.bits() as usize;
if bits > MAX_ENCODER_WINDOW_BITS {
MAX_ENCODER_WINDOW_BITS
} else {
bits
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compressor::{QualityLevel, WindowBits, WindowOutOfRange};
fn params(lgwin: u8) -> Result<CompressParams, WindowOutOfRange> {
Ok(CompressParams::new(
QualityLevel::Q0,
WindowBits::standard(lgwin)?,
))
}
#[test]
fn empty_input_still_reserves_one_fragment() -> Result<(), WindowOutOfRange> {
assert!(matches!(bound(¶ms(22)?, 0), Ok(513)));
Ok(())
}
#[test]
fn bound_grows_with_the_number_of_fragments() -> Result<(), WindowOutOfRange> {
let small_window = bound(¶ms(10)?, 1 << 20).ok();
let large_window = bound(¶ms(22)?, 1 << 20).ok();
assert!(small_window > large_window);
Ok(())
}
#[test]
fn bound_covers_at_least_the_input() -> Result<(), WindowOutOfRange> {
let params = params(22)?;
for size in [0usize, 1, 1024, 1 << 20] {
assert!(bound(¶ms, size).is_ok_and(|value| value >= size));
}
Ok(())
}
#[test]
fn bound_reports_an_overflow_instead_of_wrapping() -> Result<(), WindowOutOfRange> {
assert!(matches!(
bound(¶ms(22)?, usize::MAX),
Err(BrotliCompressError::BoundOverflow)
));
assert!(matches!(
bound(¶ms(10)?, usize::MAX / 2),
Err(BrotliCompressError::BoundOverflow)
));
Ok(())
}
}