use std::time::Duration;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Limits {
pub max_width: u32,
pub max_height: u32,
pub max_pixels: u64,
pub max_frames: u32,
pub max_animation_duration: Duration,
pub decode_memory_mib: u64,
pub decode_cpu_seconds: u64,
}
impl Default for Limits {
fn default() -> Self {
Self {
max_width: 32_768,
max_height: 32_768,
max_pixels: 256 * 1024 * 1024,
max_frames: 1024,
max_animation_duration: Duration::from_secs(60),
decode_memory_mib: 512,
decode_cpu_seconds: 30,
}
}
}
impl Limits {
pub fn unlimited() -> Self {
Self {
max_width: u32::MAX,
max_height: u32::MAX,
max_pixels: u64::MAX,
max_frames: u32::MAX,
max_animation_duration: Duration::MAX,
decode_memory_mib: u64::MAX,
decode_cpu_seconds: u64::MAX,
}
}
pub fn check_dimensions(&self, width: u32, height: u32, frames: u32) -> crate::Result<()> {
if width > self.max_width {
return Err(crate::Error::LimitExceeded("max_width"));
}
if height > self.max_height {
return Err(crate::Error::LimitExceeded("max_height"));
}
if frames > self.max_frames {
return Err(crate::Error::LimitExceeded("max_frames"));
}
let total = (width as u64)
.checked_mul(height as u64)
.and_then(|p| p.checked_mul(frames as u64));
match total {
Some(p) if p > self.max_pixels => Err(crate::Error::LimitExceeded("max_pixels")),
None => Err(crate::Error::LimitExceeded("max_pixels")),
Some(_) => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_match_documentation() {
let d = Limits::default();
assert_eq!(d.max_width, 32_768);
assert_eq!(d.max_height, 32_768);
assert_eq!(d.max_pixels, 256 * 1024 * 1024);
assert_eq!(d.max_frames, 1024);
assert_eq!(d.max_animation_duration, Duration::from_secs(60));
assert_eq!(d.decode_memory_mib, 512);
assert_eq!(d.decode_cpu_seconds, 30);
}
#[test]
fn check_dimensions_accepts_small_images() {
let l = Limits::default();
l.check_dimensions(1920, 1080, 1).unwrap();
l.check_dimensions(1, 1, 1).unwrap();
}
#[test]
fn check_dimensions_rejects_huge_width() {
let l = Limits::default();
let e = l.check_dimensions(40_000, 100, 1).unwrap_err();
assert!(matches!(e, crate::Error::LimitExceeded("max_width")));
}
#[test]
fn check_dimensions_rejects_huge_pixels() {
let l = Limits::default();
let e = l.check_dimensions(20_000, 20_000, 1).unwrap_err();
assert!(matches!(e, crate::Error::LimitExceeded("max_pixels")));
}
#[test]
fn check_dimensions_rejects_overflow() {
let l = Limits::unlimited();
let e = l
.check_dimensions(u32::MAX, u32::MAX, u32::MAX)
.unwrap_err();
assert!(matches!(e, crate::Error::LimitExceeded("max_pixels")));
}
#[test]
fn check_dimensions_rejects_too_many_frames() {
let l = Limits::default();
let e = l.check_dimensions(10, 10, 2000).unwrap_err();
assert!(matches!(e, crate::Error::LimitExceeded("max_frames")));
}
}