--- /dev/null
+/*
+tabled Asymmetric Numeral Systems
+
+author: Geoffrey Allott <geoffrey@allott.email>
+ref: https://arxiv.org/abs/1311.2540
+*/
+
+#include "tANS_encode_st.h"
+#include "tANS_decode_st.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void)
+{
+ printf(
+ "usage: tANS [-hcd] [file...]\n"
+ "\n"
+ "Compress the given files using tabled Asymmetric Numeral Systems\n"
+ "If `-d' is given, decompress instead.\n"
+ );
+}
+
+static int tANS_compress_file(FILE* input, FILE *output)
+{
+ uint32_t i, len, bits;
+ uint8_t read_buf[1048576];
+ uint8_t write_buf[2097152] = {0};
+ double p[256] = {0};
+ struct tANS_freq_tbl freq_tbl;
+ struct tANS_symbol_tbl symbol_tbl;
+ struct tANS_encode_st st;
+ const uint16_t log2_tblsz = 10;
+ uint32_t read_sz = 8;
+
+ while (!feof(input)) {
+ if (tANS_freq_tbl_init(&freq_tbl, 256, p, log2_tblsz) != 0) return -1;
+ if (tANS_symbol_tbl_init(&symbol_tbl, &freq_tbl) != 0) return -1;
+ tANS_encode_st_init(&st, &symbol_tbl);
+
+ len = (uint32_t) fread(read_buf, 1, read_sz, input);
+ for (i = 0; i < len; ++i) ++p[read_buf[i]];
+ if (fwrite(&len, sizeof len, 1, output) != 1) return -1;
+ bits = tANS_encode(&st, read_buf, len, write_buf);
+ if (fwrite(&bits, sizeof bits, 1, output) != 1) return -1;
+ if (fwrite(&st.x, sizeof st.x, 1, output) != 1) return -1;
+ if (fwrite(write_buf, (bits + 7) / 8, 1, output) != 1) return -1;
+
+ memset(write_buf, 0, (bits + 7) / 8);
+
+ read_sz *= 2;
+ if (read_sz > sizeof read_buf) read_sz = sizeof read_buf;
+ }
+
+ return 0;
+}
+
+static int tANS_decompress_file(FILE* input, FILE *output)
+{
+ uint32_t i, len, bits;
+ uint8_t read_buf[2097152];
+ uint8_t write_buf[1048576];
+ double p[256] = {0};
+ struct tANS_freq_tbl freq_tbl;
+ struct tANS_symbol_tbl symbol_tbl;
+ struct tANS_decode_st st;
+ const uint16_t log2_tblsz = 10;
+
+ while (!feof(input)) {
+ if (tANS_freq_tbl_init(&freq_tbl, 256, p, log2_tblsz) != 0) return -1;
+ if (tANS_symbol_tbl_init(&symbol_tbl, &freq_tbl) != 0) return -1;
+ tANS_decode_st_init(&st, &symbol_tbl);
+
+ if (fread(&len, sizeof len, 1, input) != 1) return -1;
+ if (fread(&bits, sizeof bits, 1, input) != 1) return -1;
+ if (fread(&st.x, sizeof st.x, 1, input) != 1) return -1;
+ if (fread(read_buf + 4, (bits + 7) / 8, 1, input) != 1) return -1;
+ st.x &= symbol_tbl.tblsz - 1;
+ bits = tANS_decode(&st, write_buf, len, read_buf + 4, bits);
+ if (bits != 0) {
+ fprintf(stderr, "tANS: corrupted file\n");
+ return -1;
+ }
+ if (fwrite(write_buf, len, 1, output) != 1) return -1;
+ for (i = 0; i < len; ++i) ++p[write_buf[i]];
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int opt, compress = 1;
+
+ while ((opt = getopt(argc, argv, "hcd")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage();
+ return 0;
+ case 'c':
+ compress = 1;
+ break;
+ case 'd':
+ compress = 0;
+ break;
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc == 0) {
+ if (compress) {
+ return tANS_compress_file(stdin, stdout) == 0;
+ } else {
+ return tANS_decompress_file(stdin, stdout) == 0;
+ }
+ }
+}