Skip to content

Commit 672403c

Browse files
updating LZSS implementation to be in line with IotCloud
1 parent 594ea2f commit 672403c

File tree

2 files changed

+230
-144
lines changed

2 files changed

+230
-144
lines changed

src/decompress/lzss.cpp

+132-138
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,163 @@
1-
/* Original code: https://oku.edu.mie-u.ac.jp/~okumura/compression/lzss.c */
1+
/*
2+
This file is part of the ArduinoIoTCloud library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
10+
This implementation took inspiration from https://okumuralab.org/~okumura/compression/lzss.c source code
11+
*/
212

313
/**************************************************************************************
414
INCLUDE
515
**************************************************************************************/
6-
716
#include "lzss.h"
817

18+
#include <stdlib.h>
19+
920
/**************************************************************************************
10-
DEFINE
21+
LZSS DECODER CLASS IMPLEMENTATION
1122
**************************************************************************************/
1223

13-
#define EI 11 /* typically 10..13 */
14-
#define EJ 4 /* typically 4..5 */
15-
#define P 1 /* If match length <= P then output one character */
16-
#define N (1 << EI) /* buffer size */
17-
#define L ((1 << EJ) + 1) /* lookahead buffer size */
18-
19-
#define LZSS_EOF (-1)
24+
// get the number of bits the algorithm will try to get given the state
25+
uint8_t LZSSDecoder::bits_required(LZSSDecoder::FSM_STATES s) {
26+
switch(s) {
27+
case FSM_0:
28+
return 1;
29+
case FSM_1:
30+
return 8;
31+
case FSM_2:
32+
return EI;
33+
case FSM_3:
34+
return EJ;
35+
default:
36+
return 0;
37+
}
38+
}
2039

21-
/**************************************************************************************
22-
GLOBAL VARIABLES
23-
**************************************************************************************/
40+
LZSSDecoder::LZSSDecoder(std::function<int()> getc_cbk, std::function<void(const uint8_t)> putc_cbk)
41+
: available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(getc_cbk) {
42+
for (int i = 0; i < N - F; i++) buffer[i] = ' ';
43+
r = N - F;
44+
}
2445

25-
/* Used to bind local module function to actual class instance */
26-
static Arduino_ESP32_OTA * esp_ota_obj_ptr = 0;
2746

28-
static size_t LZSS_FILE_SIZE = 0;
47+
LZSSDecoder::LZSSDecoder(std::function<void(const uint8_t)> putc_cbk)
48+
: available(0), state(FSM_0), put_char_cbk(putc_cbk), get_char_cbk(nullptr) {
49+
for (int i = 0; i < N - F; i++) buffer[i] = ' ';
50+
r = N - F;
51+
}
2952

