mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-27 11:37:59 -06:00
M2: copy Flate back-references in runs (#2035)
* M2: copy Flate back-references in runs * M2: fold Flate note into hayro-syntax row
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+64
-14
@@ -534,23 +534,27 @@ pub(crate) mod flate {
|
||||
|
||||
/// Copies `left` bytes from `distance` back in the output, up to the limit. Returns how
|
||||
/// many are still to copy. A copy whose source does not exist writes nothing.
|
||||
fn copy_back(&mut self, valid: bool, distance: usize, mut left: usize) -> usize {
|
||||
while left > 0 && self.output.len() < self.limit {
|
||||
if valid {
|
||||
let byte = self
|
||||
.output
|
||||
.len()
|
||||
.checked_sub(distance)
|
||||
.and_then(|index| self.output.get(index))
|
||||
.copied();
|
||||
if let Some(byte) = byte {
|
||||
self.output.push(byte);
|
||||
}
|
||||
fn copy_back(&mut self, valid: bool, distance: usize, left: usize) -> usize {
|
||||
let len = self.output.len();
|
||||
// Without a source the copy writes nothing, and it runs out only at the limit.
|
||||
let Some(mut start) = len.checked_sub(distance).filter(|_| valid && distance > 0) else {
|
||||
return if len < self.limit { 0 } else { left };
|
||||
};
|
||||
// PdfCraft patch: copy in runs of up to `distance` bytes. Each run reads only bytes
|
||||
// already in the output, so the bytes are the ones a byte-at-a-time copy writes.
|
||||
let copy = left.min(self.limit.saturating_sub(len));
|
||||
let mut done = 0;
|
||||
while done < copy {
|
||||
let end = start.saturating_add((copy - done).min(distance));
|
||||
if end > self.output.len() {
|
||||
break;
|
||||
}
|
||||
left -= 1;
|
||||
self.output.extend_from_within(start..end);
|
||||
done += end - start;
|
||||
start = end;
|
||||
}
|
||||
|
||||
left
|
||||
left - done
|
||||
}
|
||||
|
||||
fn read_dynamic_tables(&mut self) -> Option<(HuffmanTable, HuffmanTable)> {
|
||||
@@ -1292,6 +1296,52 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// PdfCraft patch: 300 zero bytes, zlib-compressed: one literal, then copies of distance 1 that
|
||||
/// are longer than their distance.
|
||||
const ZEROS_300: [u8; 13] = [0x78, 0xda, 0x63, 0x60, 0x18, 0x05, 0xc4, 0x02, 0x00, 0x01, 0x2c, 0x00, 0x01];
|
||||
|
||||
#[test]
|
||||
fn back_reference_longer_than_its_distance_repeats_its_source() {
|
||||
let mut state = flate::FlateState::new(&ZEROS_300);
|
||||
let mut out = Vec::new();
|
||||
state.decode_into(&ZEROS_300, &mut out, 1000);
|
||||
|
||||
assert_eq!(out, vec![0; 300]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn back_reference_cut_by_the_limit_resumes_in_the_next_call() {
|
||||
let mut state = flate::FlateState::new(&ZEROS_300);
|
||||
let mut out = Vec::new();
|
||||
state.decode_into(&ZEROS_300, &mut out, 100);
|
||||
assert_eq!(out, vec![0; 100]);
|
||||
|
||||
state.decode_into(&ZEROS_300, &mut out, 1000);
|
||||
assert_eq!(out, vec![0; 300]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn back_reference_reads_bytes_written_by_an_earlier_call() {
|
||||
// "abc" repeated 2000 times, zlib-compressed: copies reach 3 bytes back, and the calls below
|
||||
// stop every 7 bytes, so most copies start in an earlier call's output.
|
||||
const ABC_2000: [u8; 32] = [
|
||||
0x78, 0xda, 0xed, 0xc2, 0x41, 0x11, 0x00, 0x00, 0x0c, 0x02, 0xa0, 0xac, 0x6a, 0xff, 0x0e, 0x0b,
|
||||
0xb1, 0x2f, 0x1c, 0xe9, 0xa2, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xfe, 0x1e, 0x2f, 0x01, 0xf9, 0x59,
|
||||
];
|
||||
let expected = b"abc".repeat(2000);
|
||||
let mut state = flate::FlateState::new(&ABC_2000);
|
||||
let mut out = Vec::new();
|
||||
loop {
|
||||
let before = out.len();
|
||||
state.decode_into(&ABC_2000, &mut out, before + 7);
|
||||
if out.len() == before || out.len() >= expected.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(out, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn predictor_paeth() {
|
||||
predictor_test(
|
||||
|
||||
Reference in New Issue
Block a user