|
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 | +*/ |
2 | 12 |
|
3 | 13 | /************************************************************************************** |
4 | 14 | INCLUDE |
5 | 15 | **************************************************************************************/ |
6 | | - |
7 | 16 | #include "lzss.h" |
8 | 17 |
|
| 18 | +#include <stdlib.h> |
| 19 | + |
9 | 20 | /************************************************************************************** |
10 | | - DEFINE |
| 21 | + LZSS DECODER CLASS IMPLEMENTATION |
11 | 22 | **************************************************************************************/ |
12 | 23 |
|
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 | +} |
20 | 39 |
|
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 | +} |
24 | 45 |
|
25 | | -/* Used to bind local module function to actual class instance */ |
26 | | -static Arduino_ESP32_OTA * esp_ota_obj_ptr = 0; |
27 | 46 |
|
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 | +} |
29 | 52 |
|
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; |
32 | 55 |
|
33 | | -static size_t bytes_written_fputc = 0; |
34 | | -static size_t bytes_read_fgetc = 0; |
| 56 | + int c = getbit(bits_required(this->state)); |
35 | 57 |
|
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 | + } |
39 | 104 |
|
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; |
46 | 106 | } |
47 | 107 |
|
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 | + } |
65 | 113 |
|
66 | | -/************************************************************************************** |
67 | | - LZSS FUNCTIONS |
68 | | - **************************************************************************************/ |
| 114 | + status res = IN_PROGRESS; |
69 | 115 |
|
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); |
78 | 117 |
|
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; |
86 | 119 |
|
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; |
97 | 121 | } |
98 | 122 |
|
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; |
115 | 125 |
|
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; |
133 | 134 |
|
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; |
153 | 137 | } |
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; |
155 | 148 | } |
156 | 149 |
|
157 | | -/************************************************************************************** |
158 | | - PUBLIC FUNCTIONS |
159 | | - **************************************************************************************/ |
| 150 | +int LZSSDecoder::getc() { |
| 151 | + int c; |
160 | 152 |
|
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; |
169 | 163 | } |
0 commit comments