add tANS application
authorGeoffrey Allott <geoffrey@allott.email>
Mon, 15 Aug 2022 20:08:08 +0000 (21:08 +0100)
committerGeoffrey Allott <geoffrey@allott.email>
Mon, 15 Aug 2022 20:08:08 +0000 (21:08 +0100)
Makefile
src/tANS.c [new file with mode: 0644]
src/tANS_encode_st.c
src/tANS_freq_tbl.c

index 1c73663bca56d4969ff5be631cbee37d6a890c25..bedc284ad041c0ff80397acc1d9f5002c4300aa4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-CFLAGS = -Isrc -g -O2 -Wall -Wextra -Wconversion -fsanitize=undefined -fsanitize=address
+CFLAGS = -Isrc -g -O2 -Wall -Wextra -Wconversion -fsanitize=undefined -fsanitize=address -ftrivial-auto-var-init=pattern
 LDFLAGS = -lasan -lubsan
 
 OBJS = $(patsubst %.c, %.o, $(wildcard src/*.o))
@@ -18,6 +18,7 @@ $(TEST_OBJS): test/test.h $(OBJS)
 
 test/test_tANS.o: src/tANS_decode_st.h src/tANS_decode_tbl.h src/tANS_encode_st.h src/tANS_encode_tbl.h src/tANS_symbol_tbl.h src/tANS_freq_tbl.h
 test/test_tANS: src/tANS_decode_st.o src/tANS_decode_tbl.o src/tANS_encode_st.o src/tANS_encode_tbl.o src/tANS_symbol_tbl.o src/tANS_freq_tbl.o
+src/tANS: $(OBJS)
 
 clean:
        rm -f $(OBJS)
diff --git a/src/tANS.c b/src/tANS.c
new file mode 100644 (file)
index 0000000..371e2ae
--- /dev/null
@@ -0,0 +1,120 @@
+/* 
+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;
+        }
+    }
+}
index 8eb9d017451d84f50455454567988319029d7136..763c284e6301089fd37cbb686c94c64ba4afcf2c 100644 (file)
@@ -9,7 +9,7 @@ void tANS_encode_st_init(struct tANS_encode_st *self, const struct tANS_symbol_t
 
 static inline uint32_t get_uint32(uint8_t buf[const static 4])
 {
-    return (uint32_t) buf[0] | (uint32_t) (buf[1] << 8) | (uint32_t) (buf[2] << 16) | (uint32_t) (buf[3] << 24);
+    return (uint32_t) buf[0] | (uint32_t) buf[1] << 8 | (uint32_t) buf[2] << 16 | (uint32_t) buf[3] << 24;
 }
 
 static inline void set_uint32(uint8_t buf[static 4], uint32_t value)
index 841b4f9df22c2a2d717602ca1be77f3ec26706e8..41c5bb2dd2f4cb928817fe995b56807c695517c1 100644 (file)
@@ -19,6 +19,7 @@ int tANS_freq_tbl_init(struct tANS_freq_tbl *self, uint16_t n_symbols, double *p
     for (i = 0; i < n_symbols; ++i) {
         total_p += p[i];
     }
+    if (total_p == 0.0) total_p = 1.0;
 
     total = 0;
     for (i = 0; i < n_symbols; ++i) {