1use std::convert::Infallible;
2use std::io::{self, Bytes, Read};
3
4use crate::maps::{black, mode, white, Mode, EDFB_HALF, EOL};
5use crate::{BitReader, ByteReader, Color, Transitions};
6
7fn with_markup<D, R>(decoder: D, reader: &mut R) -> Option<u16>
8where
9 D: Fn(&mut R) -> Option<u16>,
10{
11 let mut sum: u16 = 0;
12 while let Some(n) = decoder(reader) {
13 sum = sum.checked_add(n)?;
15 if n < 64 {
16 return Some(sum);
18 }
19 }
20 None
21}
22
23fn colored(current: Color, reader: &mut impl BitReader) -> Option<u16> {
24 match current {
26 Color::Black => with_markup(black::decode, reader),
27 Color::White => with_markup(white::decode, reader),
28 }
29}
30
31pub fn pels(line: &[u16], width: u16) -> impl Iterator<Item = Color> + '_ {
36 use std::iter::repeat;
37 let mut color = Color::White;
38 let mut last = 0;
39 let pad_color = if line.len() & 1 == 1 { !color } else { color };
40 line.iter()
41 .flat_map(move |&p| {
42 let c = color;
43 color = !color;
44 let n = p.saturating_sub(last);
45 last = p;
46 repeat(c).take(n as usize)
47 })
48 .chain(repeat(pad_color))
49 .take(width as usize)
50}
51
52pub fn decode_g3(input: impl Iterator<Item = u8>, mut line_cb: impl FnMut(&[u16])) -> Option<()> {
59 let reader = input.map(Result::<u8, Infallible>::Ok);
60 let mut decoder = Group3Decoder::new(reader).ok()?;
61
62 while let Ok(status) = decoder.advance() {
63 line_cb(decoder.transitions());
67 if status == DecodeStatus::End {
68 return Some(());
69 }
70 }
71 None
72}
73
74#[derive(PartialEq, Eq, Debug, Copy, Clone)]
75pub enum DecodeStatus {
76 Incomplete,
77 End,
78}
79
80pub struct Group3Decoder<R> {
81 reader: ByteReader<R>,
82 current: Vec<u16>,
83}
84impl<E: std::fmt::Debug, R: Iterator<Item = Result<u8, E>>> Group3Decoder<R> {
85 pub fn new(reader: R) -> Result<Self, DecodeError<E>> {
86 let mut reader = ByteReader::new(reader).map_err(DecodeError::Reader)?;
87 skip_to_eol(&mut reader).map_err(|_| DecodeError::Invalid)?;
89
90 Ok(Group3Decoder {
91 reader,
92 current: vec![],
93 })
94 }
95 pub fn advance(&mut self) -> Result<DecodeStatus, DecodeError<E>> {
96 self.current.clear();
97 let mut a0: u16 = 0;
98 let mut color = Color::White;
99 loop {
100 if is_eol_ahead(&self.reader) {
104 break;
105 }
106 match colored(color, &mut self.reader) {
107 Some(p) => {
108 a0 = a0.checked_add(p).ok_or(DecodeError::Invalid)?;
109 self.current.push(a0);
110 color = !color;
111 }
112 None => break,
113 }
114 }
115 skip_to_eol(&mut self.reader).map_err(|_| DecodeError::Invalid)?;
117
118 for _ in 0..5 {
120 if is_eol_ahead(&self.reader) {
121 skip_to_eol(&mut self.reader).map_err(|_| DecodeError::Invalid)?;
122 } else {
123 return Ok(DecodeStatus::Incomplete);
124 }
125 }
126
127 Ok(DecodeStatus::End)
128 }
129 pub fn transitions(&self) -> &[u16] {
130 &self.current
131 }
132}
133
134fn is_eol_ahead<E, R: Iterator<Item = Result<u8, E>>>(reader: &ByteReader<R>) -> bool {
144 reader.peek(9) == Some(0)
148}
149
150fn skip_to_eol<E: std::fmt::Debug, R: Iterator<Item = Result<u8, E>>>(
153 reader: &mut ByteReader<R>,
154) -> Result<(), DecodeError<E>> {
155 while reader.peek(1) == Some(0) {
157 reader.consume(1).map_err(DecodeError::Reader)?;
158 }
159 if reader.peek(1) == Some(1) {
161 reader.consume(1).map_err(DecodeError::Reader)?;
162 Ok(())
163 } else {
164 Err(DecodeError::Invalid)
165 }
166}
167
168pub fn decode_g4(
179 input: impl Iterator<Item = u8>,
180 width: u16,
181 height: Option<u16>,
182 mut line_cb: impl FnMut(&[u16]),
183) -> Option<()> {
184 let reader = input.map(Result::<u8, Infallible>::Ok);
185 let mut decoder = Group4Decoder::new(reader, width).ok()?;
186
187 let max_lines = height.unwrap_or(u16::MAX);
188 let mut lines_emitted: u16 = 0;
189
190 while lines_emitted < max_lines {
191 let status = decoder.advance().ok()?;
192 if status == DecodeStatus::End {
193 break;
194 }
195 line_cb(decoder.transition());
196 lines_emitted += 1;
197 }
198
199 if let Some(h) = height {
203 while lines_emitted < h {
204 line_cb(&[]);
205 lines_emitted += 1;
206 }
207 }
208
209 Some(())
210}
211
212#[derive(Debug)]
213pub enum DecodeError<E> {
214 Reader(E),
215 Invalid,
216 Unsupported,
217}
218impl<E> std::fmt::Display for DecodeError<E> {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "Decode Error")
221 }
222}
223impl<E: std::error::Error> std::error::Error for DecodeError<E> {}
224
225pub struct Group4Decoder<R> {
226 reader: ByteReader<R>,
227 reference: Vec<u16>,
228 current: Vec<u16>,
229 width: u16,
230}
231impl<E, R: Iterator<Item = Result<u8, E>>> Group4Decoder<R> {
232 pub fn new(reader: R, width: u16) -> Result<Self, E> {
233 Ok(Group4Decoder {
234 reader: ByteReader::new(reader)?,
235 reference: Vec::new(),
236 current: Vec::new(),
237 width,
238 })
239 }
240 pub fn advance(&mut self) -> Result<DecodeStatus, DecodeError<E>> {
242 let mut transitions = Transitions::new(&self.reference);
243 let mut a0 = 0;
244 let mut color = Color::White;
245 let mut start_of_row = true;
246 loop {
249 let mode = match mode::decode(&mut self.reader) {
251 Some(mode) => mode,
252 None => return Err(DecodeError::Invalid),
253 };
254 match mode {
257 Mode::Pass => {
258 if start_of_row && color == Color::White {
259 transitions.pos += 1;
260 } else {
261 transitions
262 .next_color(a0, !color, false)
263 .ok_or(DecodeError::Invalid)?;
264 }
265 if let Some(b2) = transitions.next() {
267 a0 = b2;
269 }
270 }
271 Mode::Vertical(delta) => {
272 let b1 = transitions
273 .next_color(a0, !color, start_of_row)
274 .unwrap_or(self.width);
275 let a1_i32 = b1 as i32 + delta as i32;
276 if a1_i32 < 0 || a1_i32 > self.width as i32 {
277 break;
278 }
279 let a1 = a1_i32 as u16;
280 if a1 < self.width {
288 self.current.push(a1);
289 }
290 color = !color;
291 a0 = a1;
292 if delta < 0 {
293 transitions.seek_back(a0);
294 }
295 }
296 Mode::Horizontal => {
297 let a0a1 = colored(color, &mut self.reader).ok_or(DecodeError::Invalid)?;
298 let a1a2 = colored(!color, &mut self.reader).ok_or(DecodeError::Invalid)?;
299 let a1 = a0.checked_add(a0a1).ok_or(DecodeError::Invalid)?;
300 let a2 = a1.checked_add(a1a2).ok_or(DecodeError::Invalid)?;
301 if a1 < self.width {
306 self.current.push(a1);
307 }
308 if a2 >= self.width {
309 break;
310 }
311 self.current.push(a2);
312 a0 = a2;
313 }
314 Mode::Extension => {
315 let _ext = self.reader.peek(3).ok_or(DecodeError::Invalid)?;
316 let _ = self.reader.consume(3);
317 return Err(DecodeError::Unsupported);
318 }
319 Mode::EOF => return Ok(DecodeStatus::End),
320 }
321 start_of_row = false;
322
323 if a0 >= self.width {
324 break;
325 }
326 }
327 std::mem::swap(&mut self.reference, &mut self.current);
330 self.current.clear();
331
332 Ok(DecodeStatus::Incomplete)
333 }
334
335 pub fn transition(&self) -> &[u16] {
336 &self.reference
337 }
338
339 pub fn line(&self) -> Line {
340 Line {
341 transitions: &self.reference,
342 width: self.width,
343 }
344 }
345}
346
347pub struct Line<'a> {
348 pub transitions: &'a [u16],
349 pub width: u16,
350}
351impl<'a> Line<'a> {
352 pub fn pels(&self) -> impl Iterator<Item = Color> + 'a {
353 pels(&self.transitions, self.width)
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 #[test]
365 fn g4_fuzz_crash_horizontal_overflow() {
366 let data: Vec<u8> = vec![0xe8, 0x05, 0x00, 0x00, 0x00];
367 let mut lines = 0u32;
368 let result = decode_g4(data.into_iter(), 100, Some(10), |_| {
369 lines += 1;
370 });
371 assert!(
375 result.is_some(),
376 "decoder should recover from caught overflow"
377 );
378 assert!(lines <= 10, "should not exceed requested height");
379 }
380
381 #[test]
385 fn g3_fuzz_crash_run_length_overflow() {
386 let mut data = vec![
387 0x10, 0x10, 0x00, 0x04, 0x00, 0x10, 0x00, 0xb3, 0x00, 0x00, 0x10, 0x00, 0xb3, 0x00,
388 0x10, 0x10,
389 ];
390 data.extend_from_slice(&[0xce; 103]);
391 let result = decode_g3(data.into_iter(), |_| {});
392 assert_eq!(result, None, "corrupt G3 data should return None");
393 }
394
395 #[test]
398 fn g4_large_width_no_overflow() {
399 let data: Vec<u8> = vec![0x00; 512];
400 let result = decode_g4(data.into_iter(), 40000, Some(1), |_| {});
401 let _ = result; }
403
404 #[test]
406 fn g4_zero_width_no_panic() {
407 let data: Vec<u8> = vec![0x00; 64];
408 let result = decode_g4(data.into_iter(), 0, Some(1), |_| {});
409 let _ = result; }
411
412 #[test]
414 fn g3_random_bytes_no_panic() {
415 let data: Vec<u8> = (0..512).map(|i| (i * 37 + 13) as u8).collect();
416 let result = decode_g3(data.into_iter(), |_| {});
417 let _ = result; }
419
420 #[test]
426 fn g4_roundtrip_width_boundary_transition() {
427 let transitions = vec![3u16, 4];
428 let width = 4u16;
429 let input_pels: Vec<_> = super::pels(&transitions, width).collect();
430 let writer = crate::VecWriter::new();
431 let mut encoder = crate::encoder::Encoder::new(writer);
432 let _ = encoder.encode_line(input_pels.iter().copied(), width);
433 let encoded = encoder.finish().unwrap().finish();
434 let mut decoded = Vec::new();
435 let _ = decode_g4(encoded.into_iter(), width, Some(1), |line| {
436 decoded.push(line.to_vec());
437 });
438 let decoded_line = decoded.first().expect("decoded one line");
439 let output_pels: Vec<_> = super::pels(decoded_line, width).collect();
440 assert_eq!(
441 input_pels, output_pels,
442 "pels must roundtrip (decoded transitions: {:?})",
443 decoded_line
444 );
445 }
446
447 #[test]
452 fn g4_roundtrip_canonical_form() {
453 for &(width, ref transitions) in &[
454 (10u16, vec![5]),
455 (2000, vec![10]),
456 (2000, vec![3, 51]),
457 (4, vec![3]),
458 (100, vec![50]),
459 (100, vec![1]),
460 (100, vec![99]),
461 ] {
462 let input_pels: Vec<_> = super::pels(transitions, width).collect();
463 let writer = crate::VecWriter::new();
464 let mut encoder = crate::encoder::Encoder::new(writer);
465 let _ = encoder.encode_line(input_pels.iter().copied(), width);
466 let encoded = encoder.finish().unwrap().finish();
467 let mut decoded = Vec::new();
468 let _ = decode_g4(encoded.into_iter(), width, Some(1), |line| {
469 decoded.push(line.to_vec());
470 });
471 let decoded_line = decoded.first().expect("decoded one line");
472 let output_pels: Vec<_> = super::pels(decoded_line, width).collect();
473 assert_eq!(
474 input_pels, output_pels,
475 "pels must roundtrip for width={width} transitions={transitions:?}, \
476 got decoded transitions {decoded_line:?}"
477 );
478 assert!(
480 decoded_line.iter().all(|&t| t < width),
481 "decoder produced non-canonical transition list {decoded_line:?} \
482 (contains width={width}); transitions should all be < width"
483 );
484 }
485 }
486}