Skip to content

Commit e36cf27

Browse files
andreagilardonipennam
authored andcommitted
included LZSSDecoder library beforme making it a library
1 parent 232e25a commit e36cf27

File tree

2 files changed

+271
-0
lines changed

2 files changed

+271
-0
lines changed

src/utility/lzss/lzss.cpp

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
*/
12+
13+
/**************************************************************************************
14+
INCLUDE
15+
**************************************************************************************/
16+
#include "lzss.h"
17+
18+
#include <stdlib.h>
19+
20+
/**************************************************************************************
21+
LZSS DECODER CLASS IMPLEMENTATION
22+
**************************************************************************************/
23+
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+
}
39+
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+
}
45+
46+
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+
}
52+
53+
LZSSDecoder::status LZSSDecoder::handle_state() {
54+
LZSSDecoder::status res = IN_PROGRESS;
55+
56+
int c = getbit(bits_required(this->state));
57+
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+
}
104+
105+
return res;
106+
}
107+
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+
}
113+
114+
status res = IN_PROGRESS;
115+
116+
while((res = handle_state()) == IN_PROGRESS);
117+
118+
this->in_buffer = nullptr;
119+
120+
return res;
121+
}
122+
123+
int LZSSDecoder::getbit(uint8_t n) { // get n bits from buffer
124+
int x=0, c;
125+
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;
134+
135+
buf |= (uint8_t)c;
136+
buf_size += sizeof(uint8_t)*8;
137+
}
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;
148+
}
149+
150+
int LZSSDecoder::getc() {
151+
int c;
152+
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;
163+
}

src/utility/lzss/lzss.h

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
11+
#pragma once
12+
13+
/**************************************************************************************
14+
INCLUDE
15+
**************************************************************************************/
16+
#include <Arduino.h>
17+
#include <functional>
18+
#include <stdint.h>
19+
20+
/**************************************************************************************
21+
FUNCTION DEFINITION
22+
**************************************************************************************/
23+
24+
/**************************************************************************************
25+
LZSS DECODER CLASS
26+
**************************************************************************************/
27+
28+
29+
class LZSSDecoder {
30+
public:
31+
32+
/**
33+
* Build an LZSS decoder by providing a callback for storing the decoded bytes
34+
* @param putc_cbk: a callback that takes a char and stores it e.g. a callback to fwrite
35+
*/
36+
LZSSDecoder(std::function<void(const uint8_t)> putc_cbk);
37+
38+
/**
39+
* Build an LZSS decoder providing a callback for getting a char and putting a char
40+
* in this way you need to call decompress with no parameters
41+
* @param putc_cbk: a callback that takes a char and stores it e.g. a callback to fwrite
42+
* @param getc_cbk: a callback that returns the next char to consume
43+
* -1 means EOF, -2 means buffer is temporairly finished
44+
*/
45+
LZSSDecoder(std::function<int()> getc_cbk, std::function<void(const uint8_t)> putc_cbk);
46+
47+
/**
48+
* this enum describes the result of the computation of a single FSM computation
49+
* DONE: the decompression is completed
50+
* IN_PROGRESS: the decompression cycle completed successfully, ready to compute next
51+
* NOT_COMPLETED: the current cycle didn't complete because the available data is not enough
52+
*/
53+
enum status: uint8_t {
54+
DONE,
55+
IN_PROGRESS,
56+
NOT_COMPLETED
57+
};
58+
59+
/**
60+
* decode the provided buffer until buffer ends, then pause the process
61+
* @return DONE if the decompression is completed, NOT_COMPLETED if not
62+
*/
63+
status decompress(uint8_t* const buffer=nullptr, uint32_t size=0);
64+
65+
static const int LZSS_EOF = -1;
66+
static const int LZSS_BUFFER_EMPTY = -2;
67+
private:
68+
// TODO provide a way for the user to set these parameters
69+
static const int EI = 11; /* typically 10..13 */
70+
static const int EJ = 4; /* typically 4..5 */
71+
static const int N = (1 << EI); /* buffer size */
72+
static const int F = ((1 << EJ) + 1); /* lookahead buffer size */
73+
74+
// algorithm specific buffer used to store text that could be later referenced and copied
75+
uint8_t buffer[N * 2];
76+
77+
// this function gets 1 single char from the input buffer
78+
int getc();
79+
uint8_t* in_buffer = nullptr;
80+
uint32_t available = 0;
81+
82+
status handle_state();
83+
84+
// get 1 bit from the available input buffer
85+
int getbit(uint8_t n);
86+
// the following 2 are variables used by getbits
87+
uint32_t buf, buf_size=0;
88+
89+
enum FSM_STATES: uint8_t {
90+
FSM_0 = 0,
91+
FSM_1 = 1,
92+
FSM_2 = 2,
93+
FSM_3 = 3,
94+
FSM_EOF
95+
} state;
96+
97+
// these variable are used in a decode session and specific to the old C implementation
98+
// there is no documentation about their meaning
99+
int i, r;
100+
101+
std::function<void(const uint8_t)> put_char_cbk;
102+
std::function<uint8_t()> get_char_cbk;
103+
104+
inline void putc(const uint8_t c) { if(put_char_cbk) { put_char_cbk(c); } }
105+
106+
// get the number of bits the FSM will require given its state
107+
uint8_t bits_required(FSM_STATES s);
108+
};

0 commit comments

Comments
 (0)