/* test_lzwh.c : test harness for LZWH compression against Calgary Corpus */ /* Made by a machine. PUBLIC DOMAIN (CC0-1.0) */ #include "lzwh.h" #include #include #include #include #include #include static uint8_t * load_file(const char *path, size_t *len) { FILE *fp = fopen(path, "rb"); if (!fp) { fprintf(stderr, "cannot open %s\n", path); return NULL; } fseek(fp, 0, SEEK_END); long sz = ftell(fp); fseek(fp, 0, SEEK_SET); if (sz < 0) { fclose(fp); return NULL; } uint8_t *buf = malloc(sz); if (!buf) { fclose(fp); return NULL; } if ((long)fread(buf, 1, sz, fp) != sz) { free(buf); fclose(fp); return NULL; } fclose(fp); *len = sz; return buf; } static double time_ms(struct timespec *start, struct timespec *end) { double s = end->tv_sec - start->tv_sec; double ns = end->tv_nsec - start->tv_nsec; return s * 1000.0 + ns / 1e6; } struct result { char name[64]; size_t orig_size; size_t comp_size; double comp_ms; double decomp_ms; int verified; }; static int test_file(const char *path, const char *name, const struct lzwh_params *params, struct result *r) { size_t src_len; uint8_t *src = load_file(path, &src_len); if (!src) return LZWH_ERR; snprintf(r->name, sizeof(r->name), "%s", name); r->orig_size = src_len; r->verified = 0; /* compress */ uint8_t *comp = NULL; size_t comp_len = 0; struct timespec t0, t1; clock_gettime(CLOCK_MONOTONIC, &t0); int rc = lzwh_compress(src, src_len, &comp, &comp_len, params); clock_gettime(CLOCK_MONOTONIC, &t1); if (rc != LZWH_OK) { fprintf(stderr, "%s: compress failed (%d)\n", name, rc); free(src); return rc; } r->comp_size = comp_len; r->comp_ms = time_ms(&t0, &t1); /* decompress */ uint8_t *decomp = NULL; size_t decomp_len = 0; clock_gettime(CLOCK_MONOTONIC, &t0); rc = lzwh_decompress(comp, comp_len, &decomp, &decomp_len, params); clock_gettime(CLOCK_MONOTONIC, &t1); if (rc != LZWH_OK) { fprintf(stderr, "%s: decompress failed (%d)\n", name, rc); free(src); free(comp); return rc; } r->decomp_ms = time_ms(&t0, &t1); /* verify */ if (decomp_len == src_len && memcmp(decomp, src, src_len) == 0) r->verified = 1; else fprintf(stderr, "%s: VERIFY FAILED (orig=%zu decomp=%zu)\n", name, src_len, decomp_len); free(src); free(comp); free(decomp); return LZWH_OK; } static void print_header(const char *label) { printf("\n=== %s ===\n", label); printf("%-14s %10s %10s %7s %9s %9s %s\n", "File", "Original", "Compressed", "Ratio", "Comp ms", "Decomp ms", "OK"); printf("%-14s %10s %10s %7s %9s %9s %s\n", "--------------", "----------", "----------", "-------", "---------", "---------", "--"); } static void print_result(const struct result *r) { double ratio = r->orig_size ? (double)r->comp_size / r->orig_size * 100.0 : 0.0; printf("%-14s %10zu %10zu %6.1f%% %8.1fms %8.1fms %s\n", r->name, r->orig_size, r->comp_size, ratio, r->comp_ms, r->decomp_ms, r->verified ? "yes" : "FAIL"); } static int test_corpus(const char *dir, const struct lzwh_params *params, const char *label) { DIR *dp = opendir(dir); if (!dp) { fprintf(stderr, "cannot open directory %s\n", dir); return LZWH_ERR; } struct result results[32]; int nresults = 0; size_t total_orig = 0, total_comp = 0; print_header(label); struct dirent *de; while ((de = readdir(dp)) != NULL && nresults < 32) { if (de->d_name[0] == '.') continue; char path[512]; snprintf(path, sizeof(path), "%s/%s", dir, de->d_name); struct stat st; if (stat(path, &st) != 0 || !S_ISREG(st.st_mode)) continue; struct result *r = &results[nresults]; if (test_file(path, de->d_name, params, r) == LZWH_OK) { print_result(r); total_orig += r->orig_size; total_comp += r->comp_size; nresults++; } } closedir(dp); if (nresults > 0 && total_orig > 0) { printf("%-14s %10zu %10zu %6.1f%%\n", "TOTAL", total_orig, total_comp, (double)total_comp / total_orig * 100.0); } return nresults > 0 ? LZWH_OK : LZWH_ERR; } static int test_basic(void) { printf("=== Basic tests ===\n"); int pass = 0, fail = 0; /* test 1: empty input */ { uint8_t *comp, *decomp; size_t clen, dlen; int rc = lzwh_compress(NULL, 0, &comp, &clen, NULL); if (rc == LZWH_OK) { rc = lzwh_decompress(comp, clen, &decomp, &dlen, NULL); if (rc == LZWH_OK && dlen == 0) { printf(" empty input: pass\n"); pass++; } else { printf(" empty input: FAIL (decompress)\n"); fail++; } free(decomp); } else { printf(" empty input: FAIL (compress)\n"); fail++; } free(comp); } /* test 2: single byte */ { uint8_t in = 'A'; uint8_t *comp, *decomp; size_t clen, dlen; int rc = lzwh_compress(&in, 1, &comp, &clen, NULL); if (rc == LZWH_OK) { rc = lzwh_decompress(comp, clen, &decomp, &dlen, NULL); if (rc == LZWH_OK && dlen == 1 && decomp[0] == 'A') { printf(" single byte: pass\n"); pass++; } else { printf(" single byte: FAIL\n"); fail++; } free(decomp); } else { printf(" single byte: FAIL\n"); fail++; } free(comp); } /* test 3: repeated bytes (RLE-like) */ { uint8_t in[256]; memset(in, 'X', sizeof(in)); uint8_t *comp, *decomp; size_t clen, dlen; int rc = lzwh_compress(in, sizeof(in), &comp, &clen, NULL); if (rc == LZWH_OK) { rc = lzwh_decompress(comp, clen, &decomp, &dlen, NULL); if (rc == LZWH_OK && dlen == sizeof(in) && memcmp(decomp, in, sizeof(in)) == 0) { printf(" repeated 256x: pass (ratio %.1f%%)\n", (double)clen / sizeof(in) * 100.0); pass++; } else { printf(" repeated 256x: FAIL\n"); fail++; } free(decomp); } else { printf(" repeated 256x: FAIL\n"); fail++; } free(comp); } /* test 4: round-trip with accel_max=3 */ { const char *text = "the cat sat on the mat the cat sat on the mat"; size_t tlen = strlen(text); struct lzwh_params p; lzwh_params_init(&p); p.accel_max = 3; uint8_t *comp, *decomp; size_t clen, dlen; int rc = lzwh_compress((const uint8_t *)text, tlen, &comp, &clen, &p); if (rc == LZWH_OK) { rc = lzwh_decompress(comp, clen, &decomp, &dlen, &p); if (rc == LZWH_OK && dlen == tlen && memcmp(decomp, text, tlen) == 0) { printf(" accel_max=3: pass (ratio %.1f%%)\n", (double)clen / tlen * 100.0); pass++; } else { printf(" accel_max=3: FAIL (dlen=%zu tlen=%zu)\n", dlen, tlen); fail++; } free(decomp); } else { printf(" accel_max=3: FAIL (compress rc=%d)\n", rc); fail++; } free(comp); } printf(" %d passed, %d failed\n\n", pass, fail); return fail; } int main(int argc, char **argv) { const char *corpus_dir = "calgary"; if (argc > 1) corpus_dir = argv[1]; int failures = test_basic(); struct lzwh_params p1; lzwh_params_init(&p1); p1.accel_max = 1; test_corpus(corpus_dir, &p1, "Standard LZW (accel=1)"); struct lzwh_params p3; lzwh_params_init(&p3); p3.accel_max = 3; test_corpus(corpus_dir, &p3, "Horspool LZW (accel=3)"); struct lzwh_params p5; lzwh_params_init(&p5); p5.accel_max = 5; test_corpus(corpus_dir, &p5, "Horspool LZW (accel=5)"); return failures ? 1 : 0; }