diff options
Diffstat (limited to 'icecompr/icecompr.cc')
-rw-r--r-- | icecompr/icecompr.cc | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/icecompr/icecompr.cc b/icecompr/icecompr.cc new file mode 100644 index 0000000..322de9a --- /dev/null +++ b/icecompr/icecompr.cc @@ -0,0 +1,316 @@ +/* + * IceCompr -- A simple compressor for iCE40 bit-streams + * + * Copyright (C) 2017 Clifford Wolf <clifford@clifford.at> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <vector> +#include <map> + +int verbose = 0; + +static void push_int_bits(std::vector<bool> &outbits, int value, int bits) +{ + while (bits-- > 0) + outbits.push_back((value >> bits) & 1); +} + +static void push_zero_bits(std::vector<bool> &outbits, int bits) +{ + while (bits-- > 0) + outbits.push_back(false); +} + +static int decode_int_from_bits(const std::vector<bool> &inbits, int &cursor, int bits) +{ + int ret = 0; + while (bits-- > 0) + if (inbits.at(cursor++)) + ret |= 1 << bits; + return ret; +} + +void ice_compress(std::vector<bool> &outbits, const std::vector<bool> &inbits) +{ + int opcode_stats_d4 = 0; + int opcode_stats_d32 = 0; + int opcode_stats_d256 = 0; + int opcode_stats_raw = 0; + int opcode_stats_d8M = 0; + int opcode_stats_end = 0; + + std::vector<int> deltas; + int numzeros = 0; + + for (auto bit : inbits) + { + if (bit) { + deltas.push_back(numzeros); + numzeros = 0; + } else { + numzeros++; + } + } + + for (int i = 0; i < int(deltas.size()); i++) + { + int raw_len = 0; + int compr_len = 0; + int best_compr_raw_diff = -1; + int best_compr_raw_idx = -1; + int best_compr_raw_len = -1; + + for (int j = 0; j+i < int(deltas.size()); j++) + { + int delta = deltas.at(i + j); + raw_len += delta + 1; + + if (delta < 4) + compr_len += 3; + else if (delta < 32) + compr_len += 7; + else if (delta < 256) + compr_len += 11; + else + compr_len += 26; + + if (compr_len - raw_len < std::max(best_compr_raw_diff - 4, 0) || raw_len > 64) + break; + + if (compr_len - raw_len > best_compr_raw_diff) { + best_compr_raw_diff = compr_len - raw_len; + best_compr_raw_idx = j; + best_compr_raw_len = raw_len; + } + } + + if (best_compr_raw_diff > 9) + { + opcode_stats_raw++; + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(true); + push_int_bits(outbits, best_compr_raw_len-1, 6); + + for (int j = 0; j <= best_compr_raw_idx; j++) { + int delta = deltas.at(i + j); + for (int k = 0; k < delta; k++) + outbits.push_back(false); + if (j < best_compr_raw_idx) + outbits.push_back(true); + } + + i += best_compr_raw_idx; + continue; + } + + int delta = deltas.at(i); + + if (delta < 4) { + opcode_stats_d4++; + outbits.push_back(true); + push_int_bits(outbits, delta, 2); + } else + if (delta < 32) { + opcode_stats_d32++; + outbits.push_back(false); + outbits.push_back(true); + push_int_bits(outbits, delta, 5); + } else + if (delta < 256) { + opcode_stats_d256++; + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(true); + push_int_bits(outbits, delta, 8); + } else { + opcode_stats_d8M++; + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(true); + push_int_bits(outbits, delta, 23); + } + } + + opcode_stats_end++; + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + outbits.push_back(false); + push_int_bits(outbits, numzeros, 23); + + if (verbose > 1) { + fprintf(stderr, "opcode d4 %5d\n", opcode_stats_d4); + fprintf(stderr, "opcode d32 %5d\n", opcode_stats_d32); + fprintf(stderr, "opcode d256 %5d\n", opcode_stats_d256); + fprintf(stderr, "opcode raw %5d\n", opcode_stats_raw); + fprintf(stderr, "opcode d8M %5d\n", opcode_stats_d8M); + fprintf(stderr, "opcode end %5d\n", opcode_stats_end); + } +} + +void ice_uncompress(std::vector<bool> &outbits, const std::vector<bool> &inbits) +{ + int cursor = 0; + + while (cursor < int(inbits.size())) + { + if (inbits.at(cursor++)) { + int zeros = decode_int_from_bits(inbits, cursor, 2); + push_zero_bits(outbits, zeros); + outbits.push_back(true); + } else + if (inbits.at(cursor++)) { + int zeros = decode_int_from_bits(inbits, cursor, 5); + push_zero_bits(outbits, zeros); + outbits.push_back(true); + } else + if (inbits.at(cursor++)) { + int zeros = decode_int_from_bits(inbits, cursor, 8); + push_zero_bits(outbits, zeros); + outbits.push_back(true); + } else + if (inbits.at(cursor++)) { + int raw_len = decode_int_from_bits(inbits, cursor, 6); + while (raw_len--) + outbits.push_back(inbits.at(cursor++)); + outbits.push_back(true); + } else + if (inbits.at(cursor++)) { + int zeros = decode_int_from_bits(inbits, cursor, 23); + push_zero_bits(outbits, zeros); + outbits.push_back(true); + } else { + int zeros = decode_int_from_bits(inbits, cursor, 23); + push_zero_bits(outbits, zeros); + } + } +} + +void help() +{ + printf("\n"); + printf("Usage: icecompr [-v] [input-file [output-file]]\n"); + printf("\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + FILE *input_file = stdin; + FILE *output_file = stdout; + + int opt; + while ((opt = getopt(argc, argv, "v")) != -1) + { + switch (opt) + { + case 'v': + verbose++; + break; + default: + help(); + } + } + + if (optind < argc) { + input_file = fopen(argv[optind], "rb"); + if (input_file == NULL) { + fprintf(stderr, "Failed to open input file `%s': %s\n", argv[optind], strerror(errno)); + return 1; + } + optind++; + } + + if (optind < argc) { + output_file = fopen(argv[optind], "wb"); + if (output_file == NULL) { + fprintf(stderr, "Failed to open output file `%s': %s\n", argv[optind], strerror(errno)); + return 1; + } + optind++; + } + + if (optind != argc) + help(); + + std::vector<bool> original_bits; + int count_set_bits = 0; + + while (1) + { + int byte = fgetc(input_file); + + if (byte < 0) + break; + + // MSB first + for (int i = 7; i >= 0; i--) { + bool bit = (byte >> i) & 1; + if (bit) count_set_bits++; + original_bits.push_back(bit); + } + } + + int uncompressed_size = original_bits.size(); + + if (verbose > 0) { + fprintf(stderr, "Percentage of set bits: %.2f%%\n", (100.0*count_set_bits) / uncompressed_size); + fprintf(stderr, "Uncompressed size: %8d bits\n", uncompressed_size); + } + + std::vector<bool> compressed_bits; + ice_compress(compressed_bits, original_bits); + + int compressed_size = compressed_bits.size(); + + if (verbose > 0) { + fprintf(stderr, "Compressed size: %8d bits\n", compressed_size); + fprintf(stderr, "Space savings: %.2f%%\n", 100 - (100.0*compressed_size) / uncompressed_size); + } + + std::vector<bool> uncompressed_bits; + ice_uncompress(uncompressed_bits, compressed_bits); + + bool check_ok = original_bits == uncompressed_bits; + + if (verbose > 0 || !check_ok) { + fprintf(stderr, "Integrity check: %s\n", check_ok ? "OK" : "ERROR"); + if (!check_ok) + return 1; + } + + fprintf(output_file, "ICECOMPR"); + for (int i = 0; i < int(compressed_bits.size()); i += 8) { + int value = 0; + for (int j = 0; j < 8 && i+j < int(compressed_bits.size()); j++) + if (compressed_bits.at(i+j)) + value |= 1 << (7-j); + fputc(value, output_file); + } + + return 0; +} + |