1 | //! Input and output of images. |
2 | |
3 | use crate::{error, ColorType, ImageError, ImageResult}; |
4 | |
5 | pub(crate) mod free_functions; |
6 | mod reader; |
7 | |
8 | pub 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)] |
14 | pub struct LimitSupport { |
15 | _non_exhaustive: (), |
16 | } |
17 | |
18 | #[allow (clippy::derivable_impls)] |
19 | impl 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)] |
51 | pub 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 | |
63 | impl 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 | |
74 | impl 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 | } |
181 | |