diff options
Diffstat (limited to 'vendor/image/src/io/mod.rs')
-rw-r--r-- | vendor/image/src/io/mod.rs | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/vendor/image/src/io/mod.rs b/vendor/image/src/io/mod.rs new file mode 100644 index 0000000..8fbc6e2 --- /dev/null +++ b/vendor/image/src/io/mod.rs @@ -0,0 +1,166 @@ +//! Input and output of images. + +use std::convert::TryFrom; + +use crate::{error, ImageError, ImageResult}; + +pub(crate) mod free_functions; +mod reader; + +pub use self::reader::Reader; + +/// Set of supported strict limits for a decoder. +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[allow(missing_copy_implementations)] +#[allow(clippy::manual_non_exhaustive)] +pub struct LimitSupport { + _non_exhaustive: (), +} + +#[allow(clippy::derivable_impls)] +impl Default for LimitSupport { + fn default() -> LimitSupport { + LimitSupport { + _non_exhaustive: (), + } + } +} + +/// Resource limits for decoding. +/// +/// Limits can be either *strict* or *non-strict*. Non-strict limits are best-effort +/// limits where the library does not guarantee that limit will not be exceeded. Do note +/// that it is still considered a bug if a non-strict limit is exceeded, however as +/// some of the underlying decoders do not support not support such limits one cannot +/// rely on these limits being supported. For stric limits the library makes a stronger +/// guarantee that the limit will not be exceeded. Exceeding a strict limit is considered +/// a critical bug. If a decoder cannot guarantee that it will uphold a strict limit it +/// *must* fail with `image::error::LimitErrorKind::Unsupported`. +/// +/// Currently the only strict limits supported are the `max_image_width` and `max_image_height` +/// limits, however more will be added in the future. [`LimitSupport`] will default to support +/// being false and decoders should enable support for the limits they support in +/// [`ImageDecoder::set_limits`]. +/// +/// The limit check should only ever fail if a limit will be exceeded or an unsupported strict +/// limit is used. +/// +/// [`LimitSupport`]: ./struct.LimitSupport.html +/// [`ImageDecoder::set_limits`]: ../trait.ImageDecoder.html#method.set_limits +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[allow(missing_copy_implementations)] +#[allow(clippy::manual_non_exhaustive)] +pub struct Limits { + /// The maximum allowed image width. This limit is strict. The default is no limit. + pub max_image_width: Option<u32>, + /// The maximum allowed image height. This limit is strict. The default is no limit. + pub max_image_height: Option<u32>, + /// The maximum allowed sum of allocations allocated by the decoder at any one time excluding + /// allocator overhead. This limit is non-strict by default and some decoders may ignore it. + /// The default is 512MiB. + pub max_alloc: Option<u64>, + _non_exhaustive: (), +} + +impl Default for Limits { + fn default() -> Limits { + Limits { + max_image_width: None, + max_image_height: None, + max_alloc: Some(512 * 1024 * 1024), + _non_exhaustive: (), + } + } +} + +impl Limits { + /// Disable all limits. + pub fn no_limits() -> Limits { + Limits { + max_image_width: None, + max_image_height: None, + max_alloc: None, + _non_exhaustive: (), + } + } + + /// This function checks that all currently set strict limits are supported. + pub fn check_support(&self, _supported: &LimitSupport) -> ImageResult<()> { + Ok(()) + } + + /// This function checks the `max_image_width` and `max_image_height` limits given + /// the image width and height. + pub fn check_dimensions(&self, width: u32, height: u32) -> ImageResult<()> { + if let Some(max_width) = self.max_image_width { + if width > max_width { + return Err(ImageError::Limits(error::LimitError::from_kind( + error::LimitErrorKind::DimensionError, + ))); + } + } + + if let Some(max_height) = self.max_image_height { + if height > max_height { + return Err(ImageError::Limits(error::LimitError::from_kind( + error::LimitErrorKind::DimensionError, + ))); + } + } + + Ok(()) + } + + /// This function checks that the current limit allows for reserving the set amount + /// of bytes, it then reduces the limit accordingly. + pub fn reserve(&mut self, amount: u64) -> ImageResult<()> { + if let Some(max_alloc) = self.max_alloc.as_mut() { + if *max_alloc < amount { + return Err(ImageError::Limits(error::LimitError::from_kind( + error::LimitErrorKind::InsufficientMemory, + ))); + } + + *max_alloc -= amount; + } + + Ok(()) + } + + /// This function acts identically to [`reserve`], but takes a `usize` for convenience. + pub fn reserve_usize(&mut self, amount: usize) -> ImageResult<()> { + match u64::try_from(amount) { + Ok(n) => self.reserve(n), + Err(_) if self.max_alloc.is_some() => Err(ImageError::Limits( + error::LimitError::from_kind(error::LimitErrorKind::InsufficientMemory), + )), + Err(_) => { + // Out of bounds, but we weren't asked to consider any limit. + Ok(()) + } + } + } + + /// This function increases the `max_alloc` limit with amount. Should only be used + /// together with [`reserve`]. + /// + /// [`reserve`]: #method.reserve + pub fn free(&mut self, amount: u64) { + if let Some(max_alloc) = self.max_alloc.as_mut() { + *max_alloc = max_alloc.saturating_add(amount); + } + } + + /// This function acts identically to [`free`], but takes a `usize` for convenience. + pub fn free_usize(&mut self, amount: usize) { + match u64::try_from(amount) { + Ok(n) => self.free(n), + Err(_) if self.max_alloc.is_some() => { + panic!("max_alloc is set, we should have exited earlier when the reserve failed"); + } + Err(_) => { + // Out of bounds, but we weren't asked to consider any limit. + } + } + } +} |