ESP8266 part of the f105-motor-demo project (see f105-motor-demo_stm32)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

446 lines
13 KiB

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <err.h>
#include <fcntl.h>
#include "heatshrink_encoder.h"
#include "heatshrink_decoder.h"
#define DEF_WINDOW_SZ2 11
#define DEF_LOOKAHEAD_SZ2 4
#define DEF_DECODER_INPUT_BUFFER_SIZE 256
#define DEF_BUFFER_SIZE (64 * 1024)
#if 0
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#else
#define LOG(...) /* NO-OP */
#endif
static const int version_major = HEATSHRINK_VERSION_MAJOR;
static const int version_minor = HEATSHRINK_VERSION_MINOR;
static const int version_patch = HEATSHRINK_VERSION_PATCH;
static const char author[] = HEATSHRINK_AUTHOR;
static const char url[] = HEATSHRINK_URL;
static void usage(void) {
fprintf(stderr, "heatshrink version %u.%u.%u by %s\n",
version_major, version_minor, version_patch, author);
fprintf(stderr, "Home page: %s\n\n", url);
fprintf(stderr,
"Usage:\n"
" heatshrink [-h] [-e|-d] [-v] [-w SIZE] [-l BITS] [IN_FILE] [OUT_FILE]\n"
"\n"
"heatshrink compresses or uncompresses byte streams using LZSS, and is\n"
"designed especially for embedded, low-memory, and/or hard real-time\n"
"systems.\n"
"\n"
" -h print help\n"
" -e encode (compress, default)\n"
" -d decode (uncompress)\n"
" -v verbose (print input & output sizes, compression ratio, etc.)\n"
"\n"
" -w SIZE Base-2 log of LZSS sliding window size\n"
"\n"
" A larger value allows searches a larger history of the data for repeated\n"
" patterns, potentially compressing more effectively, but will use\n"
" more memory and processing time.\n"
" Recommended default: -w 8 (embedded systems), -w 10 (elsewhere)\n"
" \n"
" -l BITS Number of bits used for back-reference lengths\n"
"\n"
" A larger value allows longer substitutions, but since all\n"
" back-references must use -w + -l bits, larger -w or -l can be\n"
" counterproductive if most patterns are small and/or local.\n"
" Recommended default: -l 4\n"
"\n"
" If IN_FILE or OUT_FILE are unspecified, they will default to\n"
" \"-\" for standard input and standard output, respectively.\n");
exit(1);
}
typedef enum { IO_READ, IO_WRITE, } IO_mode;
typedef enum { OP_ENC, OP_DEC, } Operation;
typedef struct {
int fd; /* file descriptor */
IO_mode mode;
size_t fill; /* fill index */
size_t read; /* read index */
size_t size;
size_t total;
uint8_t buf[];
} io_handle;
typedef struct {
uint8_t window_sz2;
uint8_t lookahead_sz2;
size_t decoder_input_buffer_size;
size_t buffer_size;
uint8_t verbose;
Operation cmd;
char *in_fname;
char *out_fname;
io_handle *in;
io_handle *out;
} config;
static void die(char *msg) {
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
static void report(config *cfg);
/* Open an IO handle. Returns NULL on error. */
static io_handle *handle_open(char *fname, IO_mode m, size_t buf_sz) {
io_handle *io = NULL;
io = malloc(sizeof(*io) + buf_sz);
if (io == NULL) { return NULL; }
memset(io, 0, sizeof(*io) + buf_sz);
io->fd = -1;
io->size = buf_sz;
io->mode = m;
if (m == IO_READ) {
if (0 == strcmp("-", fname)) {
io->fd = STDIN_FILENO;
} else {
io->fd = open(fname, O_RDONLY);
}
} else if (m == IO_WRITE) {
if (0 == strcmp("-", fname)) {
io->fd = STDOUT_FILENO;
} else {
io->fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC /*| O_EXCL*/, 0644);
}
}
if (io->fd == -1) { /* failed to open */
free(io);
err(1, "open");
return NULL;
}
return io;
}
/* Read SIZE bytes from an IO handle and return a pointer to the content.
* BUF contains at least size_t bytes. Returns 0 on EOF, -1 on error. */
static ssize_t handle_read(io_handle *io, size_t size, uint8_t **buf) {
LOG("@ read %zd\n", size);
if (buf == NULL) { return -1; }
if (size > io->size) {
printf("size %zd, io->size %zd\n", size, io->size);
return -1;
}
if (io->mode != IO_READ) { return -1; }
size_t rem = io->fill - io->read;
if (rem >= size) {
*buf = &io->buf[io->read];
return size;
} else { /* read and replenish */
if (io->fd == -1) { /* already closed, return what we've got */
*buf = &io->buf[io->read];
return rem;
}
memmove(io->buf, &io->buf[io->read], rem);
io->fill -= io->read;
io->read = 0;
ssize_t read_sz = read(io->fd, &io->buf[io->fill], io->size - io->fill);
if (read_sz < 0) { err(1, "read"); }
io->total += read_sz;
if (read_sz == 0) { /* EOF */
if (close(io->fd) < 0) { err(1, "close"); }
io->fd = -1;
}
io->fill += read_sz;
*buf = io->buf;
return io->fill > size ? size : io->fill;
}
}
/* Drop the oldest SIZE bytes from the buffer. Returns <0 on error. */
static int handle_drop(io_handle *io, size_t size) {
LOG("@ drop %zd\n", size);
if (io->read + size <= io->fill) {
io->read += size;
} else {
return -1;
}
if (io->read == io->fill) {
io->read = 0;
io->fill = 0;
}
return 0;
}
/* Sink SIZE bytes from INPUT into the io handle. Returns the number of
* bytes written, or -1 on error. */
static ssize_t handle_sink(io_handle *io, size_t size, uint8_t *input) {
LOG("@ sink %zd\n", size);
if (size > io->size) { return -1; }
if (io->mode != IO_WRITE) { return -1; }
if (io->fill + size > io->size) {
ssize_t written = write(io->fd, io->buf, io->fill);
LOG("@ flushing %zd, wrote %zd\n", io->fill, written);
io->total += written;
if (written == -1) { err(1, "write"); }
memmove(io->buf, &io->buf[written], io->fill - written);
io->fill -= written;
}
memcpy(&io->buf[io->fill], input, size);
io->fill += size;
return size;
}
static void handle_close(io_handle *io) {
if (io->fd != -1) {
if (io->mode == IO_WRITE) {
ssize_t written = write(io->fd, io->buf, io->fill);
io->total += written;
LOG("@ close: flushing %zd, wrote %zd\n", io->fill, written);
if (written == -1) { err(1, "write"); }
}
close(io->fd);
io->fd = -1;
}
}
static void close_and_report(config *cfg) {
handle_close(cfg->in);
handle_close(cfg->out);
if (cfg->verbose) { report(cfg); }
free(cfg->in);
free(cfg->out);
}
static int encoder_sink_read(config *cfg, heatshrink_encoder *hse,
uint8_t *data, size_t data_sz) {
size_t out_sz = 4096;
uint8_t out_buf[out_sz];
memset(out_buf, 0, out_sz);
size_t sink_sz = 0;
size_t poll_sz = 0;
HSE_sink_res sres;
HSE_poll_res pres;
HSE_finish_res fres;
io_handle *out = cfg->out;
size_t sunk = 0;
do {
if (data_sz > 0) {
sres = heatshrink_encoder_sink(hse, &data[sunk], data_sz - sunk, &sink_sz);
if (sres < 0) { die("sink"); }
sunk += sink_sz;
}
do {
pres = heatshrink_encoder_poll(hse, out_buf, out_sz, &poll_sz);
if (pres < 0) { die("poll"); }
if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink");
} while (pres == HSER_POLL_MORE);
if (poll_sz == 0 && data_sz == 0) {
fres = heatshrink_encoder_finish(hse);
if (fres < 0) { die("finish"); }
if (fres == HSER_FINISH_DONE) { return 1; }
}
} while (sunk < data_sz);
return 0;
}
static int encode(config *cfg) {
uint8_t window_sz2 = cfg->window_sz2;
size_t window_sz = 1 << window_sz2;
heatshrink_encoder *hse = heatshrink_encoder_alloc(window_sz2, cfg->lookahead_sz2);
if (hse == NULL) { die("failed to init encoder: bad settings"); }
ssize_t read_sz = 0;
io_handle *in = cfg->in;
/* Process input until end of stream */
while (1) {
uint8_t *input = NULL;
read_sz = handle_read(in, window_sz, &input);
if (input == NULL) {
printf("handle read failure\n");
die("read");
}
if (read_sz < 0) { die("read"); }
/* Pass read to encoder and check if input is fully processed. */
if (encoder_sink_read(cfg, hse, input, read_sz)) break;
if (handle_drop(in, read_sz) < 0) { die("drop"); }
};
if (read_sz == -1) { err(1, "read"); }
heatshrink_encoder_free(hse);
close_and_report(cfg);
return 0;
}
static int decoder_sink_read(config *cfg, heatshrink_decoder *hsd,
uint8_t *data, size_t data_sz) {
io_handle *out = cfg->out;
size_t sink_sz = 0;
size_t poll_sz = 0;
size_t out_sz = 4096;
uint8_t out_buf[out_sz];
memset(out_buf, 0, out_sz);
HSD_sink_res sres;
HSD_poll_res pres;
HSD_finish_res fres;
size_t sunk = 0;
do {
if (data_sz > 0) {
sres = heatshrink_decoder_sink(hsd, &data[sunk], data_sz - sunk, &sink_sz);
if (sres < 0) { die("sink"); }
sunk += sink_sz;
}
do {
pres = heatshrink_decoder_poll(hsd, out_buf, out_sz, &poll_sz);
if (pres < 0) { die("poll"); }
if (handle_sink(out, poll_sz, out_buf) < 0) die("handle_sink");
} while (pres == HSDR_POLL_MORE);
if (data_sz == 0 && poll_sz == 0) {
fres = heatshrink_decoder_finish(hsd);
if (fres < 0) { die("finish"); }
if (fres == HSDR_FINISH_DONE) { return 1; }
}
} while (sunk < data_sz);
return 0;
}
static int decode(config *cfg) {
uint8_t window_sz2 = cfg->window_sz2;
size_t window_sz = 1 << window_sz2;
size_t ibs = cfg->decoder_input_buffer_size;
heatshrink_decoder *hsd = heatshrink_decoder_alloc(ibs,
window_sz2, cfg->lookahead_sz2);
if (hsd == NULL) { die("failed to init decoder"); }
ssize_t read_sz = 0;
io_handle *in = cfg->in;
HSD_finish_res fres;
/* Process input until end of stream */
while (1) {
uint8_t *input = NULL;
read_sz = handle_read(in, window_sz, &input);
if (input == NULL) {
printf("handle read failure\n");
die("read");
}
if (read_sz == 0) {
fres = heatshrink_decoder_finish(hsd);
if (fres < 0) { die("finish"); }
if (fres == HSDR_FINISH_DONE) break;
} else if (read_sz < 0) {
die("read");
} else {
if (decoder_sink_read(cfg, hsd, input, read_sz)) { break; }
if (handle_drop(in, read_sz) < 0) { die("drop"); }
}
}
if (read_sz == -1) { err(1, "read"); }
heatshrink_decoder_free(hsd);
close_and_report(cfg);
return 0;
}
static void report(config *cfg) {
size_t inb = cfg->in->total;
size_t outb = cfg->out->total;
fprintf(cfg->out->fd == STDOUT_FILENO ? stderr : stdout,
"%s %0.2f %%\t %zd -> %zd (-w %u -l %u)\n",
cfg->in_fname, 100.0 - (100.0 * outb) / inb, inb, outb,
cfg->window_sz2, cfg->lookahead_sz2);
}
static void proc_args(config *cfg, int argc, char **argv) {
cfg->window_sz2 = DEF_WINDOW_SZ2;
cfg->lookahead_sz2 = DEF_LOOKAHEAD_SZ2;
cfg->buffer_size = DEF_BUFFER_SIZE;
cfg->decoder_input_buffer_size = DEF_DECODER_INPUT_BUFFER_SIZE;
cfg->cmd = OP_ENC;
cfg->verbose = 0;
cfg->in_fname = "-";
cfg->out_fname = "-";
int a = 0;
while ((a = getopt(argc, argv, "hedi:w:l:v")) != -1) {
switch (a) {
case 'h': /* help */
usage();
case 'e': /* encode */
cfg->cmd = OP_ENC; break;
case 'd': /* decode */
cfg->cmd = OP_DEC; break;
case 'i': /* input buffer size */
cfg->decoder_input_buffer_size = atoi(optarg);
break;
case 'w': /* window bits */
cfg->window_sz2 = atoi(optarg);
break;
case 'l': /* lookahead bits */
cfg->lookahead_sz2 = atoi(optarg);
break;
case 'v': /* verbosity++ */
cfg->verbose++;
break;
case '?': /* unknown argument */
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc > 0) {
cfg->in_fname = argv[0];
argc--;
argv++;
}
if (argc > 0) { cfg->out_fname = argv[0]; }
}
int main(int argc, char **argv) {
config cfg;
memset(&cfg, 0, sizeof(cfg));
proc_args(&cfg, argc, argv);
if (0 == strcmp(cfg.in_fname, cfg.out_fname)
&& (0 != strcmp("-", cfg.in_fname))) {
printf("Refusing to overwrite file '%s' with itself.\n", cfg.in_fname);
exit(1);
}
cfg.in = handle_open(cfg.in_fname, IO_READ, cfg.buffer_size);
if (cfg.in == NULL) { die("Failed to open input file for read"); }
cfg.out = handle_open(cfg.out_fname, IO_WRITE, cfg.buffer_size);
if (cfg.out == NULL) { die("Failed to open output file for write"); }
if (cfg.cmd == OP_ENC) {
return encode(&cfg);
} else if (cfg.cmd == OP_DEC) {
return decode(&cfg);
} else {
usage();
}
}