Skip to content

Commit 946a410

Browse files
implementing lzss decode into a decoder class
The objective of this implementation is to provide a more versatile way of handling decompression of lzss streams of data. The main reason is to allow streaming decompression
1 parent b7d5a9f commit 946a410

File tree

2 files changed

+208
-0
lines changed

2 files changed

+208
-0
lines changed

src/decompress/lzss.cpp

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,136 @@ void lzss_decode(void)
161161
}
162162
}
163163
}
164+
165+
166+
/**************************************************************************************
167+
LZSS DECODER CLASS IMPLEMENTATION
168+
**************************************************************************************/
169+
170+
// get the number of bits the algorithm will try to get given the state
171+
int LZSSDecoder::bits_required(LZSSDecoder::FSM_STATES s) {
172+
switch(s) {
173+
case FSM_0:
174+
return 1;
175+
case FSM_1:
176+
return 8;
177+
case FSM_2:
178+
return EI;
179+
case FSM_3:
180+
return EJ;
181+
}
182+
}
183+
184+
LZSSDecoder::LZSSDecoder(std::function<void(const uint8_t)> putc_cbk)
185+
: put_char_cbk(putc_cbk), state(FSM_0), available_bits(0) {
186+
for (int i = 0; i < N - F; i++) buffer[i] = ' ';
187+
r = N - F;
188+
}
189+
190+
LZSSDecoder::status LZSSDecoder::handle_state() {
191+
LZSSDecoder::status res = IN_PROGRESS;
192+
193+
int c = getbit(bits_required(this->state));
194+
195+
if(c == LZSS_BUFFER_EMPTY) {
196+
res = NOT_COMPLETED;
197+
} else if (c == LZSS_EOF) {
198+
res = DONE;
199+
this->state = FSM_EOF;
200+
} else {
201+
switch(this->state) {
202+
case FSM_0:
203+
if(c) {
204+
this->state = FSM_1;
205+
} else {
206+
this->state = FSM_2;
207+
}
208+
break;
209+
case FSM_1:
210+
putc(c);
211+
buffer[r++] = c;
212+
r &= (N - 1); // equivalent to r = r % N
213+
214+
this->state = FSM_0;
215+
break;
216+
case FSM_2:
217+
this->i = c;
218+
this->state = FSM_3;
219+
break;
220+
case FSM_3: {
221+
int j = c;
222+
for (int k = 0; k <= j + 1; k++) { // TODO improve by using memcpy
223+
c = buffer[(this->i + k) & (N - 1)]; // equivalent to buffer[(i+k) % N]
224+
putc(c);
225+
buffer[r++] = c; r &= (N - 1); // equivalent to r = r % N
226+
}
227+
this->state = FSM_0;
228+
229+
break;
230+
}
231+
}
232+
}
233+
234+
return res;
235+
}
236+
237+
LZSSDecoder::status LZSSDecoder::decompress(const char* buffer, uint32_t size) {
238+
this->in_buffer = (uint8_t*)buffer;
239+
240+
// I won't ever exceed the (2^32)-1 value, since It is impossible to have a buffer of 512MB on an embedded dev
241+
// thus this value fits a 32 bit uint
242+
this->available_bits += size*8;
243+
status res = IN_PROGRESS;
244+
245+
while((res = handle_state()) == IN_PROGRESS);
246+
247+
if(res == NOT_COMPLETED && this->available_bits > 0) {
248+
this->save_excess();
249+
}
250+
251+
return res;
252+
}
253+
254+
int LZSSDecoder::getbit(int n) { // get n bits from buffer
255+
256+
// check that we have all the available bits required
257+
if(in_buffer == nullptr || n > (available_bits+excess_len)) {
258+
return LZSS_BUFFER_EMPTY;
259+
}
260+
261+
// get the required bits
262+
static int buf, mask=0;
263+
int i, x=0;
264+
265+
// TODO this function couls be improved by extracting bits>1 with a single mask
266+
for (i = 0; i < n; i++) {
267+
if (mask == 0) {
268+
if ((buf = getc()) == LZSS_EOF) return LZSS_EOF;// no need to decrement available_bits
269+
mask = 128;
270+
}
271+
x <<= 1;
272+
if (buf & mask) x++;
273+
mask >>= 1;
274+
}
275+
available_bits -= n;
276+
return x;
277+
}
278+
279+
int LZSSDecoder::getc() {
280+
int c;
281+
if(excess_len > 0) {
282+
c = excess_bits;
283+
excess_len -= 8;
284+
} else {
285+
c = *in_buffer;
286+
in_buffer++;
287+
}
288+
return c;
289+
}
290+
291+
void LZSSDecoder::save_excess() { // TODO Explain that
292+
if(this->available_bits >= 8) {
293+
this->excess_bits = *this->in_buffer;
294+
this->excess_len = 8;
295+
}
296+
}

