1 | //! Input and output of images. |
2 | |
3 | use crate::{error, ColorType, ImageError, ImageResult}; |
4 | |
5 | pub(crate) mod free_functions; |
6 | mod image_reader_type; |
7 | |
8 | pub 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 ] |
14 | pub 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 ] |
40 | pub 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 | |
52 | impl 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 | |
62 | impl 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 | } |
176 | |