From eec10374c51a88f6b5baced5524eda99a10df60a Mon Sep 17 00:00:00 2001
From: Mathis Rosenhauer <rosenhauer@dkrz.de>
Date: Tue, 4 Apr 2023 17:08:12 +0200
Subject: [PATCH] Test aec_buffer_seek()

---
 include/libaec.h.in   |   3 +
 tests/CMakeLists.txt  |   3 +
 tests/Makefile.am     |   7 +-
 tests/check_seeking.c | 232 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 243 insertions(+), 2 deletions(-)
 create mode 100644 tests/check_seeking.c

diff --git a/include/libaec.h.in b/include/libaec.h.in
index a38bc70..85b3de3 100644
--- a/include/libaec.h.in
+++ b/include/libaec.h.in
@@ -160,6 +160,9 @@ LIBAEC_DLL_EXPORTED int aec_decode_end(struct aec_stream *strm);
 /***************************************************************/
 LIBAEC_DLL_EXPORTED int aec_buffer_encode(struct aec_stream *strm);
 LIBAEC_DLL_EXPORTED int aec_buffer_decode(struct aec_stream *strm);
+LIBAEC_DLL_EXPORTED int aec_buffer_seek(struct aec_stream *strm,
+                                        size_t byte_offset,
+                                        unsigned char bit_offset);
 
 #ifdef __cplusplus
 }
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 52d36ff..77f4918 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,6 +6,9 @@ add_test(NAME check_code_options COMMAND check_code_options)
 add_executable(check_buffer_sizes check_buffer_sizes.c)
 target_link_libraries(check_buffer_sizes PUBLIC check_aec aec)
 add_test(NAME check_buffer_sizes COMMAND check_buffer_sizes)
+add_executable(check_seeking check_seeking.c)
+target_link_libraries(check_seeking PUBLIC check_aec aec)
+add_test(NAME check_seeking COMMAND check_seeking)
 add_executable(check_long_fs check_long_fs.c)
 target_link_libraries(check_long_fs PUBLIC check_aec aec)
 add_test(NAME check_long_fs COMMAND check_long_fs)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d66304e..f2bfd4d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,13 +1,13 @@
 AUTOMAKE_OPTIONS = color-tests
 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include
 TESTS = check_code_options check_buffer_sizes check_long_fs \
-szcomp.sh sampledata.sh
+check_seeking szcomp.sh sampledata.sh
 TEST_EXTENSIONS = .sh
 CLEANFILES = test.dat test.rz
 check_LTLIBRARIES = libcheck_aec.la
 libcheck_aec_la_SOURCES = check_aec.c check_aec.h
 check_PROGRAMS = check_code_options check_buffer_sizes check_long_fs \
-check_szcomp
+check_szcomp check_seeking
 
 check_code_options_SOURCES = check_code_options.c check_aec.h \
 $(top_builddir)/include/libaec.h
@@ -18,6 +18,9 @@ $(top_builddir)/include/libaec.h
 check_long_fs_SOURCES = check_long_fs.c check_aec.h \
 $(top_builddir)/include/libaec.h
 
+check_seeking_SOURCES = check_seeking.c check_aec.h \
+$(top_builddir)/include/libaec.h
+
 check_szcomp_SOURCES = check_szcomp.c $(top_srcdir)/include/szlib.h
 
 LDADD = libcheck_aec.la $(top_builddir)/src/libaec.la
