use pdfrum_common::{DiagKind, Diagnostics, Severity};
use crate::Error;
pub const RUN_LENGTH_MAX_OUTPUT: u64 = 20 * 1024 * 1024;
pub fn decode_run_length(input: &[u8], diags: &mut Diagnostics) -> Result<(Vec<u8>, usize), Error> {
let size = declared_size(input)?;
let size = usize::try_from(size).map_err(|_| Error::SizeOverflow)?;
let mut out = Vec::with_capacity(size);
let mut i = 0usize;
let mut truncated = false;
while let Some(&header) = input.get(i) {
if header == 128 {
break;
}
if header < 128 {
let want = usize::from(header) + 1;
let available = input.get(i + 1..).unwrap_or_default();
let take = want.min(available.len());
out.extend_from_slice(available.get(..take).unwrap_or_default());
out.resize(out.len() + (want - take), 0);
if take < want {
truncated = true;
}
i += want + 1;
} else {
let count = 257 - usize::from(header);
let fill = input.get(i + 1).copied().unwrap_or_else(|| {
truncated = true;
0
});
out.resize(out.len() + count, fill);
i += 2;
}
}
if truncated {
diags.record(
Severity::Recovered,
DiagKind::UndecodableStream,
Some(i as u64),
);
}
debug_assert_eq!(out.len(), size);
Ok((out, (i + 1).min(input.len())))
}
fn declared_size(input: &[u8]) -> Result<u64, Error> {
let mut size: u32 = 0;
let mut i = 0usize;
while let Some(&header) = input.get(i) {
if header == 128 {
break;
}
let (run, step) = if header < 128 {
(u32::from(header) + 1, usize::from(header) + 2)
} else {
(257 - u32::from(header), 2)
};
size = size.checked_add(run).ok_or(Error::SizeOverflow)?;
i += step;
}
let size = u64::from(size);
if size >= RUN_LENGTH_MAX_OUTPUT {
return Err(Error::RunLengthTooLarge { size });
}
Ok(size)
}
#[cfg(test)]
mod tests {
use super::{RUN_LENGTH_MAX_OUTPUT, decode_run_length};
use crate::Error;
use pdfrum_common::Diagnostics;
fn decode(input: &[u8]) -> (Vec<u8>, usize) {
let mut diags = Diagnostics::default();
decode_run_length(input, &mut diags).expect("decodes")
}
#[test]
fn a_one_byte_literal_run_then_the_end_marker() {
assert_eq!(decode(&[0, 1, 128]), (vec![1], 3));
}
#[test]
fn mixed_literal_and_repeat_runs_round_trip() {
let plain = [2u8, 2, 2, 2, 4, 4, 4, 4, 4, 4];
let encoded = [253u8, 2, 251, 4, 128];
assert_eq!(decode(&encoded).0, plain);
}
#[test]
fn a_repeat_run_of_the_maximum_length() {
let (out, consumed) = decode(&[129, b'q', 128]);
assert_eq!(out, vec![b'q'; 128]);
assert_eq!(consumed, 3);
}
#[test]
fn a_literal_run_of_the_maximum_length() {
let mut input = vec![127u8];
input.extend(0..128u8);
input.push(128);
let (out, consumed) = decode(&input);
assert_eq!(out.len(), 128);
assert_eq!(out.first(), Some(&0));
assert_eq!(out.last(), Some(&127));
assert_eq!(consumed, 130);
}
#[test]
fn a_truncated_literal_run_keeps_its_declared_length() {
let input = [127u8, 1, 2, 3, 4, 5];
let mut diags = Diagnostics::default();
let (out, consumed) = decode_run_length(&input, &mut diags).expect("decodes");
assert_eq!(out.len(), 128, "the pass-1 length survives");
assert_eq!(out.get(..5), Some(&[1u8, 2, 3, 4, 5][..]));
assert!(out.get(5..).expect("a tail").iter().all(|&b| b == 0));
assert_eq!(consumed, 6);
assert_eq!(diags.len(), 1, "the repair is recorded");
}
#[test]
fn a_repeat_run_missing_its_fill_byte_fills_with_zero() {
let mut diags = Diagnostics::default();
let (out, consumed) = decode_run_length(&[250], &mut diags).expect("decodes");
assert_eq!(out, vec![0u8; 7]);
assert_eq!(consumed, 1);
assert_eq!(diags.len(), 1);
}
#[test]
fn a_missing_end_marker_decodes_to_the_end() {
let input = [1u8, b'a', b'b', 0, b'c'];
let (out, consumed) = decode(&input);
assert_eq!(out, b"abc");
assert_eq!(consumed, input.len());
}
#[test]
fn trailing_bytes_after_the_end_marker_are_not_read() {
let (out, consumed) = decode(&[0, b'x', 128, b'j', b'u', b'n', b'k']);
assert_eq!(out, b"x");
assert_eq!(consumed, 3);
}
#[test]
fn empty_input_decodes_to_nothing() {
assert_eq!(decode(&[]), (Vec::new(), 0));
}
#[test]
fn the_twenty_mebibyte_cap_rejects_at_the_boundary() {
let mut input = Vec::new();
for _ in 0..163_840 {
input.extend_from_slice(&[129, 0]);
}
let mut diags = Diagnostics::default();
assert_eq!(
decode_run_length(&input, &mut diags),
Err(Error::RunLengthTooLarge {
size: RUN_LENGTH_MAX_OUTPUT
})
);
}
#[test]
fn one_byte_under_the_cap_still_decodes() {
let mut input = Vec::new();
for _ in 0..163_839 {
input.extend_from_slice(&[129, 0]);
}
input.push(126);
input.extend(std::iter::repeat_n(0u8, 127));
input.push(128);
let (out, _) = decode(&input);
assert_eq!(out.len() as u64, RUN_LENGTH_MAX_OUTPUT - 1);
}
#[test]
fn an_overflowing_declared_size_is_refused_before_allocating() {
let mut input = Vec::new();
for _ in 0..200_000 {
input.extend_from_slice(&[129, 0]);
}
let mut diags = Diagnostics::default();
assert!(decode_run_length(&input, &mut diags).is_err());
}
}