src/decompress/lzss.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,79 @@ void lzss_init(FILE * update_file_ptr, FILE * target_file_ptr, uint32_t const lz
1717
void lzss_decode();
1818
void lzss_flush();
1919

20+
/**************************************************************************************
21+
LZSS DECODER CLASS
22+
**************************************************************************************/
23+
24+
25+
// TODO improve documentation
26+
class LZSSDecoder {
27+
public:
28+
LZSSDecoder(std::function<void(const uint8_t)>);
29+
/**
30+
* this enum describes the result of the computation of a single FSM computation
31+
* DONE: the decompression is completed
32+
* IN_PROGRESS: the decompression cycle completed successfully, ready to compute next
33+
* NOT_COMPLETED: the current cycle didn't complete because the available data is not enough
34+
*/
35+
enum status: uint8_t {
36+
DONE,
37+
IN_PROGRESS,
38+
NOT_COMPLETED
39+
};
40+
41+
/**
42+
* decode the provided buffer until buffer ends, then pause the process
43+
* @return 0 if the decompression is completed, 1 if not
44+
*/
45+
status decompress(const char* buffer, uint32_t size);
46+
private:
47+
// TODO provide a way for the user to set these parameters
48+
static const int EI = 11; /* typically 10..13 */
49+
static const int EJ = 4; /* typically 4..5 */
50+
static const int N = (1 << EI); /* buffer size */
51+
static const int F = ((1 << EJ) + 1); /* lookahead buffer size */
52+
53+
static const int LZSS_EOF = -1;
54+
static const int LZSS_BUFFER_EMPTY = -2;
55+
56+
// alogirthm specific buffer used to store text that could be later referenced and copied
57+
uint8_t buffer[N * 2];
58+
59+
// it may happen that the buffer provided is not enough and there could be an
60+
// excess byte that cannot be handled by the current FSM cycle, we save it in excess_bits
61+
void save_excess();
62+
uint8_t excess_bits=0;
63+
uint8_t excess_len=0; // expressed in number of bits
64+
65+
// this function gets 1 single char from the input buffer
66+
int getc();
67+
uint8_t* in_buffer = nullptr;
68+
uint32_t available_bits = 0;
69+
70+
status handle_state();
71+
72+
// get 1 bit from the available input buffer
73+
int getbit(int n);
74+
75+
enum FSM_STATES: uint8_t {
76+
FSM_0 = 0,
77+
FSM_1 = 1,
78+
FSM_2 = 2,
79+
FSM_3 = 3,
80+
FSM_EOF
81+
} state;
82+
83+
// these varialbe are used in a decode session and specific to the old C implementation
84+
// there is no documentation about their meaning
85+
int i, r;
86+
87+
std::function<void(const uint8_t)> put_char_cbk=nullptr;
88+
89+
inline void putc(const uint8_t c) { if(put_char_cbk) { put_char_cbk(c); } }
90+
91+
// get the number of bits the FSM will require given its state
92+
int bits_required(FSM_STATES s);
93+
};
94+
2095
#endif /* SSU_LZSS_H_ */

0 commit comments

Comments
 (0)