otf_pixels_compress/lzw.rs
1//! LZW, in both the GIF and TIFF dialects.
2//!
3//! # One implementation, two dialects
4//!
5//! GIF (§Appendix F) and TIFF (§Section 13) both use variable-width LZW with a
6//! clear code and an end code, and they differ in exactly two ways:
7//!
8//! - **Bit order.** GIF packs codes least-significant-bit first; TIFF packs
9//! most-significant-bit first.
10//! - **When the code width grows.** GIF widens *after* the table reaches
11//! `2^width` entries; TIFF widens one code *early*, a documented quirk of
12//! the original implementation that every conforming decoder must match.
13//!
14//! Everything else — the table, the deferred-clear behaviour, the special
15//! first code — is identical. Two copies would be two chances to get the
16//! table logic wrong, with nothing testing that they agree, so this is one
17//! implementation with the differences named as [`BitOrder`].
18//!
19//! # The KwKwK case
20//!
21//! LZW's one genuinely subtle case: a code can refer to an entry that is only
22//! being defined by this very code. It happens whenever the input contains
23//! `KwKwK` — a sequence whose second occurrence begins before its first
24//! finished being added. A decoder that looks the code up naively finds
25//! nothing and either errors or reads uninitialized memory. It is handled
26//! explicitly below, and tested directly, because it is rare enough in small
27//! fixtures to escape casual testing and common enough in real images to break
28//! immediately in production.
29
30use crate::{Error, Result};
31
32/// Which end of a byte codes are packed from.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum BitOrder {
35 /// Least-significant bit first, as GIF packs codes.
36 LsbFirst,
37 /// Most-significant bit first, as TIFF packs codes.
38 ///
39 /// TIFF also widens the code one entry early; the two go together in
40 /// practice, so [`BitOrder`] selects both.
41 MsbFirst,
42}
43
44impl BitOrder {
45 /// Whether this dialect increases the code width one code early.
46 const fn early_change(self) -> bool {
47 matches!(self, Self::MsbFirst)
48 }
49}
50
51/// The largest code width either dialect permits.
52const MAX_WIDTH: u32 = 12;
53
54/// The table size at which a decoder moves to the next code width.
55///
56/// TIFF widens one entry early — a documented quirk of the original
57/// implementation that every conforming decoder must reproduce.
58const fn widen_at(width: u32, order: BitOrder) -> u16 {
59 let full = 1_u16 << width;
60 if order.early_change() {
61 full.saturating_sub(1)
62 } else {
63 full
64 }
65}
66/// The largest table any dialect can reach.
67const MAX_CODES: usize = 1 << MAX_WIDTH;
68
69/// Reads variable-width codes from a byte stream.
70#[derive(Debug)]
71struct CodeReader<'a> {
72 data: &'a [u8],
73 position: usize,
74 bits: u32,
75 count: u32,
76 order: BitOrder,
77}
78
79impl<'a> CodeReader<'a> {
80 const fn new(data: &'a [u8], order: BitOrder) -> Self {
81 Self {
82 data,
83 position: 0,
84 bits: 0,
85 count: 0,
86 order,
87 }
88 }
89
90 /// Read one code of `width` bits, or `None` at end of input.
91 fn next(&mut self, width: u32) -> Option<u16> {
92 while self.count < width {
93 let &byte = self.data.get(self.position)?;
94 self.position += 1;
95 match self.order {
96 BitOrder::LsbFirst => self.bits |= u32::from(byte) << self.count,
97 BitOrder::MsbFirst => self.bits = (self.bits << 8) | u32::from(byte),
98 }
99 self.count += 8;
100 }
101 let code = match self.order {
102 BitOrder::LsbFirst => {
103 let mask = (1_u32 << width) - 1;
104 let value = self.bits & mask;
105 self.bits >>= width;
106 self.count -= width;
107 value
108 }
109 BitOrder::MsbFirst => {
110 let shift = self.count - width;
111 let value = (self.bits >> shift) & ((1_u32 << width) - 1);
112 self.count -= width;
113 // Keep only the bits still unconsumed, so `bits` cannot grow
114 // past 32 on a long run of whole bytes.
115 self.bits &= (1_u32 << self.count).wrapping_sub(1);
116 value
117 }
118 };
119 Some(code as u16)
120 }
121}
122
123/// Writes variable-width codes to a byte stream.
124#[derive(Debug)]
125struct CodeWriter {
126 out: Vec<u8>,
127 bits: u32,
128 count: u32,
129 order: BitOrder,
130}
131
132impl CodeWriter {
133 const fn new(order: BitOrder) -> Self {
134 Self {
135 out: Vec::new(),
136 bits: 0,
137 count: 0,
138 order,
139 }
140 }
141
142 fn push(&mut self, code: u16, width: u32) {
143 match self.order {
144 BitOrder::LsbFirst => {
145 self.bits |= u32::from(code) << self.count;
146 self.count += width;
147 while self.count >= 8 {
148 self.out.push((self.bits & 0xFF) as u8);
149 self.bits >>= 8;
150 self.count -= 8;
151 }
152 }
153 BitOrder::MsbFirst => {
154 self.bits = (self.bits << width) | u32::from(code);
155 self.count += width;
156 while self.count >= 8 {
157 let shift = self.count - 8;
158 self.out.push(((self.bits >> shift) & 0xFF) as u8);
159 self.count -= 8;
160 self.bits &= (1_u32 << self.count).wrapping_sub(1);
161 }
162 }
163 }
164 }
165
166 /// Flush any partial byte and return the stream.
167 fn finish(mut self) -> Vec<u8> {
168 if self.count > 0 {
169 let byte = match self.order {
170 BitOrder::LsbFirst => (self.bits & 0xFF) as u8,
171 // Pad with zeroes on the right, which is what both
172 // specifications' encoders emit.
173 BitOrder::MsbFirst => ((self.bits << (8 - self.count)) & 0xFF) as u8,
174 };
175 self.out.push(byte);
176 }
177 self.out
178 }
179}
180
181/// Decompresses an LZW stream.
182#[derive(Debug)]
183pub struct LzwDecoder {
184 order: BitOrder,
185 minimum_width: u32,
186}
187
188impl LzwDecoder {
189 /// A decoder for `order`, with `minimum_width` bits per initial code.
190 ///
191 /// GIF carries the minimum width in the image block; TIFF fixes it at 8.
192 ///
193 /// # Errors
194 ///
195 /// Returns [`Error`] if `minimum_width` is outside 2..=11. A width of 1 has
196 /// no room for both the clear and end codes, and 12 leaves none for the
197 /// table.
198 pub fn new(order: BitOrder, minimum_width: u32) -> Result<Self> {
199 if !(2..=11).contains(&minimum_width) {
200 return Err(Error::malformed(
201 "lzw",
202 format!("minimum code width {minimum_width} is outside 2..=11"),
203 ));
204 }
205 Ok(Self {
206 order,
207 minimum_width,
208 })
209 }
210
211 /// A decoder for GIF's dialect at `minimum_width`.
212 ///
213 /// # Errors
214 ///
215 /// As [`LzwDecoder::new`].
216 pub fn gif(minimum_width: u32) -> Result<Self> {
217 Self::new(BitOrder::LsbFirst, minimum_width)
218 }
219
220 /// A decoder for TIFF's dialect, whose minimum width is always 8.
221 #[must_use]
222 pub const fn tiff() -> Self {
223 Self {
224 order: BitOrder::MsbFirst,
225 minimum_width: 8,
226 }
227 }
228
229 /// Decompress `data`, refusing to produce more than `limit` bytes.
230 ///
231 /// The bound is what makes a decompression bomb a malformed-input error
232 /// rather than an allocation the caller has to survive: LZW expands by up
233 /// to 4096:1, so a caller states what it is prepared to accept.
234 ///
235 /// # Errors
236 ///
237 /// Returns [`Error`] for an invalid code, a stream that ends mid-symbol
238 /// without an end code, or output exceeding `limit`.
239 pub fn decode(&self, data: &[u8], limit: usize) -> Result<Vec<u8>> {
240 let clear = 1_u16 << self.minimum_width;
241 let end = clear + 1;
242 // Codes below this are literals. It is *not* 256: GIF derives the
243 // minimum width from the palette size, so a 4-colour image has
244 // literals 0..=3 and its table starts at code 6. Assuming 256 here
245 // decodes small-palette images as gibberish while leaving 8-bit ones
246 // perfect, which is exactly the bug that survives casual testing.
247 let literals = clear;
248
249 let mut reader = CodeReader::new(data, self.order);
250 let mut out: Vec<u8> = Vec::new();
251
252 // The table stores each entry as (prefix code, final byte), so an
253 // entry costs four bytes instead of a growing Vec. Expanding walks the
254 // prefix chain backwards, which is why `scratch` is reversed after.
255 let mut prefix = vec![0_u16; MAX_CODES];
256 let mut suffix = vec![0_u8; MAX_CODES];
257 let mut scratch: Vec<u8> = Vec::with_capacity(MAX_CODES);
258
259 let mut width = self.minimum_width + 1;
260 let mut next = end + 1;
261 let mut previous: Option<u16> = None;
262
263 // Running out of input without an end code is common enough in real
264 // files — many encoders omit it — that treating it as malformed would
265 // reject images every other decoder accepts. So the loop ends on
266 // either an end code or exhausted input, and both are success.
267 while let Some(code) = reader.next(width) {
268 if code == clear {
269 width = self.minimum_width + 1;
270 next = end + 1;
271 previous = None;
272 continue;
273 }
274 if code == end {
275 break;
276 }
277
278 // Expand this code into `scratch`, handling the KwKwK case where
279 // the code being read is the one this step is about to define.
280 scratch.clear();
281 let first = if code < next {
282 expand(code, literals, &prefix, &suffix, &mut scratch)?
283 } else if code == next {
284 let Some(previous_code) = previous else {
285 return Err(Error::malformed(
286 "lzw",
287 "the first code after a clear cannot be a forward reference",
288 ));
289 };
290 // KwKwK: the entry is `previous` followed by its own first
291 // byte, which is exactly what this branch reconstructs.
292 let first = expand(previous_code, literals, &prefix, &suffix, &mut scratch)?;
293 scratch.push(first);
294 first
295 } else {
296 return Err(Error::malformed(
297 "lzw",
298 format!("code {code} is beyond the {next} entries defined so far"),
299 ));
300 };
301
302 if out.len() + scratch.len() > limit {
303 return Err(Error::malformed(
304 "lzw",
305 format!("stream expands beyond the {limit} byte limit"),
306 ));
307 }
308 out.extend_from_slice(&scratch);
309
310 // Define the new entry: the previous string plus this one's first
311 // byte. Nothing is defined for the first code after a clear.
312 let room = (next as usize) < MAX_CODES;
313 if let Some(previous_code) = previous.filter(|_| room) {
314 if let (Some(p), Some(s)) =
315 (prefix.get_mut(next as usize), suffix.get_mut(next as usize))
316 {
317 *p = previous_code;
318 *s = first;
319 }
320 next += 1;
321 if next >= widen_at(width, self.order) && width < MAX_WIDTH {
322 width += 1;
323 }
324 }
325 previous = Some(code);
326 }
327
328 Ok(out)
329 }
330}
331
332/// Expand `code` into `out`, returning the first byte of the expansion.
333///
334/// `literals` is the number of single-byte codes, which is `2^minimum_width`
335/// and not necessarily 256. Walking the prefix chain backwards and reversing
336/// is why the length is bounded by the table size rather than by the output.
337fn expand(
338 code: u16,
339 literals: u16,
340 prefix: &[u16],
341 suffix: &[u8],
342 out: &mut Vec<u8>,
343) -> Result<u8> {
344 let start = out.len();
345 let mut current = code;
346 // A corrupt table could in principle form a cycle; the table size is a
347 // hard bound on any legitimate chain, so exceeding it is malformed input.
348 for _ in 0..=MAX_CODES {
349 if current < literals {
350 out.push(current as u8);
351 let Some(slice) = out.get_mut(start..) else {
352 break;
353 };
354 slice.reverse();
355 return Ok(current as u8);
356 }
357 let index = current as usize;
358 let (Some(&byte), Some(&parent)) = (suffix.get(index), prefix.get(index)) else {
359 return Err(Error::malformed("lzw", "code refers outside the table"));
360 };
361 out.push(byte);
362 current = parent;
363 }
364 Err(Error::malformed("lzw", "code chain does not terminate"))
365}
366
367/// Compresses to an LZW stream.
368#[derive(Debug)]
369pub struct LzwEncoder {
370 order: BitOrder,
371 minimum_width: u32,
372}
373
374impl LzwEncoder {
375 /// An encoder for `order`, with `minimum_width` bits per initial code.
376 ///
377 /// # Errors
378 ///
379 /// As [`LzwDecoder::new`].
380 pub fn new(order: BitOrder, minimum_width: u32) -> Result<Self> {
381 if !(2..=11).contains(&minimum_width) {
382 return Err(Error::malformed(
383 "lzw",
384 format!("minimum code width {minimum_width} is outside 2..=11"),
385 ));
386 }
387 Ok(Self {
388 order,
389 minimum_width,
390 })
391 }
392
393 /// An encoder for GIF's dialect at `minimum_width`.
394 ///
395 /// # Errors
396 ///
397 /// As [`LzwDecoder::new`].
398 pub fn gif(minimum_width: u32) -> Result<Self> {
399 Self::new(BitOrder::LsbFirst, minimum_width)
400 }
401
402 /// An encoder for TIFF's dialect.
403 #[must_use]
404 pub const fn tiff() -> Self {
405 Self {
406 order: BitOrder::MsbFirst,
407 minimum_width: 8,
408 }
409 }
410
411 /// Compress `data`.
412 ///
413 /// The output always begins with a clear code and ends with an end code,
414 /// which every conforming decoder accepts and some require.
415 #[must_use]
416 pub fn encode(&self, data: &[u8]) -> Vec<u8> {
417 let clear = 1_u16 << self.minimum_width;
418 let end = clear + 1;
419 let mut writer = CodeWriter::new(self.order);
420 let mut width = self.minimum_width + 1;
421
422 // The dictionary maps (prefix code, next byte) to a code. A flat map
423 // over `prefix * 256 + byte` would be 1 MB; a hash map of the pairs
424 // actually used is smaller and no slower at these sizes.
425 let mut table: std::collections::HashMap<(u16, u8), u16> = std::collections::HashMap::new();
426 let mut next = end + 1;
427
428 writer.push(clear, width);
429
430 let mut current: Option<u16> = None;
431 for &byte in data {
432 let combined = match current {
433 None => {
434 current = Some(u16::from(byte));
435 continue;
436 }
437 Some(code) => (code, byte),
438 };
439
440 if let Some(&found) = table.get(&combined) {
441 current = Some(found);
442 continue;
443 }
444
445 // Emit what we had, then define the extended string.
446 if let Some(code) = current {
447 writer.push(code, width);
448 }
449 if (next as usize) < MAX_CODES {
450 table.insert(combined, next);
451 next += 1;
452 // Strictly greater, where the decoder uses "at least". The
453 // asymmetry is not a choice: a decoder cannot define an entry
454 // until it has read the *following* code and knows its first
455 // byte, so its table is one behind the encoder's at the moment
456 // the width is decided. Using the same comparison on both
457 // sides makes an encoder and decoder agree with each other and
458 // disagree with the rest of the world — which passes every
459 // round-trip test and fails on the first real image.
460 if next > widen_at(width, self.order) && width < MAX_WIDTH {
461 width += 1;
462 }
463 } else {
464 // The table is full: reset, exactly as the decoder will when
465 // it sees the clear code.
466 writer.push(clear, width);
467 table.clear();
468 width = self.minimum_width + 1;
469 next = end + 1;
470 }
471 current = Some(u16::from(byte));
472 }
473
474 if let Some(code) = current {
475 writer.push(code, width);
476 }
477 writer.push(end, width);
478 writer.finish()
479 }
480}
481
482#[cfg(test)]
483#[allow(
484 clippy::unwrap_used,
485 clippy::expect_used,
486 clippy::indexing_slicing,
487 clippy::panic,
488 reason = "tests operate on known-good values and assert shapes directly"
489)]
490mod tests {
491 use super::*;
492
493 fn round_trip(order: BitOrder, width: u32, data: &[u8]) {
494 let encoder = LzwEncoder::new(order, width).unwrap();
495 let decoder = LzwDecoder::new(order, width).unwrap();
496 let compressed = encoder.encode(data);
497 let back = decoder
498 .decode(&compressed, data.len() * 4 + 1024)
499 .unwrap_or_else(|e| panic!("{order:?} width {width}: {e}"));
500 assert_eq!(back, data, "{order:?} width {width} did not round-trip");
501 }
502
503 #[test]
504 fn both_dialects_round_trip_everything() {
505 let cases: Vec<Vec<u8>> = vec![
506 Vec::new(),
507 vec![0],
508 vec![1, 2, 3],
509 vec![7; 1000],
510 b"the quick brown fox jumps over the lazy dog. ".repeat(60),
511 (0..=255_u8).cycle().take(5000).collect(),
512 (0..4000).map(|i| ((i * 37) % 251) as u8).collect(),
513 ];
514 for order in [BitOrder::LsbFirst, BitOrder::MsbFirst] {
515 for data in &cases {
516 round_trip(order, 8, data);
517 }
518 }
519 }
520
521 #[test]
522 fn every_gif_minimum_width_round_trips() {
523 // GIF carries the minimum width per image, derived from the palette
524 // size, so every legal width has to work.
525 for width in 2..=8_u32 {
526 // The alphabet is 2^width, which is 256 at width 8 and therefore
527 // does not fit a u8 — compute it wider and narrow the result.
528 let alphabet = 1_u16 << width;
529 let data: Vec<u8> = (0..3000).map(|i| ((i * 7) % alphabet) as u8).collect();
530 round_trip(BitOrder::LsbFirst, width, &data);
531 }
532 }
533
534 #[test]
535 fn the_kwkwk_case_decodes_correctly() {
536 // The one genuinely subtle case in LZW: a code referring to the entry
537 // this very step defines. It needs input whose second occurrence of a
538 // run begins before the first finished being added — a long run of one
539 // byte is the shortest way to force it.
540 for order in [BitOrder::LsbFirst, BitOrder::MsbFirst] {
541 for length in [3_usize, 4, 5, 10, 100, 4000] {
542 let data = vec![0xAB_u8; length];
543 round_trip(order, 8, &data);
544 }
545 // The classic textbook case, spelled out.
546 round_trip(order, 8, b"ababababab");
547 round_trip(order, 8, b"aaaaaaaaaaaaaaaa");
548 }
549 }
550
551 #[test]
552 fn a_stream_longer_than_the_table_resets_cleanly() {
553 // Past 4096 entries the encoder must emit a clear and start again, and
554 // the decoder must follow it. Random-ish data fills the table fastest.
555 for order in [BitOrder::LsbFirst, BitOrder::MsbFirst] {
556 let data: Vec<u8> = (0..200_000_u32)
557 .map(|i| (i.wrapping_mul(2_654_435_761) >> 24) as u8)
558 .collect();
559 round_trip(order, 8, &data);
560 }
561 }
562
563 #[test]
564 fn a_forward_reference_is_an_error_not_a_panic() {
565 // A code beyond the table is the classic corrupt-LZW case, and the
566 // one that reads uninitialized memory in a careless implementation.
567 let decoder = LzwDecoder::gif(8).unwrap();
568 // 0x100 is clear at width 9; 0x1FF is far beyond anything defined.
569 let mut writer = CodeWriter::new(BitOrder::LsbFirst);
570 writer.push(0x100, 9);
571 writer.push(0x1FF, 9);
572 let stream = writer.finish();
573 let error = decoder.decode(&stream, 1 << 20).unwrap_err();
574 assert!(error.detail().contains("beyond"), "{error}");
575 }
576
577 #[test]
578 fn arbitrary_bytes_never_panic() {
579 // Every LZW stream a codec sees is attacker-controlled.
580 let mut seed = 0x1234_5678_u32;
581 for order in [BitOrder::LsbFirst, BitOrder::MsbFirst] {
582 for width in [2_u32, 8, 11] {
583 let decoder = LzwDecoder::new(order, width).unwrap();
584 for _ in 0..500 {
585 let len = (seed % 200) as usize + 1;
586 let data: Vec<u8> = (0..len)
587 .map(|_| {
588 seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
589 (seed >> 16) as u8
590 })
591 .collect();
592 let _ = decoder.decode(&data, 1 << 16);
593 }
594 }
595 }
596 }
597
598 #[test]
599 fn every_truncation_of_a_valid_stream_is_an_error_or_a_short_read() {
600 // Truncation must never panic. It may legitimately produce a partial
601 // decode, because a missing end code is tolerated.
602 let data = b"the quick brown fox. ".repeat(40);
603 let stream = LzwEncoder::gif(8).unwrap().encode(&data);
604 let decoder = LzwDecoder::gif(8).unwrap();
605 for cut in 0..stream.len() {
606 let _ = decoder.decode(&stream[..cut], 1 << 20);
607 }
608 }
609
610 #[test]
611 fn the_output_limit_is_enforced() {
612 // LZW expands by up to 4096:1, so the bound is what stands between a
613 // 2 KB file and a gigabyte of memory.
614 let data = vec![0_u8; 1_000_000];
615 let stream = LzwEncoder::gif(8).unwrap().encode(&data);
616 assert!(stream.len() < 100_000, "the bomb should be small");
617 let error = LzwDecoder::gif(8)
618 .unwrap()
619 .decode(&stream, 4096)
620 .unwrap_err();
621 assert!(error.detail().contains("limit"), "{error}");
622 }
623
624 #[test]
625 fn an_out_of_range_minimum_width_is_rejected() {
626 for width in [0_u32, 1, 12, 99] {
627 assert!(
628 LzwDecoder::new(BitOrder::LsbFirst, width).is_err(),
629 "{width}"
630 );
631 assert!(
632 LzwEncoder::new(BitOrder::LsbFirst, width).is_err(),
633 "{width}"
634 );
635 }
636 }
637
638 #[test]
639 fn a_clear_code_resets_the_table_mid_stream() {
640 // Decoders must follow an encoder that clears early, which some do to
641 // bound their own memory.
642 let mut writer = CodeWriter::new(BitOrder::LsbFirst);
643 writer.push(0x100, 9); // clear
644 writer.push(b'A'.into(), 9);
645 writer.push(b'B'.into(), 9);
646 writer.push(0x100, 9); // clear again
647 writer.push(b'C'.into(), 9);
648 writer.push(0x101, 9); // end
649 let stream = writer.finish();
650 let out = LzwDecoder::gif(8).unwrap().decode(&stream, 1024).unwrap();
651 assert_eq!(out, b"ABC");
652 }
653
654 #[test]
655 fn the_two_dialects_pack_bits_differently() {
656 // If both dialects produced the same bytes, one of them would be
657 // wrong, and every round-trip test above would still pass.
658 let data = b"hello world, this is a test of bit ordering".repeat(4);
659 let lsb = LzwEncoder::new(BitOrder::LsbFirst, 8)
660 .unwrap()
661 .encode(&data);
662 let msb = LzwEncoder::new(BitOrder::MsbFirst, 8)
663 .unwrap()
664 .encode(&data);
665 assert_ne!(lsb, msb, "the two bit orders produced identical streams");
666 }
667}