gistools/util/compression/
lzw.rs1use alloc::{vec, vec::Vec};
2use core::ops::Div;
3
4const LZW_MIN_BITS: usize = 9;
5const LZW_CLEAR_CODE: usize = 256; const LZW_EOI_CODE: usize = 257; const LZW_MAX_BYTELENGTH: usize = 12;
8
9fn 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 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
48fn 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 pub dictionary_index: Vec<u16>,
65 pub dictionary_char: Vec<u8>,
67 pub dictionary_length: usize,
69 pub byte_length: usize,
71 pub position: usize,
73}
74impl LZWDecoder {
75 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 pub fn init_dictionary(&mut self) {
95 self.dictionary_length = 258;
96 self.byte_length = LZW_MIN_BITS;
97 }
98
99 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 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 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
126pub 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}