Skip to content

Commit cb884f0

Browse files
authored
Merge pull request #76 from image-rs/no-progress-with-small-buffer
Fix NoProgress indication despite buffered code
2 parents b0c1dfd + 56d0f24 commit cb884f0

3 files changed

Lines changed: 47 additions & 2 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,9 @@ required-features = ["alloc"]
9797
name = "fuzz_panic_regression"
9898
required-features = ["alloc"]
9999

100+
[[test]]
101+
name = "progress_into_buffer"
102+
required-features = ["alloc"]
103+
100104
[package.metadata.docs.rs]
101105
all-features = true

src/decode.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ struct Buffer {
179179
bytes: Box<[u8]>,
180180
read_mark: usize,
181181
write_mark: usize,
182+
/// Co-operatively used to track if decoding progress was written to the buffer, not `out`.
183+
pub(crate) reconstructed_another_code: bool,
182184
}
183185

184186
/// Mask for indexing into fixed-size arrays. Since MAX_ENTRIES = 4096 = 2^12,
@@ -795,6 +797,9 @@ impl<C: CodeBuffer, CgC: CodegenConstants> Stateful for DecodeState<C, CgC> {
795797
// The status, which is written to on an invalid code.
796798
let mut status = Ok(LzwStatus::Ok);
797799

800+
// Reset if a value was restored into the buffer.
801+
self.buffer.reconstructed_another_code = false;
802+
798803
match self.last.take() {
799804
// No last state? This is the first code after a reset?
800805
None => {
@@ -1161,8 +1166,11 @@ impl<C: CodeBuffer, CgC: CodegenConstants> Stateful for DecodeState<C, CgC> {
11611166
}
11621167

11631168
// Ensure we don't indicate that no progress was made if we read some bytes from the input
1164-
// (which is progress).
1165-
if o_in > inp.len() {
1169+
// (which is progress). The field `new_value` will have been written if any reconstruction
1170+
// was buffered. This is mostly a fail-safe in case the above loop does nothing else but
1171+
// buffer the value, without consuming any of the bytes. (It really should right now but
1172+
// that may change and it's not structurally required to).
1173+
if o_in > inp.len() || self.buffer.reconstructed_another_code {
11661174
if let Ok(LzwStatus::NoProgress) = status {
11671175
status = Ok(LzwStatus::Ok);
11681176
}
@@ -1403,6 +1411,7 @@ impl Buffer {
14031411
bytes: vec![0; MAX_ENTRIES].into_boxed_slice(),
14041412
read_mark: 0,
14051413
write_mark: 0,
1414+
reconstructed_another_code: false,
14061415
}
14071416
}
14081417

@@ -1412,6 +1421,7 @@ impl Buffer {
14121421
/// with the reconstruction of `B`.
14131422
fn fill_cscsc(&mut self) -> u8 {
14141423
self.bytes[self.write_mark] = self.bytes[0];
1424+
self.reconstructed_another_code = true;
14151425
self.write_mark += 1;
14161426
self.read_mark = 0;
14171427
self.bytes[0]
@@ -1425,6 +1435,7 @@ impl Buffer {
14251435
let mut memory = core::mem::replace(&mut self.bytes, Box::default());
14261436

14271437
let out = &mut memory[..usize::from(depth)];
1438+
self.reconstructed_another_code = true;
14281439
let last = table.reconstruct(code, out);
14291440

14301441
self.bytes = memory;

tests/progress_into_buffer.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use weezl::{decode::Configuration, encode, BitOrder, LzwStatus};
2+
3+
#[test]
4+
fn fill_into_buffer_counts_as_progress() {
5+
let data = vec![42u8; 8];
6+
let encoded = encode::Encoder::new(BitOrder::Lsb, 8)
7+
.encode(&data)
8+
.unwrap();
9+
10+
let mut dec = Configuration::new(BitOrder::Lsb, 8)
11+
.with_yield_on_full_buffer(true)
12+
.build();
13+
14+
let mut result = Vec::new();
15+
let mut inp = encoded.as_slice();
16+
loop {
17+
let mut tmp = [0u8; 1]; // 1-byte output buffer
18+
let r = dec.decode_bytes(inp, &mut tmp);
19+
inp = &inp[r.consumed_in..];
20+
result.extend_from_slice(&tmp[..r.consumed_out]);
21+
match r.status {
22+
Ok(LzwStatus::Done) => break,
23+
Ok(LzwStatus::NoProgress) if r.consumed_in == 0 && r.consumed_out == 0 => break,
24+
Ok(_) => {}
25+
Err(_) => break,
26+
}
27+
}
28+
29+
assert_eq!(result.len(), 8); // FAILS: result.len() == 1
30+
}

0 commit comments

Comments
 (0)