pnm decoding and encoding
fix a lil ub
| -rw-r--r-- | src/decode.rs | 49 | ||||
| -rw-r--r-- | src/pam.rs | 22 | ||||
| -rw-r--r-- | src/pbm.rs | 8 | ||||
| -rw-r--r-- | src/pgm.rs | 5 | ||||
| -rw-r--r-- | src/ppm.rs | 9 |
5 files changed, 66 insertions, 27 deletions
diff --git a/src/decode.rs b/src/decode.rs index 4ca678c..8cfe2e9 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -28,21 +28,42 @@ macro_rules! tenz { }; } tenz!(u8); -tenz!(u16); tenz!(u32); -tenz!(u64); -tenz!(u128); -tenz!(i8); -tenz!(i16); -tenz!(i32); -tenz!(i64); -tenz!(i128); + +pub(crate) trait Ck +where + Self: Sized, +{ + fn checked_mul(self, rhs: Self) -> Option<Self>; + fn checked_add(self, rhs: Self) -> Option<Self>; +} + +macro_rules! cks { + ($for:ty) => { + impl Ck for $for { + fn checked_mul(self, rhs: Self) -> Option<Self> { + <$for>::checked_mul(self, rhs) + } + fn checked_add(self, rhs: Self) -> Option<Self> { + <$for>::checked_add(self, rhs) + } + } + }; +} +cks!(u8); +cks!(u32); /// Result alias with [`Error`]. pub type Result<T> = std::result::Result<T, Error>; pub(crate) fn read_til< - T: Default + std::ops::Mul<T, Output = T> + std::ops::Add<T, Output = T> + From<u8> + Copy + Ten, + T: Default + + Ck + + std::ops::Mul<T, Output = T> + + std::ops::Add<T, Output = T> + + From<u8> + + Copy + + Ten, >( x: &mut &[u8], ) -> Result<T> { @@ -54,7 +75,11 @@ pub(crate) fn read_til< if !x.is_ascii_digit() { return Err(Error::NotDigit(x as char)); } - n = n * T::ten() + T::from(x - b'0') + n = n + .checked_mul(T::ten()) + .ok_or(Error::Overflow)? + .checked_add(T::from(x - b'0')) + .ok_or(Error::Overflow)?; } Ok(n) } @@ -113,6 +138,7 @@ pub enum Error { MissingMax, MissingDepth, MissingTupltype, + Overflow, } impl std::fmt::Display for Error { @@ -133,6 +159,7 @@ impl std::fmt::Display for Error { Self::MissingMax => write!(f, "no max value"), Self::MissingDepth => write!(f, "no depth"), Self::MissingTupltype => write!(f, "no tupltype"), + Self::Overflow => write!(f, "overflow while parsing number"), } } } @@ -141,7 +168,7 @@ impl std::error::Error for Error {} /// Decodes the magic number. pub fn magic(x: &mut &[u8]) -> Option<u8> { (x.by()? == b'P').then_some(())?; - let m = x.by().map(|x| x - b'0'); + let m = x.by().and_then(|x| x.checked_sub(b'0')); while x.first()?.is_ascii_whitespace() { x.by(); } @@ -23,18 +23,20 @@ pub fn encode(x: impl PAM) -> Vec<u8> { x.encode() } +/// Encode this <code>[Image]<[bool], N></code> to a [PAM](https://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format) Raw (binary) Image. +pub fn encode_bitmap(x: impl PAMBit) -> Vec<u8> { + x.encode_bitmap() +} + #[doc(hidden)] pub trait PAM { - /// Encode this image to pam. fn encode(self) -> Vec<u8>; #[doc = include_str!("encode_into.md")] unsafe fn encode_into(x: Self, out: *mut u8) -> usize; } -/// Bool images. -/// Unstable api. +#[doc(hidden)] pub trait PAMBit { - /// Encode this bit image to pam. fn encode_bitmap(self) -> Vec<u8>; #[doc = include_str!("encode_into.md")] unsafe fn encode_into(x: Self, out: *mut u8) -> usize; @@ -193,7 +195,8 @@ impl Type { } /// Decode a PAM image into a [`DynImage`]. -pub fn decode(mut x: &[u8]) -> Result<DynImage<Vec<u8>>> { +pub fn decode(x: impl AsRef<[u8]>) -> Result<DynImage<Vec<u8>>> { + let mut x = x.as_ref(); crate::decode::magic(&mut x); decode_wo_magic(x) } @@ -229,16 +232,19 @@ pub unsafe fn decode_inner(x: &[u8], mut into: *mut u8, header: PAMHeader) -> Re match header.tupltype { Type::Bit => x .iter() - .map(|&x| x * 0xff) + .map(|&x| x.saturating_mul(0xff)) .take(n) .for_each(|x| into.push(x)), Type::BitA => x .iter() .array_chunks::<2>() - .take(n) - .map(|[&x, &a]| [x * 0xff, a]) + .take(header.width.get() as usize * header.height.get() as usize) + .map(|[&x, &a]| [x.saturating_mul(0xff), a]) .for_each(|x| into.put(x)), Type::Y | Type::YA | Type::RGB | Type::RGBA => { + if x.len() < n { + return Err(Error::MissingData); + } into.copy_from(x.as_ptr(), n); } } @@ -179,10 +179,10 @@ pub mod raw { .collect::<Vec<_>>() .chunks_exact((into.width() + padding) as _) .map(|x| &x[..into.width() as _]) - .take(pixels as _) + .take(into.height() as _) .flatten() { - // SAFETY: took `pixels` pixels. + // SAFETY: took `width` * `height` pixels. unsafe { out.push(x) }; } if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < pixels as usize } { @@ -209,11 +209,11 @@ pub mod raw { .collect::<Vec<_>>() .chunks_exact((into.width() + padding) as _) .map(|x| &x[..into.width() as _]) - .take(pixels as _) + .take(into.height() as _) .flatten() .map(|&x| x as u8 * 0xff) { - // SAFETY: took `pixels` pixels. + // SAFETY: took `height` * `width` pixels. unsafe { out.push(x) }; } if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < pixels as usize } { @@ -40,7 +40,10 @@ pub mod plain { .split(u8::is_ascii_whitespace) .filter(|x| !x.is_empty() && x.len() <= 3) .filter(|x| x.iter().all(u8::is_ascii_digit)) - .map(|x| x.iter().fold(0, |acc, &x| acc * 10 + (x - b'0'))) + .flat_map(|x| { + x.iter() + .try_fold(0u8, |acc, &x| acc.checked_mul(10)?.checked_add(x - b'0')) + }) .map(|x| { if max == 255 { x @@ -39,7 +39,10 @@ pub mod plain { .split(u8::is_ascii_whitespace) .filter(|x| !x.is_empty() && x.len() <= 3) .filter(|x| x.iter().all(u8::is_ascii_digit)) - .map(|x| x.iter().fold(0, |acc, &x| acc * 10 + (x - b'0'))) + .flat_map(|x| { + x.iter() + .try_fold(0u8, |acc, &x| acc.checked_mul(10)?.checked_add(x - b'0')) + }) .map(|x| { if max == 255 { x @@ -53,7 +56,7 @@ pub mod plain { // SAFETY: iterator over `pixels` elements. unsafe { out.put(b) }; } - if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < (pixels * 3) as usize } { + if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < (pixels as usize * 3) } { return Err(Error::MissingData); } // SAFETY: checked that the pixels have been initialized. @@ -143,7 +146,7 @@ pub mod raw { // SAFETY: took `pixels` pixels. unsafe { out.put(b) }; } - if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < (pixels * 3) as usize } { + if unsafe { out.sub_ptr(into.buf().as_mut_ptr().cast()) < (pixels as usize * 3) } { return Err(Error::MissingData); } // SAFETY: checked that the pixels have been initialized. |