Skip to content

Commit 1a47bcb

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 02149a1 commit 1a47bcb

File tree

2 files changed

+254
-122
lines changed

2 files changed

+254
-122
lines changed

src/decompress/lzss.cpp

Lines changed: 168 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -5,159 +5,205 @@
55
#include "lzss.h"
66

77
#include <stdlib.h>
8-
#include <stdint.h>
98

109
/**************************************************************************************
11-
DEFINE
10+
GLOBAL VARIABLES
1211
**************************************************************************************/
1312

14-
#define EI 11 /* typically 10..13 */
15-
#define EJ 4 /* typically 4..5 */
16-
#define P 1 /* If match length <= P then output one character */
17-
#define N (1 << EI) /* buffer size */
18-
#define F ((1 << EJ) + 1) /* lookahead buffer size */
19-
20-
#define LZSS_EOF (-1)
13+
static FILE * update_file = nullptr;
14+
static FILE * target_file = nullptr;
2115

22-
#define FPUTC_BUF_SIZE (64)
23-
#define FGETC_BUF_SIZE (64)
16+
static ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func = nullptr;
17+
static LZSSDecoder* decoder = nullptr;
2418

2519
/**************************************************************************************
26-
GLOBAL VARIABLES
20+
PUBLIC FUNCTIONS
2721
**************************************************************************************/
2822

29-
static uint32_t LZSS_FILE_SIZE = 0;
30-
static FILE * update_file = 0;
31-
static FILE * target_file = 0;
23+
void lzss_init(
24+
FILE * update_file_ptr,
25+
FILE * target_file_ptr,
26+
uint32_t const lzss_file_size,
27+
ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func_ptr) {
28+
update_file = update_file_ptr;
29+
target_file = target_file_ptr;
30+
wdog_feed_func = wdog_feed_func_ptr;
31+
32+
if(decoder != nullptr) {
33+
delete decoder;
34+
decoder = nullptr;
35+
}
36+
37+
decoder = new LZSSDecoder(
38+
[target_file](const uint8_t c) {
39+
fwrite(&c, 1, 1, target_file);
40+
}
41+
);
42+
}
43+
44+
void lzss_flush() {
45+
fflush(target_file);
46+
}
47+
48+
void lzss_decode() {
49+
if(decoder == nullptr) {
50+
return;
51+
}
52+
const size_t buf_size = 64;
53+
uint8_t buffer[buf_size];
54+
size_t res = 0;
3255

33-
int bit_buffer = 0, bit_mask = 128;
34-
unsigned char buffer[N * 2];
56+
do {
57+
if(wdog_feed_func) {
58+
wdog_feed_func();
59+
}
60+
res = fread(buffer, sizeof(uint8_t), buf_size, update_file);
61+
decoder->decompress(buffer, res);
62+
} while(res == buf_size);
63+
}
3564

36-
static char write_buf[FPUTC_BUF_SIZE];
37-
static size_t write_buf_num_bytes = 0;
38-
static size_t bytes_written_fputc = 0;
39-
static ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func = 0;
4065

4166
/**************************************************************************************
42-
PUBLIC FUNCTIONS
67+
LZSS DECODER CLASS IMPLEMENTATION
4368
**************************************************************************************/
4469

