gistools/util/compression/
lzw.rs

1use alloc::{vec, vec::Vec};
2use core::ops::Div;
3
4const LZW_MIN_BITS: usize = 9;
5const LZW_CLEAR_CODE: usize = 256; // clear code
6const LZW_EOI_CODE: usize = 257; // end of information
7const LZW_MAX_BYTELENGTH: usize = 12;
8
9/// Get a byte from an array
10///
11/// ## Parameters
12/// - `array`: The array to read the byte from
13/// - `position`: The position to read the byte from
14/// - `length`: The length of the byte
15///
16/// ## Returns
17/// The byte
18fn lzw_get_byte(array: &[u8], position: usize, length: usize) -> usize {
19    let d = position % 8;
20    let a = position.div(8);
21    let de = 8 - d;
22    let ef = position + length - (a + 1) * 8;
23    let fg: isize = 8 * (a + 2) as isize - (position + length) as isize;
24    let dg = (a + 2) * 8 - position;
25    let fg = isize::max(0, fg) as usize;
26    if a >= array.len() {
27        // panic!("ran off the end of the buffer before finding LZW_EOI_CODE (end on input code)");
28        return LZW_EOI_CODE;
29    }
30    let mut chunk1 = (array[a] as usize) & (2_usize.pow(8 - (d as u32)) - 1);
31    chunk1 <<= length - de;
32    let mut chunks = chunk1;
33    if a + 1 < array.len() {
34        let mut chunk2 = (array[a + 1] as usize) >> fg;
35        let chunk2_shift = isize::max(0, (length as isize) - (dg as isize)) as usize;
36        chunk2 <<= chunk2_shift;
37        chunks += chunk2;
38    }
39    if ef > 8 && a + 2 < array.len() {
40        let hi = (a + 3) * 8 - (position + length);
41        let chunk3 = (array[a + 2] as usize) >> hi;
42        chunks += chunk3;
43    }
44
45    chunks
46}
47
48/// Append an array in reverse
49///
50/// ## Parameters
51/// - `dest`: The array to append to
52/// - `source`: The array to append
53///
54/// ## Returns
55/// The dest array
56fn append_reversed(dest: &mut Vec<u8>, source: &[u8]) {
57    for i in (0..source.len()).rev() {
58        dest.push(source[i]);
59    }
60}
61
62struct LZWDecoder {
63    /// The dictionary index
64    pub dictionary_index: Vec<u16>,
65    /// The dictionary chars
66    pub dictionary_char: Vec<u8>,
67    /// The dictionary length
68    pub dictionary_length: usize,
69    /// The byte length
70    pub byte_length: usize,
71    /// The position
72    pub position: usize,
73}
74impl LZWDecoder {
75    /// Create a new LZWDecoder
76    pub fn new() -> LZWDecoder {
77        let mut entry = LZWDecoder {
78            dictionary_index: vec![0_u16; 4093],
79            dictionary_char: vec![0_u8; 4093],
80            dictionary_length: 258,
81            byte_length: LZW_MIN_BITS,
82            position: 0,
83        };
84
85        for i in 0..=257 {
86            entry.dictionary_index[i] = 4096;
87            entry.dictionary_char[i] = i as u8;
88        }
89
90        entry
91    }
92
93    /// Initializes the dictionary
94    pub fn init_dictionary(&mut self) {
95        self.dictionary_length = 258;
96        self.byte_length = LZW_MIN_BITS;
97    }
98
99    /// Go Next
100    pub fn get_next(&mut self, array: &[u8]) -> usize {
101        let byte = lzw_get_byte(array, self.position, self.byte_length);
102        self.position += self.byte_length;
103        byte
104    }
105
106    /// Add to the dictionary
107    pub fn add_to_dictionary(&mut self, i: usize, c: u8) -> usize {
108        self.dictionary_char[self.dictionary_length] = c;
109        self.dictionary_index[self.dictionary_length] = i as u16;
110        self.dictionary_length += 1;
111        self.dictionary_length - 1
112    }
113
114    /// Get the dictionary reversed
115    pub fn get_dictionary_reversed(&self, n: usize) -> Vec<u8> {
116        let mut rev = vec![];
117        let mut i = n;
118        while i != 4096 {
119            rev.push(self.dictionary_char[i]);
120            i = self.dictionary_index[i] as usize;
121        }
122        rev
123    }
124}
125
126/// Decompress the LZW data
127///
128/// ## Parameters
129/// - `input`: The LZW data
130///
131/// ## Returns
132/// The decompressed data
133pub fn decompress_lzw(input: &[u8]) -> Vec<u8> {
134    let mut entry = LZWDecoder::new();
135
136    let mut result = vec![];
137    entry.init_dictionary();
138    let mut code = entry.get_next(input);
139    let mut old_code: Option<usize> = None;
140    while code != LZW_EOI_CODE {
141        if code == LZW_CLEAR_CODE {
142            entry.init_dictionary();
143            code = entry.get_next(input);
144            while code == LZW_CLEAR_CODE {
145                code = entry.get_next(input);
146            }
147
148            if code == LZW_EOI_CODE {
149                break;
150            } else if code > LZW_CLEAR_CODE {
151                panic!("corrupted code at scanline {code}");
152            } else {
153                let val = entry.get_dictionary_reversed(code);
154                append_reversed(&mut result, &val);
155                old_code = Some(code);
156            }
157        } else if code < entry.dictionary_length {
158            let val = entry.get_dictionary_reversed(code);
159            append_reversed(&mut result, &val);
160            entry.add_to_dictionary(old_code.unwrap_or(code), val[val.len() - 1]);
161            old_code = Some(code);
162        } else {
163            let old_val = entry.get_dictionary_reversed(old_code.unwrap_or(code));
164            if old_val.is_empty() {
165                panic!(
166                    "Bogus entry. Not in dictionary, {:?} / {}, position: {}",
167                    old_code, entry.dictionary_length, entry.position
168                );
169            }
170            append_reversed(&mut result, &old_val);
171            result.push(old_val[old_val.len() - 1]);
172            entry.add_to_dictionary(old_code.unwrap_or(code), old_val[old_val.len() - 1]);
173            old_code = Some(code);
174        }
175
176        let two_pow = 2_usize.pow(entry.byte_length as u32);
177        if entry.dictionary_length + 1 >= two_pow {
178            if entry.byte_length == LZW_MAX_BYTELENGTH {
179                old_code = None;
180            } else {
181                entry.byte_length += 1;
182            }
183        }
184        code = entry.get_next(input);
185    }
186    result
187}