MeterLogger
heatshrink.c
Go to the documentation of this file.
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <assert.h>
5 #include <string.h>
6 #include <err.h>
7 #include <fcntl.h>
8 
9 #include "heatshrink_encoder.h"
10 #include "heatshrink_decoder.h"
11 
12 #define DEF_WINDOW_SZ2 11
13 #define DEF_LOOKAHEAD_SZ2 4
14 #define DEF_DECODER_INPUT_BUFFER_SIZE 256
15 #define DEF_BUFFER_SIZE (64 * 1024)
16 
17 #if 0
18 #define LOG(...) fprintf(stderr, __VA_ARGS__)
19 #else
20 #define LOG(...) /* NO-OP */
21 #endif
22 
26 static const char author[] = HEATSHRINK_AUTHOR;
27 static const char url[] = HEATSHRINK_URL;
28 
29 static void usage(void) {
30  fprintf(stderr, "heatshrink version %u.%u.%u by %s\n",
32  fprintf(stderr, "Home page: %s\n\n", url);
33  fprintf(stderr,
34  "Usage:\n"
35  " heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]\n"
36  "\n"
37  "heatshrink compresses or uncompresses byte streams using LZSS, and is\n"
38  "designed especially for embedded, low-memory, and/or hard real-time\n"
39  "systems.\n"
40  "\n"
41  " -h print help\n"
42  " -e encode (compress, default)\n"
43  " -d decode (uncompress)\n"
44  " -v verbose (print input & output sizes, compression ratio, etc.)\n"
45  "\n"
46  " -w SIZE Base-2 log of LZSS sliding window size\n"
47  "\n"
48  " A larger value allows searches a larger history of the data for repeated\n"
49  " patterns, potentially compressing more effectively, but will use\n"
50  " more memory and processing time.\n"
51  " Recommended default: -w 8 (embedded systems), -w 10 (elsewhere)\n"
52  " \n"
53  " -l BITS Number of bits used for back-reference lengths\n"
54  "\n"
55  " A larger value allows longer substitutions, but since all\n"
56  " back-references must use -w + -l bits, larger -w or -l can be\n"
57  " counterproductive if most patterns are small and/or local.\n"
58  " Recommended default: -l 4\n"
59  "\n"
60  " If IN_FILE or OUT_FILE are unspecified, they will default to\n"
61  " \"-\" for standard input and standard output, respectively.\n");
62  exit(1);
63 }
64 
65 typedef enum { IO_READ, IO_WRITE, } IO_mode;
66 typedef enum { OP_ENC, OP_DEC, } Operation;
67 
68 typedef struct {
69  int fd; /* file descriptor */
71  size_t fill; /* fill index */
72  size_t read; /* read index */
73  size_t size;
74  size_t total;
75  uint8_t buf[];
76 } io_handle;
77 
78 typedef struct {
79  uint8_t window_sz2;
80  uint8_t lookahead_sz2;
82  size_t buffer_size;
83  uint8_t verbose;
85  char *in_fname;
86  char *out_fname;
89 } config;
90 
91 static void die(char *msg) {
92  fprintf(stderr, "%s\n", msg);
93  exit(EXIT_FAILURE);
94 }
95 
96 static void report(config *cfg);
97 
98 /* Open an IO handle. Returns NULL on error. */
99 static io_handle *handle_open(char *fname, IO_mode m, size_t buf_sz) {
100  io_handle *io = NULL;
101  io = malloc(sizeof(*io) + buf_sz);
102  if (io == NULL) { return NULL; }
103  memset(io, 0, sizeof(*io) + buf_sz);
104  io->fd = -1;
105  io->size = buf_sz;
106  io->mode = m;
107 
108  if (m == IO_READ) {
109  if (0 == strcmp("-", fname)) {
110  io->fd = STDIN_FILENO;
111  } else {
112  io->fd = open(fname, O_RDONLY);
113  }
114  } else if (m == IO_WRITE) {
115  if (0 == strcmp("-", fname)) {
116  io->fd = STDOUT_FILENO;
117  } else {
118  io->fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC /*| O_EXCL*/, 0644);
119  }
120  }
121 
122  if (io->fd == -1) { /* failed to open */
123  free(io);
124  err(1, "open");
125  return NULL;
126  }
127 
128  return io;
129 }
130 
131 /* Read SIZE bytes from an IO handle and return a pointer to the content.
132  * BUF contains at least size_t bytes. Returns 0 on EOF, -1 on error. */
133 static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf) {
134  LOG("@ read %zd\n", size);
135  if (buf == NULL) { return -1; }
136  if (size > io->size) {
137  printf("size %zd, io->size %zd\n", size, io->size);
138  return -1;
139  }
140  if (io->mode != IO_READ) { return -1; }
141 
142  size_t rem = io->fill - io->read;
143  if (rem >= size) {
144  *buf = &io->buf[io->read];
145  return size;
146  } else { /* read and replenish */
147  if (io->fd == -1) { /* already closed, return what we've got */
148  *buf = &io->buf[io->read];
149  return rem;
150  }
151 
152  memmove(io->buf, &io->buf[io->read], rem);
153  io->fill -= io->read;
154  io->read = 0;
155  ssize_t read_sz = read(io->fd, &io->buf[io->fill], io->size - io->fill);
156  if (read_sz < 0) { err(1, "read"); }
157  io->total += read_sz;
158  if (read_sz == 0) { /* EOF */
159  if (close(io->fd) < 0) { err(1, "close"); }
160  io->fd = -1;
161  }
162  io->fill += read_sz;
163  *buf = io->buf;
164  return io->fill > size ? size : io->fill;
165  }
166 }
167 
168 /* Drop the oldest SIZE bytes from the buffer. Returns <0 on error. */
169 static int handle_drop(io_handle *io, size_t size) {
170  LOG("@ drop %zd\n", size);
171  if (io->read + size <= io->fill) {
172  io->read += size;
173  } else {
174  return -1;
175  }
176  if (io->read == io->fill) {
177  io->read = 0;
178  io->fill = 0;
179  }
180  return 0;
181 }
182 
183 /* Sink SIZE bytes from INPUT into the io handle. Returns the number of
184  * bytes written, or -1 on error. */
185 static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input) {
186  LOG("@ sink %zd\n", size);
187  if (size > io->size) { return -1; }
188  if (io->mode != IO_WRITE) { return -1; }
189 
190  if (io->fill + size > io->size) {
191  ssize_t written = write(io->fd, io->buf, io->fill);
192  LOG("@ flushing %zd, wrote %zd\n", io->fill, written);
193  io->total += written;
194  if (written == -1) { err(1, "write"); }
195  memmove(io->buf, &io->buf[written], io->fill - written);
196  io->fill -= written;
197  }
198  memcpy(&io->buf[io->fill], input, size);
199  io->fill += size;
200  return size;
201 }
202 
203 static void handle_close(io_handle *io) {
204  if (io->fd != -1) {
205  if (io->mode == IO_WRITE) {
206  ssize_t written = write(io->fd, io->buf, io->fill);
207  io->total += written;
208  LOG("@ close: flushing %zd, wrote %zd\n", io->fill, written);
209  if (written == -1) { err(1, "write"); }
210  }
211  close(io->fd);
212  io->fd = -1;
213  }
214 }
215 
216 static void close_and_report(config *cfg) {
217  handle_close(cfg->in);
218  handle_close(cfg->out);
219  if (cfg->verbose) { report(cfg); }
220  free(cfg->in);
221  free(cfg->out);
222 }
223 
225  uint8_t *data, size_t data_sz) {
226  size_t out_sz = 4096;
227  uint8_t out_buf[out_sz];
228  memset(out_buf, 0, out_sz);
229  size_t sink_sz = 0;
230  size_t poll_sz = 0;
231  HSE_sink_res sres;
232  HSE_poll_res pres;
233  HSE_finish_res fres;
234  io_handle *out = cfg->out;
235 
236  size_t sunk = 0;
237  do {
238  if (data_sz > 0) {
239  sres = heatshrink_encoder_sink(hse, &data[sunk], data_sz - sunk, &sink_sz);
240  if (sres < 0) { die("sink"); }
241  sunk += sink_sz;
242  }
243 
244  do {
245  pres = heatshrink_encoder_poll(hse, out_buf, out_sz, &poll_sz);
246  if (pres < 0) { die("poll"); }
247  if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink");
248  } while (pres == HSER_POLL_MORE);
249 
250  if (poll_sz == 0 && data_sz == 0) {
251  fres = heatshrink_encoder_finish(hse);
252  if (fres < 0) { die("finish"); }
253  if (fres == HSER_FINISH_DONE) { return 1; }
254  }
255  } while (sunk < data_sz);
256  return 0;
257 }
258 
259 static int encode(config *cfg) {
260  uint8_t window_sz2 = cfg->window_sz2;
261  size_t window_sz = 1 << window_sz2;
263  if (hse == NULL) { die("failed to init encoder: bad settings"); }
264  ssize_t read_sz = 0;
265  io_handle *in = cfg->in;
266 
267  /* Process input until end of stream */
268  while (1) {
269  uint8_t *input = NULL;
270  read_sz = handle_read(in, window_sz, &input);
271  if (input == NULL) {
272  printf("handle read failure\n");
273  die("read");
274  }
275  if (read_sz < 0) { die("read"); }
276 
277  /* Pass read to encoder and check if input is fully processed. */
278  if (encoder_sink_read(cfg, hse, input, read_sz)) break;
279 
280  if (handle_drop(in, read_sz) < 0) { die("drop"); }
281  };
282 
283  if (read_sz == -1) { err(1, "read"); }
284 
286  close_and_report(cfg);
287  return 0;
288 }
289 
291  uint8_t *data, size_t data_sz) {
292  io_handle *out = cfg->out;
293  size_t sink_sz = 0;
294  size_t poll_sz = 0;
295  size_t out_sz = 4096;
296  uint8_t out_buf[out_sz];
297  memset(out_buf, 0, out_sz);
298 
299  HSD_sink_res sres;
300  HSD_poll_res pres;
301  HSD_finish_res fres;
302 
303  size_t sunk = 0;
304  do {
305  if (data_sz > 0) {
306  sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz);
307  if (sres < 0) { die("sink"); }
308  sunk += sink_sz;
309  }
310 
311  do {
312  pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz);
313  if (pres < 0) { die("poll"); }
314  if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink");
315  } while (pres == HSDR_POLL_MORE);
316 
317  if (data_sz == 0 && poll_sz == 0) {
318  fres = heatshrink_decoder_finish(hsd);
319  if (fres < 0) { die("finish"); }
320  if (fres == HSDR_FINISH_DONE) { return 1; }
321  }
322  } while (sunk < data_sz);
323 
324  return 0;
325 }
326 
327 static int decode(config *cfg) {
328  uint8_t window_sz2 = cfg->window_sz2;
329  size_t window_sz = 1 << window_sz2;
330  size_t ibs = cfg->decoder_input_buffer_size;
332  window_sz2, cfg->lookahead_sz2);
333  if (hsd == NULL) { die("failed to init decoder"); }
334 
335  ssize_t read_sz = 0;
336 
337  io_handle *in = cfg->in;
338 
339  HSD_finish_res fres;
340 
341  /* Process input until end of stream */
342  while (1) {
343  uint8_t *input = NULL;
344  read_sz = handle_read(in, window_sz, &input);
345  if (input == NULL) {
346  printf("handle read failure\n");
347  die("read");
348  }
349  if (read_sz == 0) {
350  fres = heatshrink_decoder_finish(hsd);
351  if (fres < 0) { die("finish"); }
352  if (fres == HSDR_FINISH_DONE) break;
353  } else if (read_sz < 0) {
354  die("read");
355  } else {
356  if (decoder_sink_read(cfg, hsd, input, read_sz)) { break; }
357  if (handle_drop(in, read_sz) < 0) { die("drop"); }
358  }
359  }
360  if (read_sz == -1) { err(1, "read"); }
361 
363  close_and_report(cfg);
364  return 0;
365 }
366 
367 static void report(config *cfg) {
368  size_t inb = cfg->in->total;
369  size_t outb = cfg->out->total;
370  fprintf(cfg->out->fd == STDOUT_FILENO ? stderr : stdout,
371  "%s %0.2f %%\t %zd -> %zd (-w %u -l %u)\n",
372  cfg->in_fname, 100.0 - (100.0 * outb) / inb, inb, outb,
373  cfg->window_sz2, cfg->lookahead_sz2);
374 }
375 
376 static void proc_args(config *cfg, int argc, char **argv) {
377  cfg->window_sz2 = DEF_WINDOW_SZ2;
381  cfg->cmd = OP_ENC;
382  cfg->verbose = 0;
383  cfg->in_fname = "-";
384  cfg->out_fname = "-";
385 
386  int a = 0;
387  while ((a = getopt(argc, argv, "hedi:w:l:v")) != -1) {
388  switch (a) {
389  case 'h': /* help */
390  usage();
391  case 'e': /* encode */
392  cfg->cmd = OP_ENC; break;
393  case 'd': /* decode */
394  cfg->cmd = OP_DEC; break;
395  case 'i': /* input buffer size */
396  cfg->decoder_input_buffer_size = atoi(optarg);
397  break;
398  case 'w': /* window bits */
399  cfg->window_sz2 = atoi(optarg);
400  break;
401  case 'l': /* lookahead bits */
402  cfg->lookahead_sz2 = atoi(optarg);
403  break;
404  case 'v': /* verbosity++ */
405  cfg->verbose++;
406  break;
407  case '?': /* unknown argument */
408  default:
409  usage();
410  }
411  }
412  argc -= optind;
413  argv += optind;
414  if (argc > 0) {
415  cfg->in_fname = argv[0];
416  argc--;
417  argv++;
418  }
419  if (argc > 0) { cfg->out_fname = argv[0]; }
420 }
421 
422 int main(int argc, char **argv) {
423  config cfg;
424  memset(&cfg, 0, sizeof(cfg));
425  proc_args(&cfg, argc, argv);
426 
427  if (0 == strcmp(cfg.in_fname, cfg.out_fname)
428  && (0 != strcmp("-", cfg.in_fname))) {
429  printf("Refusing to overwrite file '%s' with itself.\n", cfg.in_fname);
430  exit(1);
431  }
432 
433  cfg.in = handle_open(cfg.in_fname, IO_READ, cfg.buffer_size);
434  if (cfg.in == NULL) { die("Failed to open input file for read"); }
435  cfg.out = handle_open(cfg.out_fname, IO_WRITE, cfg.buffer_size);
436  if (cfg.out == NULL) { die("Failed to open output file for write"); }
437 
438  if (cfg.cmd == OP_ENC) {
439  return encode(&cfg);
440  } else if (cfg.cmd == OP_DEC) {
441  return decode(&cfg);
442  } else {
443  usage();
444  }
445 }
#define free(x)
Definition: platform.h:20
static const int version_major
Definition: heatshrink.c:23
IO_mode
Definition: heatshrink.c:65
#define DEF_BUFFER_SIZE
Definition: heatshrink.c:15
size_t total
Definition: heatshrink.c:74
HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, uint8_t *out_buf, size_t out_buf_size, size_t *output_size)
#define HEATSHRINK_AUTHOR
size_t fill
Definition: heatshrink.c:71
#define memset(x, a, b)
Definition: platform.h:21
static void usage(void)
Definition: heatshrink.c:29
uint8_t window_sz2
Definition: heatshrink.c:79
#define LOG(...)
Definition: heatshrink.c:20
#define NULL
Definition: def.h:47
uint8_t verbose
Definition: heatshrink.c:83
uint8_t buf[]
Definition: heatshrink.c:75
size_t size
Definition: heatshrink.c:73
static void handle_close(io_handle *io)
Definition: heatshrink.c:203
heatshrink_decoder * heatshrink_decoder_alloc(uint16_t input_buffer_size, uint8_t window_sz2, uint8_t lookahead_sz2)
size_t buffer_size
Definition: heatshrink.c:82
uint8_t lookahead_sz2
Definition: heatshrink.c:80
#define HEATSHRINK_VERSION_PATCH
size_t read
Definition: heatshrink.c:72
static const char author[]
Definition: heatshrink.c:26
#define DEF_LOOKAHEAD_SZ2
Definition: heatshrink.c:13
#define malloc(x)
Definition: platform.h:19
size_t decoder_input_buffer_size
Definition: heatshrink.c:81
HSE_sink_res
io_handle * in
Definition: heatshrink.c:87
HSD_poll_res
char * out_fname
Definition: heatshrink.c:86
static void report(config *cfg)
Definition: heatshrink.c:367
void heatshrink_decoder_free(heatshrink_decoder *hsd)
heatshrink_encoder * heatshrink_encoder_alloc(uint8_t window_sz2, uint8_t lookahead_sz2)
HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd)
#define DEF_DECODER_INPUT_BUFFER_SIZE
Definition: heatshrink.c:14
static int handle_drop(io_handle *io, size_t size)
Definition: heatshrink.c:169
HSE_finish_res
static void close_and_report(config *cfg)
Definition: heatshrink.c:216
static heatshrink_decoder hsd
HSD_sink_res
void heatshrink_encoder_free(heatshrink_encoder *hse)
static const int version_patch
Definition: heatshrink.c:25
int main(int argc, char **argv)
Definition: heatshrink.c:422
IO_mode mode
Definition: heatshrink.c:70
static io_handle * handle_open(char *fname, IO_mode m, size_t buf_sz)
Definition: heatshrink.c:99
HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse)
HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, uint8_t *in_buf, size_t size, size_t *input_size)
static const char url[]
Definition: heatshrink.c:27
HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, uint8_t *out_buf, size_t out_buf_size, size_t *output_size)
io_handle * out
Definition: heatshrink.c:88
HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, uint8_t *in_buf, size_t size, size_t *input_size)
static int decode(config *cfg)
Definition: heatshrink.c:327
Operation cmd
Definition: heatshrink.c:84
static const int version_minor
Definition: heatshrink.c:24
#define memcpy(x, a, b)
Definition: platform.h:22
static void proc_args(config *cfg, int argc, char **argv)
Definition: heatshrink.c:376
HSD_finish_res
static void die(char *msg)
Definition: heatshrink.c:91
char * in_fname
Definition: heatshrink.c:85
static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input)
Definition: heatshrink.c:185
#define printf(...)
Definition: platform.h:13
Operation
Definition: heatshrink.c:66
#define HEATSHRINK_VERSION_MINOR
#define DEF_WINDOW_SZ2
Definition: heatshrink.c:12
#define HEATSHRINK_VERSION_MAJOR
#define HEATSHRINK_URL
#define strcmp(a, b)
Definition: platform.h:17
static int encoder_sink_read(config *cfg, heatshrink_encoder *hse, uint8_t *data, size_t data_sz)
Definition: heatshrink.c:224
static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd, uint8_t *data, size_t data_sz)
Definition: heatshrink.c:290
static heatshrink_encoder hse
static int encode(config *cfg)
Definition: heatshrink.c:259
HSE_poll_res
static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf)
Definition: heatshrink.c:133