-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Christopher N. Hesse <[email protected]>
- Loading branch information
Showing
5 changed files
with
333 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use std::str::FromStr; | ||
|
||
pub fn parse_char(bytes: &mut impl Iterator<Item = u8>) -> Option<Result<char, u8>> { | ||
let byte = bytes.next()?; | ||
match byte { | ||
b'A'..=b'Z' | b'a'..=b'z' => Some(Ok(byte as char)), | ||
_ => Some(Err(byte)), | ||
} | ||
} | ||
|
||
pub fn parse_digit(bytes: &mut impl Iterator<Item = u8>) -> Option<Result<char, u8>> { | ||
let byte = bytes.next()?; | ||
match byte { | ||
b'0'..=b'9' => Some(Ok(byte as char)), | ||
_ => Some(Err(byte)), | ||
} | ||
} | ||
|
||
pub fn parse_seq<I>( | ||
bytes: &mut I, | ||
predicate: impl Fn(&mut I) -> Option<Result<char, u8>>, | ||
) -> Option<(String, u8)> | ||
where | ||
I: Iterator<Item = u8>, | ||
{ | ||
let mut seq = String::new(); | ||
|
||
loop { | ||
let res = predicate(bytes)?; | ||
match res { | ||
Ok(val) => seq.push(val), | ||
Err(b) => return Some((seq, b)), | ||
} | ||
} | ||
} | ||
|
||
pub fn parse_ascii(bytes: &mut impl Iterator<Item = u8>) -> Option<(String, u8)> { | ||
parse_seq(bytes, |iter| { | ||
let b = iter.next()?; | ||
match b { | ||
b' ' => Some(Err(b)), | ||
b'\n' => Some(Err(b)), | ||
_ => Some(Ok(b as char)), | ||
} | ||
}) | ||
} | ||
|
||
pub fn parse_u32( | ||
bytes: &mut impl Iterator<Item = u8>, | ||
) -> Option<(Result<u32, <u32 as FromStr>::Err>, u8)> { | ||
let (word, other) = parse_seq(bytes, parse_digit)?; | ||
match word.parse::<u32>() { | ||
Ok(number) => Some((Ok(number), other)), | ||
Err(e) => Some((Err(e), other)), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,87 +1,73 @@ | ||
use crate::parser::*; | ||
|
||
pub struct Ppm { | ||
pub width: u32, | ||
pub height: u32, | ||
pub range: u32, | ||
pub bytes: Vec<u8>, | ||
} | ||
|
||
pub fn read(bytes: impl IntoIterator<Item = u8>) -> Result<Ppm, &'static str> { | ||
pub fn read(bytes: impl IntoIterator<Item = u8>) -> Option<Result<Ppm, &'static str>> { | ||
let mut bytes = bytes.into_iter(); | ||
|
||
// parse format from first line | ||
let mut magic = [0u8; 2]; | ||
magic[0] = if let Some(byte) = bytes.next() { | ||
byte | ||
} else { | ||
return Err("ppm: not enough bytes"); | ||
return None; | ||
}; | ||
magic[1] = if let Some(byte) = bytes.next() { | ||
byte | ||
} else { | ||
return Err("ppm: not enough bytes"); | ||
return None; | ||
}; | ||
|
||
// is this a P6 PPM? | ||
if magic != *b"P6" { | ||
return Err("ppm: cannot handle magic"); | ||
return None; | ||
} | ||
|
||
fn real_bytes(iter: &mut impl Iterator<Item = u8>, limit: usize) -> Vec<u8> { | ||
let mut bytes = Vec::new(); | ||
for byte in iter { | ||
if bytes.len() == limit { | ||
break; | ||
} | ||
|
||
if byte == b' ' || byte == b'\n' { | ||
if !bytes.is_empty() { | ||
break; | ||
} | ||
} else { | ||
bytes.push(byte); | ||
} | ||
} | ||
bytes | ||
match bytes.next()? { | ||
b' ' | b'\n' => {} | ||
_ => return Some(Err("ppm: expected whitespace")), | ||
} | ||
|
||
// parse width | ||
let width_bytes = real_bytes(&mut bytes, 10); | ||
let width = std::str::from_utf8(&width_bytes) | ||
.expect("bytes should contain ASCII data") | ||
.parse::<usize>() | ||
.expect("value should be integer"); | ||
let width = match parse_u32(&mut bytes)?.0 { | ||
Ok(val) => val, | ||
Err(_e) => return Some(Err("ppm: failed to parse width")), | ||
}; | ||
|
||
// parse height | ||
let height_bytes = real_bytes(&mut bytes, 10); | ||
let height = std::str::from_utf8(&height_bytes) | ||
.expect("bytes should contain ASCII data") | ||
.parse::<usize>() | ||
.expect("value should be integer"); | ||
let height = match parse_u32(&mut bytes)?.0 { | ||
Ok(val) => val, | ||
Err(_e) => return Some(Err("ppm: failed to parse height")), | ||
}; | ||
|
||
// parse range | ||
let range_bytes = real_bytes(&mut bytes, 10); | ||
let range = std::str::from_utf8(&range_bytes) | ||
.expect("bytes should contain ASCII data") | ||
.parse::<usize>() | ||
.expect("value should be integer"); | ||
let range = match parse_u32(&mut bytes)?.0 { | ||
Ok(val) => val, | ||
Err(_e) => return Some(Err("ppm: failed to parse range")), | ||
}; | ||
|
||
if range > 255 { | ||
return Err("ppm: cannot handle range: {range}"); | ||
return Some(Err("ppm: cannot handle range: {range}")); | ||
} | ||
|
||
// take only as many bytes as we expect there to be in the image | ||
let ppm_len = width * height * 3; | ||
let ppm_len = (width * height * 3) as usize; | ||
let bytes: Vec<u8> = bytes.take(ppm_len).collect(); | ||
|
||
// verify buffer length | ||
if bytes.len() != width * height * 3 { | ||
return Err("ppm: invalid length"); | ||
if bytes.len() != ppm_len { | ||
return Some(Err("ppm: invalid length")); | ||
} | ||
|
||
Ok(Ppm { | ||
Some(Ok(Ppm { | ||
width: width as u32, | ||
height: height as u32, | ||
range: range as u32, | ||
bytes, | ||
}) | ||
})) | ||
} |
Oops, something went wrong.