|
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