diff --git a/tests/check_seeking.c b/tests/check_seeking.c
new file mode 100644
index 0000000..2cb9d83
--- /dev/null
+++ b/tests/check_seeking.c
@@ -0,0 +1,232 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "check_aec.h"
+
+#define BUF_SIZE 1024 * 3
+
+void shift_cdata(struct test_state *state, unsigned char *cbuf_unshifted,
+                 int byte_offset, int bit_offset)
+{
+    struct aec_stream *strm = state->strm;
+    unsigned char *dst = state->cbuf + byte_offset;
+
+    memset(state->cbuf, 0, state->buf_len);
+    for (int i = 0; i < strm->avail_in; i++) {
+        dst[i] |= cbuf_unshifted[i] >> bit_offset;
+        dst[i + 1] |= cbuf_unshifted[i] << (8 - bit_offset);
+    }
+}
+
+int encode_decode_large_seek(struct test_state *state)
+{
+    int status;
+    int bflags = 0;
+    int c_len;
+    size_t to;
+    char fbase[1024];
+    unsigned char *cbuf_unshifted;
+    struct aec_stream *strm = state->strm;
+
+    strm->avail_in = state->ibuf_len;
+    strm->avail_out = state->cbuf_len;
+    strm->next_in = state->ubuf;
+    strm->next_out = state->cbuf;
+
+    status = aec_encode_init(strm);
+    if (status != AEC_OK) {
+        printf("Init failed.\n");
+        return 99;
+    }
+    if (state->dump) {
+        char fname[1024 + 4];
+        FILE *fp;
+        snprintf(fbase, sizeof(fbase), "BPS%02iID%iBS%02iRSI%04iFLG%04i",
+                 strm->bits_per_sample,
+                 state->id,
+                 strm->block_size,
+                 strm->rsi,
+                 strm->flags);
+        snprintf(fname, sizeof(fname), "%s.dat", fbase);
+        if ((fp = fopen(fname, "wb")) == NULL) {
+            fprintf(stderr, "ERROR: cannot open dump file %s\n", fname);
+            return 99;
+        }
+        fputc(strm->bits_per_sample, fp);
+        bflags = strm->block_size >> 8;
+        if (strm->flags | AEC_DATA_MSB)
+            bflags |= 0x80;
+        if (strm->flags | AEC_DATA_SIGNED)
+            bflags |= 0x40;
+        if (strm->flags | AEC_DATA_3BYTE)
+            bflags |= 0x10;
+        bflags |= 0x20; /* encode */
+        fputc(bflags, fp);
+        fwrite(strm->next_in, strm->avail_in, 1, fp);
+        fclose(fp);
+    }
+
+    status = aec_encode(strm, AEC_FLUSH);
+    if (status != AEC_OK) {
+        printf("Encode failed.\n");
+        return 99;
+    }
+
+    aec_encode_end(strm);
+
+    if (state->dump) {
+        char fname[1024 + 3];
+        FILE *fp;
+        snprintf(fname, sizeof(fname), "%s.rz", fbase);
+        if ((fp = fopen(fname, "wb")) == NULL) {
+            fprintf(stderr, "ERROR: cannot open dump file %s\n", fname);
+            return 99;
+        }
+        fputc(strm->bits_per_sample, fp);
+        bflags &= ~0x20;
+        fputc(bflags, fp);
+        fwrite(state->cbuf, strm->total_out, 1, fp);
+        fclose(fp);
+    }
+
+    cbuf_unshifted = (unsigned char *)malloc(state->cbuf_len);
+    if (!cbuf_unshifted) {
+        fprintf(stderr, "Not enough memory.\n");
+        return 99;
+    }
+    c_len = strm->total_out;
+    memcpy(cbuf_unshifted, state->cbuf, c_len);
+
+
+    for (int byte_offset = 0; byte_offset < 256; byte_offset++) {
+        for (int bit_offset = 0; bit_offset < 8; bit_offset++) {
+
+            strm->avail_in = c_len;
+            strm->avail_out = state->buf_len;
+            strm->next_in = state->cbuf;
+            strm->next_out = state->obuf;
+            to = strm->total_out;
+            shift_cdata(state, cbuf_unshifted, byte_offset, bit_offset);
+
+            status = aec_decode_init(strm);
+            if (status != AEC_OK) {
+                printf("Init failed.\n");
+                return 99;
+            }
+            status = aec_buffer_seek(strm, byte_offset, bit_offset);
+            if (status != AEC_OK) {
+                printf("Seeking failed.\n");
+                return 99;
+            }
+            status = aec_decode(strm, AEC_FLUSH);
+            if (status != AEC_OK) {
+                printf("Decode failed.\n");
+                return 99;
+            }
+
+            if (memcmp(state->ubuf, state->obuf, state->ibuf_len)) {
+                printf("\n%s: Uncompressed output differs from input.\n",
+                       CHECK_FAIL);
+
+                printf("\nuncompressed buf");
+                for (int i = 0; i < 80; i++) {
+                    if (i % 8 == 0)
+                        printf("\n");
+                    printf("%02x ", state->ubuf[i]);
+                }
+                printf("\n\ncompressed buf len %zu", to);
+                for (int i = 0; i < 80; i++) {
+                    if (i % 8 == 0)
+                        printf("\n");
+                    printf("%02x ", state->cbuf[i]);
+                }
+                printf("\n\ndecompressed buf");
+                for (int i = 0; i < 80; i++) {
+                    if (i % 8 == 0)
+                        printf("\n");
+                    printf("%02x ", state->obuf[i]);
+                }
+                printf("\n");
+                return 99;
+            }
+            aec_decode_end(strm);
+        }
+    }
+    return 0;
+}
+
+int check_block_sizes_seek(struct test_state *state)
+{
+    for (int bs = 8; bs <= 64; bs *= 2) {
+        int status;
+        state->strm->block_size = bs;
+        state->strm->rsi = (int)(state->buf_len
+                                 / (bs * state->bytes_per_sample));
+
+        status = encode_decode_large_seek(state);
+        if (status)
+            return status;
+    }
+    return 0;
+}
+
+int check_rsi_seek(struct test_state *state)
+{
+    int status;
+    int size = state->bytes_per_sample;
+
+    for (unsigned char *tmp = state->ubuf;
+         tmp < state->ubuf + state->buf_len;
+         tmp += 2 * state->bytes_per_sample) {
+        state->out(tmp, state->xmax - ((state->ubuf - tmp) % 64), size);
+        state->out(tmp + size, state->xmin, size);
+    }
+
+    printf("Checking seeking ... ");
+    status = check_block_sizes_seek(state);
+    if (status)
+        return status;
+
+    printf ("%s\n", CHECK_PASS);
+    return 0;
+}
+
+int main (void)
+{
+    int status;
+    struct aec_stream strm;
+    struct test_state state;
+
+    state.dump = 0;
+    state.buf_len = state.ibuf_len = BUF_SIZE;
+    state.cbuf_len = 3 * BUF_SIZE;
+
+    state.ubuf = (unsigned char *)malloc(state.buf_len);
+    state.cbuf = (unsigned char *)malloc(state.cbuf_len);
+    state.obuf = (unsigned char *)malloc(state.buf_len);
+
+    if (!state.ubuf || !state.cbuf || !state.obuf) {
+        printf("Not enough memory.\n");
+        status = 99;
+        goto DESTRUCT;
+    }
+
+    strm.flags = AEC_DATA_PREPROCESS;
+    state.strm = &strm;
+    strm.bits_per_sample = 32;
+    update_state(&state);
+
+    status = check_rsi_seek(&state);
+    if (status)
+        goto DESTRUCT;
+
+DESTRUCT:
+    if (state.ubuf)
+        free(state.ubuf);
+    if (state.cbuf)
+        free(state.cbuf);
+    if (state.obuf)
+        free(state.obuf);
+
+    return status;
+}
-- 
GitLab