Skip to content

Commit 0c93ab9

Browse files
authored
Merge pull request #245
pdf-decode: reuse BitReader for packed samples
2 parents bcae0b1 + 4a94947 commit 0c93ab9

3 files changed

Lines changed: 48 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pdf-decode/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ workspace = true
99
[dependencies]
1010
num-traits = "0.2.19"
1111
pdf-object = { path = "../pdf-object" }
12+
pdf-utils = { path = "../pdf-utils" }
1213
thiserror = "2.0.12"

crates/pdf-decode/src/samples.rs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Sample code unpacking and normalization helpers.
22
33
use num_traits::ToPrimitive;
4+
use pdf_utils::BitReader;
45

56
use crate::{error::DecodeError, layout::SampleLayout};
67

@@ -161,7 +162,7 @@ fn decode_contiguous_samples(
161162
}
162163
_ => 0,
163164
}),
164-
_ => decode_packed_samples(data, bits_per_sample, sample_count, 0),
165+
_ => decode_packed_samples(data, bits_per_sample, sample_count),
165166
}
166167
}
167168

@@ -217,7 +218,6 @@ fn decode_row_aligned_samples(
217218
row_data,
218219
bits_per_sample,
219220
samples_per_row,
220-
0,
221221
)?);
222222
}
223223
}
@@ -245,39 +245,27 @@ fn decode_packed_samples(
245245
data: &[u8],
246246
bits_per_sample: usize,
247247
sample_count: usize,
248-
initial_bit_offset: usize,
249248
) -> Result<Vec<u32>, DecodeError> {
250249
let total_bits = sample_count.saturating_mul(bits_per_sample);
251-
let required_bits = initial_bit_offset.saturating_add(total_bits);
252-
let required_bytes = required_bits.div_ceil(8);
250+
let required_bytes = total_bits.div_ceil(8);
253251
ensure_len(data, required_bytes)?;
252+
let bit_width = u8::try_from(bits_per_sample)
253+
.map_err(|_| DecodeError::InvalidBitsPerSample { bits_per_sample })?;
254254

255+
let mut reader = BitReader::new(data);
255256
let mut out = Vec::with_capacity(sample_count);
256-
let mut bit_offset = initial_bit_offset;
257257
for _ in 0..sample_count {
258-
out.push(read_bits(data, bit_offset, bits_per_sample));
259-
bit_offset = bit_offset.saturating_add(bits_per_sample);
258+
let sample = reader
259+
.read_bits_u32(bit_width)
260+
.ok_or(DecodeError::InsufficientData {
261+
expected_bytes: required_bytes,
262+
actual_bytes: data.len(),
263+
})?;
264+
out.push(sample);
260265
}
261266
Ok(out)
262267
}
263268

264-
/// Reads one packed sample value from the input bit stream.
265-
fn read_bits(data: &[u8], bit_offset: usize, bits_per_sample: usize) -> u32 {
266-
let mut value = 0u32;
267-
268-
for bit_index in 0..bits_per_sample {
269-
let absolute_bit = bit_offset.saturating_add(bit_index);
270-
let byte_index = absolute_bit / 8;
271-
let bit_in_byte = absolute_bit % 8;
272-
let byte = data.get(byte_index).copied().unwrap_or_default();
273-
let bit = (byte >> (7usize.saturating_sub(bit_in_byte))) & 1;
274-
value <<= 1;
275-
value |= u32::from(bit);
276-
}
277-
278-
value
279-
}
280-
281269
#[cfg(test)]
282270
#[allow(clippy::unwrap_used, clippy::expect_used)]
283271
mod tests {
@@ -295,6 +283,25 @@ mod tests {
295283
assert_eq!(samples, vec![1, 0, 1, 1, 0, 0, 1, 0]);
296284
}
297285

286+
#[test]
287+
fn decode_contiguous_2_and_4_bit_samples() {
288+
let two_bit_samples = decode_sample_codes(
289+
&[0b00_01_10_11],
290+
2,
291+
SampleLayout::Contiguous { sample_count: 4 },
292+
)
293+
.unwrap();
294+
let four_bit_samples = decode_sample_codes(
295+
&[0x1F, 0xA0],
296+
4,
297+
SampleLayout::Contiguous { sample_count: 3 },
298+
)
299+
.unwrap();
300+
301+
assert_eq!(two_bit_samples, vec![0, 1, 2, 3]);
302+
assert_eq!(four_bit_samples, vec![1, 15, 10]);
303+
}
304+
298305
#[test]
299306
fn decode_contiguous_12_bit_samples() {
300307
let samples = decode_sample_codes(
@@ -483,6 +490,20 @@ mod tests {
483490
));
484491
}
485492

493+
#[test]
494+
fn decode_reports_truncated_packed_data() {
495+
let err = decode_sample_codes(&[0xFF], 12, SampleLayout::Contiguous { sample_count: 1 })
496+
.expect_err("12-bit sample requires two bytes");
497+
498+
assert!(matches!(
499+
err,
500+
DecodeError::InsufficientData {
501+
expected_bytes: 2,
502+
actual_bytes: 1
503+
}
504+
));
505+
}
506+
486507
#[test]
487508
fn decode_normalizes_samples() {
488509
let samples = decode_normalized_samples(&[0x00, 0xFF], 8, 2).unwrap();

0 commit comments

Comments
 (0)