45-
void lzss_init(FILE * update_file_ptr, FILE * target_file_ptr, uint32_t const lzss_file_size, ArduinoPortentaOtaWatchdogResetFuncPointer wdog_feed_func_ptr)
46-
{
47-
update_file = update_file_ptr;
48-
target_file = target_file_ptr;
49-
LZSS_FILE_SIZE = lzss_file_size;
50-
wdog_feed_func = wdog_feed_func_ptr;
70+
// get the number of bits the algorithm will try to get given the state
71+
uint8_t LZSSDecoder::bits_required(LZSSDecoder::FSM_STATES s) {
72+
switch(s) {
73+
case FSM_0:
74+
return 1;
75+
case FSM_1:
76+
return 8;
77+
case FSM_2:
78+
return EI;
79+
case FSM_3:
80+
return EJ;
81+
default:
82+
return 0;
83+
}
5184
}
5285

53-
void lzss_flush()
54-
{
55-
bytes_written_fputc += write_buf_num_bytes;
56-
57-
if (wdog_feed_func)
58-
wdog_feed_func();
86+
LZSSDecoder::LZSSDecoder(std::function<int()> getc_cbk, std::function<void(const uint8_t)> putc_cbk)
87+
: available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(getc_cbk) {
88+
for (int i = 0; i < N - F; i++) buffer[i] = ' ';
89+
r = N - F;
90+
}
5991

60-
fwrite(write_buf, 1, write_buf_num_bytes, target_file);
6192

62-
write_buf_num_bytes = 0;
93+
LZSSDecoder::LZSSDecoder(std::function<void(const uint8_t)> putc_cbk)
94+
: available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(nullptr) {
95+
for (int i = 0; i < N - F; i++) buffer[i] = ' ';
96+
r = N - F;
6397
}
6498

65-
/**************************************************************************************
66-
PRIVATE FUNCTIONS
67-
**************************************************************************************/
99+
LZSSDecoder::status LZSSDecoder::handle_state() {
100+
LZSSDecoder::status res = IN_PROGRESS;
101+
102+
int c = getbit(bits_required(this->state));
103+
104+
if(c == LZSS_BUFFER_EMPTY) {
105+
res = NOT_COMPLETED;
106+
} else if (c == LZSS_EOF) {
107+
res = DONE;
108+
this->state = FSM_EOF;
109+
} else {
110+
switch(this->state) {
111+
case FSM_0:
112+
if(c) {
113+
this->state = FSM_1;
114+
} else {
115+
this->state = FSM_2;
116+
}
117+
break;
118+
case FSM_1:
119+
putc(c);
120+
buffer[r++] = c;
121+
r &= (N - 1); // equivalent to r = r % N when N is a power of 2
122+
123+
this->state = FSM_0;
124+
break;
125+
case FSM_2:
126+
this->i = c;
127+
this->state = FSM_3;
128+
break;
129+
case FSM_3: {
130+
int j = c;
131+
132+
// This is where the actual decompression takes place: we look into the local buffer for reuse
133+
// of byte chunks. This can be improved by means of memcpy and by changing the putc function
134+
// into a put_buf function in order to avoid buffering on the other end.
135+
// TODO improve this section of code
136+
for (int k = 0; k <= j + 1; k++) {
137+
c = buffer[(this->i + k) & (N - 1)]; // equivalent to buffer[(i+k) % N] when N is a power of 2
138+
putc(c);
139+
buffer[r++] = c;
140+
r &= (N - 1); // equivalent to r = r % N
141+
}
142+
this->state = FSM_0;
143+
144+
break;
145+
}
146+
case FSM_EOF:
147+
break;
148+
}
149+
}
68150

69-
void lzss_fputc(int const c)
70-
{
71-
/* Buffer the decompressed data into a buffer so
72-
* we can perform block writes and don't need to
73-
* write every byte singly on the flash (which
74-
* wouldn't be possible anyway).
75-
*/
76-
write_buf[write_buf_num_bytes] = static_cast<char>(c);
77-
write_buf_num_bytes++;
78-
79-
/* The write buffer is full of decompressed
80-
* data, write it to the flash now.
81-
*/
82-
if (write_buf_num_bytes == FPUTC_BUF_SIZE)
83-
lzss_flush();
151+
return res;
84152
}
85153

86-
int lzss_fgetc()
87-
{
88-
static uint8_t read_buf[FGETC_BUF_SIZE];
89-
static size_t read_buf_pos = FGETC_BUF_SIZE;
90-
static size_t bytes_read_fgetc = 0;
91-
static size_t bytes_read_from_modem = 0;
92-
93-
/* lzss_file_size is set within SSUBoot:main
94-
* and contains the size of the LZSS file. Once
95-
* all those bytes have been read its time to return
96-
* LZSS_EOF in order to signal that the end of
97-
* the file has been reached.
98-
*/
99-
if (bytes_read_fgetc == LZSS_FILE_SIZE)
100-
return LZSS_EOF;
101-
102-
/* If there is no data left to be read from the read buffer
103-
* than read a new block and store it into the read buffer.
104-
*/
105-
if (read_buf_pos == FGETC_BUF_SIZE)
106-
{
107-
/* Read the next block from the flash memory. */
108-
bytes_read_from_modem += fread(read_buf, 1, FGETC_BUF_SIZE, update_file);
109-
/* Reset the read buffer position. */
110-
read_buf_pos = 0;
111-
}
112-
113-
uint8_t const c = read_buf[read_buf_pos];
114-
read_buf_pos++;
115-
bytes_read_fgetc++;
116-
117-
return c;
154+
LZSSDecoder::status LZSSDecoder::decompress(uint8_t* const buffer, uint32_t size) {
155+
if(!get_char_cbk) {
156+
this->in_buffer = buffer;
157+
this->available += size;
158+
}
159+
160+
status res = IN_PROGRESS;
161+
162+
while((res = handle_state()) == IN_PROGRESS);
163+
164+
this->in_buffer = nullptr;
165+
166+
return res;
118167
}
119168

120-
/**************************************************************************************
121-
LZSS FUNCTIONS
122-
**************************************************************************************/
169+
int LZSSDecoder::getbit(uint8_t n) { // get n bits from buffer
170+
int x=0, c;
123171

124-
int getbit(int n) /* get n bits */
125-
{
126-
int i, x;
127-
static int buf, mask = 0;
128-
129-
x = 0;
130-
for (i = 0; i < n; i++) {
131-
if (mask == 0) {
132-
if ((buf = lzss_fgetc()) == LZSS_EOF) return LZSS_EOF;
133-
mask = 128;
172+
// if the local bit buffer doesn't have enough bit get them
173+
while(buf_size < n) {
174+
switch(c=getc()) {
175+
case LZSS_EOF:
176+
case LZSS_BUFFER_EMPTY:
177+
return c;
134178
}
135-
x <<= 1;
136-
if (buf & mask) x++;
137-
mask >>= 1;
179+
buf <<= 8;
180+
181+
buf |= (uint8_t)c;
182+
buf_size += sizeof(uint8_t)*8;
138183
}
184+
185+
// the result is the content of the buffer starting from msb to n successive bits
186+
x = buf >> (buf_size-n);
187+
188+
// remove from the buffer the read bits with a mask
189+
buf &= (1<<(buf_size-n))-1;
190+
191+
buf_size-=n;
192+
139193
return x;
140194
}
141195

142-
void lzss_decode(void)
143-
{
144-
int i, j, k, r, c;
145-
146-
for (i = 0; i < N - F; i++) buffer[i] = ' ';
147-
r = N - F;
148-
while ((c = getbit(1)) != LZSS_EOF) {
149-
if (c) {
150-
if ((c = getbit(8)) == LZSS_EOF) break;
151-
lzss_fputc(c);
152-
buffer[r++] = c; r &= (N - 1);
153-
} else {
154-
if ((i = getbit(EI)) == LZSS_EOF) break;
155-
if ((j = getbit(EJ)) == LZSS_EOF) break;
156-
for (k = 0; k <= j + 1; k++) {
157-
c = buffer[(i + k) & (N - 1)];
158-
lzss_fputc(c);
159-
buffer[r++] = c; r &= (N - 1);
160-
}
161-
}
196+
int LZSSDecoder::getc() {
197+
int c;
198+
199+
if(get_char_cbk) {
200+
c = get_char_cbk();
201+
} else if(in_buffer == nullptr || available == 0) {
202+
c = LZSS_BUFFER_EMPTY;
203+
} else {
204+
c = *in_buffer;
205+
in_buffer++;
206+
available--;
162207
}
208+
return c;
163209
}

0 commit comments

Comments
 (0)