image/image_reader/
mod.rs

1//! Input and output of images.
2
3use crate::{error, ColorType, ImageError, ImageResult};
4
5pub(crate) mod free_functions;
6mod image_reader_type;
7
8pub use self::image_reader_type::ImageReader;
9
10/// Set of supported strict limits for a decoder.
11#[derive(Clone, Debug, Default, Eq, PartialEq, Hash)]
12#[allow(missing_copy_implementations)]
13#[non_exhaustive]
14pub struct LimitSupport {}
15
16/// Resource limits for decoding.
17///
18/// Limits can be either *strict* or *non-strict*. Non-strict limits are best-effort
19/// limits where the library does not guarantee that limit will not be exceeded. Do note
20/// that it is still considered a bug if a non-strict limit is exceeded.
21/// Some of the underlying decoders do not support such limits, so one cannot
22/// rely on these limits being supported. For strict limits, the library makes a stronger
23/// guarantee that the limit will not be exceeded. Exceeding a strict limit is considered
24/// a critical bug. If a decoder cannot guarantee that it will uphold a strict limit, it
25/// *must* fail with [`error::LimitErrorKind::Unsupported`].
26///
27/// The only currently supported strict limits are the `max_image_width` and `max_image_height`
28/// limits, but more will be added in the future. [`LimitSupport`] will default to support
29/// being false, and decoders should enable support for the limits they support in
30/// [`ImageDecoder::set_limits`].
31///
32/// The limit check should only ever fail if a limit will be exceeded or an unsupported strict
33/// limit is used.
34///
35/// [`LimitSupport`]: ./struct.LimitSupport.html
36/// [`ImageDecoder::set_limits`]: ../trait.ImageDecoder.html#method.set_limits
37#[derive(Clone, Debug, Eq, PartialEq, Hash)]
38#[allow(missing_copy_implementations)]
39#[non_exhaustive]
40pub struct Limits {
41    /// The maximum allowed image width. This limit is strict. The default is no limit.
42    pub max_image_width: Option<u32>,
43    /// The maximum allowed image height. This limit is strict. The default is no limit.
44    pub max_image_height: Option<u32>,
45    /// The maximum allowed sum of allocations allocated by the decoder at any one time excluding
46    /// allocator overhead. This limit is non-strict by default and some decoders may ignore it.
47    /// The bytes required to store the output image count towards this value. The default is
48    /// 512MiB.
49    pub max_alloc: Option<u64>,
50}
51
52impl Default for Limits {
53    fn default() -> Limits {
54        Limits {
55            max_image_width: None,
56            max_image_height: None,
57            max_alloc: Some(512 * 1024 * 1024),
58        }
59    }
60}
61
62impl Limits {
63    /// Disable all limits.
64    #[must_use]
65    pub fn no_limits() -> Limits {
66        Limits {
67            max_image_width: None,
68            max_image_height: None,
69            max_alloc: None,
70        }
71    }
72
73    /// This function checks that all currently set strict limits are supported.
74    pub fn check_support(&self, _supported: &LimitSupport) -> ImageResult<()> {
75        Ok(())
76    }
77
78    /// This function checks the `max_image_width` and `max_image_height` limits given
79    /// the image width and height.
80    pub fn check_dimensions(&self, width: u32, height: u32) -> ImageResult<()> {
81        if let Some(max_width) = self.max_image_width {
82            if width > max_width {
83                return Err(ImageError::Limits(error::LimitError::from_kind(
84                    error::LimitErrorKind::DimensionError,
85                )));
86            }
87        }
88
89        if let Some(max_height) = self.max_image_height {
90            if height > max_height {
91                return Err(ImageError::Limits(error::LimitError::from_kind(
92                    error::LimitErrorKind::DimensionError,
93                )));
94            }
95        }
96
97        Ok(())
98    }
99
100    /// This function checks that the current limit allows for reserving the set amount
101    /// of bytes, it then reduces the limit accordingly.
102    pub fn reserve(&mut self, amount: u64) -> ImageResult<()> {
103        if let Some(max_alloc) = self.max_alloc.as_mut() {
104            if *max_alloc < amount {
105                return Err(ImageError::Limits(error::LimitError::from_kind(
106                    error::LimitErrorKind::InsufficientMemory,
107                )));
108            }
109
110            *max_alloc -= amount;
111        }
112
113        Ok(())
114    }
115
116    /// This function acts identically to [`reserve`], but takes a `usize` for convenience.
117    ///
118    /// [`reserve`]: #method.reserve
119    pub fn reserve_usize(&mut self, amount: usize) -> ImageResult<()> {
120        match u64::try_from(amount) {
121            Ok(n) => self.reserve(n),
122            Err(_) if self.max_alloc.is_some() => Err(ImageError::Limits(
123                error::LimitError::from_kind(error::LimitErrorKind::InsufficientMemory),
124            )),
125            Err(_) => {
126                // Out of bounds, but we weren't asked to consider any limit.
127                Ok(())
128            }
129        }
130    }
131
132    /// This function acts identically to [`reserve`], but accepts the width, height and color type
133    /// used to create an [`ImageBuffer`] and does all the math for you.
134    ///
135    /// [`ImageBuffer`]: crate::ImageBuffer
136    /// [`reserve`]: #method.reserve
137    pub fn reserve_buffer(
138        &mut self,
139        width: u32,
140        height: u32,
141        color_type: ColorType,
142    ) -> ImageResult<()> {
143        self.check_dimensions(width, height)?;
144        let in_memory_size = u64::from(width)
145            .saturating_mul(u64::from(height))
146            .saturating_mul(color_type.bytes_per_pixel().into());
147        self.reserve(in_memory_size)?;
148        Ok(())
149    }
150
151    /// This function increases the `max_alloc` limit with amount. Should only be used
152    /// together with [`reserve`].
153    ///
154    /// [`reserve`]: #method.reserve
155    pub fn free(&mut self, amount: u64) {
156        if let Some(max_alloc) = self.max_alloc.as_mut() {
157            *max_alloc = max_alloc.saturating_add(amount);
158        }
159    }
160
161    /// This function acts identically to [`free`], but takes a `usize` for convenience.
162    ///
163    /// [`free`]: #method.free
164    pub fn free_usize(&mut self, amount: usize) {
165        match u64::try_from(amount) {
166            Ok(n) => self.free(n),
167            Err(_) if self.max_alloc.is_some() => {
168                panic!("max_alloc is set, we should have exited earlier when the reserve failed");
169            }
170            Err(_) => {
171                // Out of bounds, but we weren't asked to consider any limit.
172            }
173        }
174    }
175}