| 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 | |