30-
int bit_buffer = 0, bit_mask = 128;
31-
unsigned char buffer[N * 2];
53+
LZSSDecoder::status LZSSDecoder::handle_state() {
54+
LZSSDecoder::status res = IN_PROGRESS;
3255

33-
static size_t bytes_written_fputc = 0;
34-
static size_t bytes_read_fgetc = 0;
56+
int c = getbit(bits_required(this->state));
3557

36-
/**************************************************************************************
37-
PRIVATE FUNCTIONS
38-
**************************************************************************************/
58+
if(c == LZSS_BUFFER_EMPTY) {
59+
res = NOT_COMPLETED;
60+
} else if (c == LZSS_EOF) {
61+
res = DONE;
62+
this->state = FSM_EOF;
63+
} else {
64+
switch(this->state) {
65+
case FSM_0:
66+
if(c) {
67+
this->state = FSM_1;
68+
} else {
69+
this->state = FSM_2;
70+
}
71+
break;
72+
case FSM_1:
73+
putc(c);
74+
buffer[r++] = c;
75+
r &= (N - 1); // equivalent to r = r % N when N is a power of 2
76+
77+
this->state = FSM_0;
78+
break;
79+
case FSM_2:
80+
this->i = c;
81+
this->state = FSM_3;
82+
break;
83+
case FSM_3: {
84+
int j = c;
85+
86+
// This is where the actual decompression takes place: we look into the local buffer for reuse
87+
// of byte chunks. This can be improved by means of memcpy and by changing the putc function
88+
// into a put_buf function in order to avoid buffering on the other end.
89+
// TODO improve this section of code
90+
for (int k = 0; k <= j + 1; k++) {
91+
c = buffer[(this->i + k) & (N - 1)]; // equivalent to buffer[(i+k) % N] when N is a power of 2
92+
putc(c);
93+
buffer[r++] = c;
94+
r &= (N - 1); // equivalent to r = r % N
95+
}
96+
this->state = FSM_0;
97+
98+
break;
99+
}
100+
case FSM_EOF:
101+
break;
102+
}
103+
}
39104

40-
void lzss_fputc(int const c)
41-
{
42-
esp_ota_obj_ptr->write_byte_to_flash((uint8_t)c);
43-
44-
/* write byte callback */
45-
bytes_written_fputc++;
105+
return res;
46106
}
47107

48-
int lzss_fgetc()
49-
{
50-
/* lzss_file_size is set within SSUBoot:main
51-
* and contains the size of the LZSS file. Once
52-
* all those bytes have been read its time to return
53-
* LZSS_EOF in order to signal that the end of
54-
* the file has been reached.
55-
*/
56-
if (bytes_read_fgetc == LZSS_FILE_SIZE)
57-
return LZSS_EOF;
58-
59-
/* read byte callback */
60-
uint8_t const c = esp_ota_obj_ptr->read_byte_from_network();
61-
bytes_read_fgetc++;
62-
63-
return c;
64-
}
108+
LZSSDecoder::status LZSSDecoder::decompress(uint8_t* const buffer, uint32_t size) {
109+
if(!get_char_cbk) {
110+
this->in_buffer = buffer;
111+
this->available += size;
112+
}
65113

66-
/**************************************************************************************
67-
LZSS FUNCTIONS
68-
**************************************************************************************/
114+
status res = IN_PROGRESS;
69115

70-
void putbit1(void)
71-
{
72-
bit_buffer |= bit_mask;
73-
if ((bit_mask >>= 1) == 0) {
74-
lzss_fputc(bit_buffer);
75-
bit_buffer = 0; bit_mask = 128;
76-
}
77-
}
116+
while((res = handle_state()) == IN_PROGRESS);
78117

79-
void putbit0(void)
80-
{
81-
if ((bit_mask >>= 1) == 0) {
82-
lzss_fputc(bit_buffer);
83-
bit_buffer = 0; bit_mask = 128;
84-
}
85-
}
118+
this->in_buffer = nullptr;
86119

87-
void output1(int c)
88-
{
89-
int mask;
90-
91-
putbit1();
92-
mask = 256;
93-
while (mask >>= 1) {
94-
if (c & mask) putbit1();
95-
else putbit0();
96-
}
120+
return res;
97121
}
98122

99-
void output2(int x, int y)
100-
{
101-
int mask;
102-
103-
putbit0();
104-
mask = N;
105-
while (mask >>= 1) {
106-
if (x & mask) putbit1();
107-
else putbit0();
108-
}
109-
mask = (1 << EJ);
110-
while (mask >>= 1) {
111-
if (y & mask) putbit1();
112-
else putbit0();
113-
}
114-
}
123+
int LZSSDecoder::getbit(uint8_t n) { // get n bits from buffer
124+
int x=0, c;
115125

116-
int getbit(int n) /* get n bits */
117-
{
118-
int i, x;
119-
static int buf, mask = 0;
120-
121-
x = 0;
122-
for (i = 0; i < n; i++) {
123-
if (mask == 0) {
124-
if ((buf = lzss_fgetc()) == LZSS_EOF) return LZSS_EOF;
125-
mask = 128;
126-
}
127-
x <<= 1;
128-
if (buf & mask) x++;
129-
mask >>= 1;
130-
}
131-
return x;
132-
}
126+
// if the local bit buffer doesn't have enough bit get them
127+
while(buf_size < n) {
128+
switch(c=getc()) {
129+
case LZSS_EOF:
130+
case LZSS_BUFFER_EMPTY:
131+
return c;
132+
}
133+
buf <<= 8;
133134

134-
void lzss_decode(void)
135-
{
136-
int i, j, k, r, c;
137-
138-
for (i = 0; i < N - L; i++) buffer[i] = ' ';
139-
r = N - L;
140-
while ((c = getbit(1)) != LZSS_EOF) {
141-
if (c) {
142-
if ((c = getbit(8)) == LZSS_EOF) break;
143-
lzss_fputc(c);
144-
buffer[r++] = c; r &= (N - 1);
145-
} else {
146-
if ((i = getbit(EI)) == LZSS_EOF) break;
147-
if ((j = getbit(EJ)) == LZSS_EOF) break;
148-
for (k = 0; k <= j + 1; k++) {
149-
c = buffer[(i + k) & (N - 1)];
150-
lzss_fputc(c);
151-
buffer[r++] = c; r &= (N - 1);
152-
}
135+
buf |= (uint8_t)c;
136+
buf_size += sizeof(uint8_t)*8;
153137
}
154-
}
138+
139+
// the result is the content of the buffer starting from msb to n successive bits
140+
x = buf >> (buf_size-n);
141+
142+
// remove from the buffer the read bits with a mask
143+
buf &= (1<<(buf_size-n))-1;
144+
145+
buf_size-=n;
146+
147+
return x;
155148
}
156149

157-
/**************************************************************************************
158-
PUBLIC FUNCTIONS
159-
**************************************************************************************/
150+
int LZSSDecoder::getc() {
151+
int c;
160152

161-
int lzss_download(Arduino_ESP32_OTA * instance, size_t const lzss_file_size)
162-
{
163-
esp_ota_obj_ptr = instance;
164-
LZSS_FILE_SIZE = lzss_file_size;
165-
bytes_written_fputc = 0;
166-
bytes_read_fgetc = 0;
167-
lzss_decode();
168-
return bytes_written_fputc;
153+
if(get_char_cbk) {
154+
c = get_char_cbk();
155+
} else if(in_buffer == nullptr || available == 0) {
156+
c = LZSS_BUFFER_EMPTY;
157+
} else {
158+
c = *in_buffer;
159+
in_buffer++;
160+
available--;
161+
}
162+
return c;
169163
}

0 commit comments

Comments
 (0)