pnm decoding and encoding
fix a lil ub
bendn 2024-03-16
parent e801485 · commit d7d614b
-rw-r--r--src/decode.rs49
-rw-r--r--src/pam.rs22
-rw-r--r--src/pbm.rs8
-rw-r--r--src/pgm.rs5
-rw-r--r--src/ppm.rs9
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();
}
diff --git a/src/pam.rs b/src/pam.rs
index 726562c..49225cc 100644
--- a/src/pam.rs
+++ b/src/pam.rs
@@ -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);
}
}
diff --git a/src/pbm.rs b/src/pbm.rs
index 40b3bf3..1461344 100644
--- a/src/pbm.rs
+++ b/src/pbm.rs
@@ -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 } {
diff --git a/src/pgm.rs b/src/pgm.rs
index f2dcb73..f1f2cb8 100644
--- a/src/pgm.rs
+++ b/src/pgm.rs
@@ -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
diff --git a/src/ppm.rs b/src/ppm.rs
index 3b10c30..950e4d0 100644
--- a/src/ppm.rs
+++ b/src/ppm.rs
@@ -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.