image/io/
mod.rs

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