From ce378021ba2450b3c65d9dbb1e41767cd37f60ff Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Sat, 6 Sep 2025 18:59:18 +0100 Subject: [PATCH] Implement HA's ASC and HSC algorithms. --- CMakeLists.txt | 12 +- ha/acoder.c | 134 ++++++++++ ha/acoder.h | 38 +++ ha/asc.c | 304 +++++++++++++++++++++++ ha/asc.h | 71 ++++++ ha/decompress.c | 70 ++++++ ha/hsc.c | 568 +++++++++++++++++++++++++++++++++++++++++++ ha/hsc.h | 90 +++++++ ha/internal.h | 45 ++++ ha/swdict.c | 99 ++++++++ ha/swdict.h | 45 ++++ library.h | 12 + tests/CMakeLists.txt | 12 +- tests/ha.cpp | 133 ++++++++++ tests/lib | 2 +- 15 files changed, 1629 insertions(+), 6 deletions(-) create mode 100644 ha/acoder.c create mode 100644 ha/acoder.h create mode 100644 ha/asc.c create mode 100644 ha/asc.h create mode 100644 ha/decompress.c create mode 100644 ha/hsc.c create mode 100644 ha/hsc.h create mode 100644 ha/internal.h create mode 100644 ha/swdict.c create mode 100644 ha/swdict.h create mode 100644 tests/ha.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 892de39..a8fc0cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -132,7 +132,17 @@ add_library("Aaru.Compression.Native" SHARED library.c apple_rle.c apple_rle.h a zoo/lzd.c zoo/lzd.h zoo/lzh.c zoo/decode.c zoo/huf.c zoo/io.c zoo/lh5.c zoo/lh5.h zoo/lzh.h zoo/ar.h zoo/maketbl.c arc/pack.c arc/squeeze.c arc/crunch.c arc/lzw.c pak/crush.c pak/distill.c pak/bitstream.c pak/bitstream.h pak/lzw.c pak/lzw.h pak/prefixcode.c - pak/prefixcode.h) + pak/prefixcode.h + ha/acoder.c + ha/acoder.h + ha/asc.c + ha/asc.h + ha/decompress.c + ha/hsc.c + ha/hsc.h + ha/internal.h + ha/swdict.c + ha/swdict.h) include(3rdparty/bzip2.cmake) include(3rdparty/flac.cmake) diff --git a/ha/acoder.c b/ha/acoder.c new file mode 100644 index 0000000..026ec8b --- /dev/null +++ b/ha/acoder.c @@ -0,0 +1,134 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA arithmetic coder +***********************************************************************/ + +/*********************************************************************** +This file contains some small changes made by Nico de Vries (AIP-NL) +allowing it to be compiled with Borland C++ 3.1. +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#include "internal.h" +#include "acoder.h" +#include + +static decompress_context_t *g_ctx = NULL; +static U16B h, l, v; +static S16B gpat; + +/* Get byte from input buffer */ +static int getbyte_from_buffer(void) +{ + if(!g_ctx || g_ctx->input.pos >= g_ctx->input.size) { return -1; } + return g_ctx->input.data[g_ctx->input.pos++]; +} + +/* Bit input from memory buffer */ +static int getbit_from_buffer(void) +{ + if(!g_ctx || g_ctx->input.error) { return -1; } + + gpat <<= 1; + if(!(gpat & 0xff)) + { + int byte = getbyte_from_buffer(); + if(byte < 0) { gpat = 0x100; } + else + { + gpat = byte; + gpat <<= 1; + gpat |= 1; + } + } + return (gpat & 0x100) >> 8; +} + +#define getbit(b) { \ + int _bit = getbit_from_buffer(); \ + if (_bit < 0) { \ + g_ctx->input.error = 1; \ + b = 0; \ + } else { \ + b |= _bit; \ + } \ +} + +void ac_init_decode(decompress_context_t *ctx) +{ + g_ctx = ctx; + h = 0xffff; + l = 0; + gpat = 0; + + int b1 = getbyte_from_buffer(); + int b2 = getbyte_from_buffer(); + + if(b1 < 0 || b2 < 0) + { + ctx->input.error = 1; + v = 0; + return; + } + + v = (b1 << 8) | (0xff & b2); +} + +void ac_in(U16B low, U16B high, U16B tot) +{ + if(!g_ctx || g_ctx->input.error) return; + + U32B r = (U32B)(h - l) + 1; + h = (U16B)(r * high / tot - 1) + l; + l += (U16B)(r * low / tot); + + while(!((h ^ l) & 0x8000)) + { + l <<= 1; + h <<= 1; + h |= 1; + v <<= 1; + getbit(v); + if(g_ctx->input.error) return; + } + + while((l & 0x4000) && !(h & 0x4000)) + { + l <<= 1; + l &= 0x7fff; + h <<= 1; + h |= 0x8001; + v <<= 1; + v ^= 0x8000; + getbit(v); + if(g_ctx->input.error) return; + } +} + +U16B ac_threshold_val(U16B tot) +{ + if(!g_ctx || g_ctx->input.error) return 0; + + U32B r = (U32B)(h - l) + 1; + return (U16B)((((U32B)(v - l) + 1) * tot - 1) / r); +} diff --git a/ha/acoder.h b/ha/acoder.h new file mode 100644 index 0000000..1cdabe0 --- /dev/null +++ b/ha/acoder.h @@ -0,0 +1,38 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA arithmetic coder +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#ifndef ACODER_H +#define ACODER_H + +#include "internal.h" + +void ac_init_decode(decompress_context_t *ctx); + +void ac_in(U16B low, U16B high, U16B tot); + +U16B ac_threshold_val(U16B tot); + +#endif /* ACODER_H */ diff --git a/ha/asc.c b/ha/asc.c new file mode 100644 index 0000000..ae8481b --- /dev/null +++ b/ha/asc.c @@ -0,0 +1,304 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA ASC method +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#include "internal.h" +#include "acoder.h" +#include "swdict.h" +#include "asc.h" +#include +#include + +static decompress_context_t *g_ctx = NULL; + +static void tabinit(U16B t[], U16B tl, U16B ival) +{ + register U16B i, j; + + for(i = tl; i < 2 * tl; ++i) t[i] = ival; + for(i = tl - 1, j = (tl << 1) - 2; i; --i, j -= 2) { t[i] = t[j] + t[j + 1]; } +} + +static void tscale(U16B t[], U16B tl) +{ + register U16B i, j; + + for(i = (tl << 1) - 1; i >= tl; --i) { if(t[i] > 1) t[i] >>= 1; } + for(i = tl - 1, j = (tl << 1) - 2; i; --i, j -= 2) { t[i] = t[j] + t[j + 1]; } +} + +static void tupd(U16B t[], U16B tl, U16B maxt, U16B step, U16B p) +{ + register S16B i; + + for(i = p + tl; i; i >>= 1) t[i] += step; + if(t[1] >= maxt) tscale(t, tl); +} + +static void tzero(U16B t[], U16B tl, U16B p) +{ + register S16B i, step; + + for(i = p + tl, step = t[i]; i; i >>= 1) t[i] -= step; +} + +static void model_init(void) +{ + register S16B i; + + ces = CTSTEP; + les = LTSTEP; + ccnt = 0; + ttcon = 0; + npt = pmax = 1; + for(i = 0; i < TTORD; ++i) ttab[i][0] = ttab[i][1] = TTSTEP; + tabinit(ltab, LTCODES, 0); + tabinit(eltab, LTCODES, 1); + tabinit(ctab, CTCODES, 0); + tabinit(ectab, CTCODES, 1); + tabinit(ptab, PTCODES, 0); + tupd(ptab, PTCODES, MAXPT, PTSTEP, 0); +} + +static void unpack_init(decompress_context_t *ctx) +{ + g_ctx = ctx; + model_init(); + ac_init_decode(ctx); +} + +static void ttscale(U16B con) +{ + ttab[con][0] >>= 1; + if(ttab[con][0] == 0) ttab[con][0] = 1; + ttab[con][1] >>= 1; + if(ttab[con][1] == 0) ttab[con][1] = 1; +} + +static void flush_output(void) +{ + /* Output flushing is handled by the buffer management */ +} + +int asc_unpack(decompress_context_t *ctx) +{ + register U16B l, p, tv, i, lt; + + swd_dinit(ctx, POSCODES); + if(ctx->output.error) return -1; + + unpack_init(ctx); + if(ctx->input.error) + { + swd_cleanup(); + return -1; + } + + for(;;) + { + if(ctx->input.error || ctx->output.error) break; + + tv = ac_threshold_val(ttab[ttcon][0] + ttab[ttcon][1] + 1); + i = ttab[ttcon][0] + ttab[ttcon][1]; + + if(ttab[ttcon][0] > tv) + { + ac_in(0, ttab[ttcon][0], i + 1); + ttab[ttcon][0] += TTSTEP; + if(i >= MAXTT) ttscale(ttcon); + ttcon = (ttcon << 1) & TTOMASK; + + tv = ac_threshold_val(ctab[1] + ces); + if(tv >= ctab[1]) + { + ac_in(ctab[1], ctab[1] + ces, ctab[1] + ces); + tv = ac_threshold_val(ectab[1]); + for(l = 2, lt = 0;;) + { + if(lt + ectab[l] <= tv) + { + lt += ectab[l]; + ++l; + } + if(l >= CTCODES) + { + l -= CTCODES; + break; + } + l <<= 1; + } + ac_in(lt, lt + ectab[CTCODES + l], ectab[1]); + tzero(ectab, CTCODES, l); + if(ectab[1] != 0) ces += CTSTEP; + else ces = 0; + for(i = l < CPLEN ? 0 : l - CPLEN; i < (l + CPLEN >= CTCODES - 1 ? CTCODES - 1 : l + CPLEN); ++i) + { + if(ectab[CTCODES + i]) tupd(ectab, CTCODES, MAXCT, 1, i); + } + } + else + { + for(l = 2, lt = 0;;) + { + if(lt + ctab[l] <= tv) + { + lt += ctab[l]; + l++; + } + if(l >= CTCODES) + { + l -= CTCODES; + break; + } + l <<= 1; + } + ac_in(lt, lt + ctab[CTCODES + l], ctab[1] + ces); + } + tupd(ctab, CTCODES, MAXCT, CTSTEP, l); + if(ctab[CTCODES + l] == CCUTOFF) ces -= CTSTEP < ces ? CTSTEP : ces - 1; + swd_dchar(l); + if(ccnt < POSCODES) ++ccnt; + } + else if(i > tv) + { + ac_in(ttab[ttcon][0], i, i + 1); + ttab[ttcon][1] += TTSTEP; + if(i >= MAXTT) ttscale(ttcon); + ttcon = ((ttcon << 1) | 1) & TTOMASK; + + while(ccnt > pmax) + { + tupd(ptab, PTCODES, MAXPT, PTSTEP, npt++); + pmax <<= 1; + } + + tv = ac_threshold_val(ptab[1]); + for(p = 2, lt = 0;;) + { + if(lt + ptab[p] <= tv) + { + lt += ptab[p]; + p++; + } + if(p >= PTCODES) + { + p -= PTCODES; + break; + } + p <<= 1; + } + ac_in(lt, lt + ptab[PTCODES + p], ptab[1]); + tupd(ptab, PTCODES, MAXPT, PTSTEP, p); + + if(p > 1) + { + for(i = 1; p; i <<= 1, --p); + i >>= 1; + if(i == (pmax >> 1)) l = ccnt - (pmax >> 1); + else l = i; + p = ac_threshold_val(l); + ac_in(p, p + 1, l); + p += i; + } + + tv = ac_threshold_val(ltab[1] + les); + if(tv >= ltab[1]) + { + ac_in(ltab[1], ltab[1] + les, ltab[1] + les); + tv = ac_threshold_val(eltab[1]); + for(l = 2, lt = 0;;) + { + if(lt + eltab[l] <= tv) + { + lt += eltab[l]; + ++l; + } + if(l >= LTCODES) + { + l -= LTCODES; + break; + } + l <<= 1; + } + ac_in(lt, lt + eltab[LTCODES + l], eltab[1]); + tzero(eltab, LTCODES, l); + if(eltab[1] != 0) les += LTSTEP; + else les = 0; + for(i = l < LPLEN ? 0 : l - LPLEN; i < (l + LPLEN >= LTCODES - 1 ? LTCODES - 1 : l + LPLEN); ++i) + { + if(eltab[LTCODES + i]) tupd(eltab, LTCODES, MAXLT, 1, i); + } + } + else + { + for(l = 2, lt = 0;;) + { + if(lt + ltab[l] <= tv) + { + lt += ltab[l]; + ++l; + } + if(l >= LTCODES) + { + l -= LTCODES; + break; + } + l <<= 1; + } + ac_in(lt, lt + ltab[LTCODES + l], ltab[1] + les); + } + tupd(ltab, LTCODES, MAXLT, LTSTEP, l); + if(ltab[LTCODES + l] == LCUTOFF) les -= LTSTEP < les ? LTSTEP : les - 1; + + if(l == SLCODES - 1) l = LENCODES - 1; + else if(l >= SLCODES) + { + i = ac_threshold_val(LLLEN); + ac_in(i, i + 1, LLLEN); + l = ((l - SLCODES) << LLBITS) + i + SLCODES - 1; + } + l += 3; + + if(ccnt < POSCODES) + { + ccnt += l; + if(ccnt > POSCODES) ccnt = POSCODES; + } + swd_dpair(l, p); + } + else + { + ac_in(i, i + 1, i + 1); + flush_output(); + swd_cleanup(); + return 0; + } + + if(ctx->input.error || ctx->output.error) break; + } + + swd_cleanup(); + return -1; +} diff --git a/ha/asc.h b/ha/asc.h new file mode 100644 index 0000000..7dd7c59 --- /dev/null +++ b/ha/asc.h @@ -0,0 +1,71 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA ASC method +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#ifndef ASC_H +#define ASC_H + +#include "internal.h" + +int asc_unpack(decompress_context_t *ctx); + +#define POSCODES 31200 +#define SLCODES 16 +#define LLCODES 48 +#define LLLEN 16 +#define LLBITS 4 +#define LLMASK (LLLEN-1) +#define LENCODES (SLCODES+LLCODES*LLLEN) +#define LTCODES (SLCODES+LLCODES) +#define CTCODES 256 +#define PTCODES 16 +#define LTSTEP 8 +#define MAXLT (750*LTSTEP) +#define CTSTEP 1 +#define MAXCT (1000*CTSTEP) +#define PTSTEP 24 +#define MAXPT (250*PTSTEP) +#define TTSTEP 40 +#define MAXTT (150*TTSTEP) +#define TTORD 4 +#define TTOMASK (TTORD-1) +#define LCUTOFF (3*LTSTEP) +#define CCUTOFF (3*CTSTEP) +#define CPLEN 8 +#define LPLEN 4 +#define MINLENLIM 4096 + +static U16B ltab[2 * LTCODES]; +static U16B eltab[2 * LTCODES]; +static U16B ptab[2 * PTCODES]; +static U16B ctab[2 * CTCODES]; +static U16B ectab[2 * CTCODES]; +static U16B ttab[TTORD][2]; +static U16B ccnt, pmax, npt; +static U16B ces; +static U16B les; +static U16B ttcon; + +#endif /* ASC_H */ diff --git a/ha/decompress.c b/ha/decompress.c new file mode 100644 index 0000000..995aebc --- /dev/null +++ b/ha/decompress.c @@ -0,0 +1,70 @@ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#include "internal.h" +#include "asc.h" +#include "hsc.h" +#include "../library.h" +#include + +int ha_algorithm_decompress(ha_algorithm_t algorithm, + const unsigned char *in_buf, + size_t in_len, + unsigned char * out_buf, + size_t * out_len) +{ + if(!in_buf || !out_buf || !out_len || in_len == 0) { return -1; } + + decompress_context_t ctx; + memset(&ctx, 0, sizeof(ctx)); + + /* Initialize input buffer */ + ctx.input.data = in_buf; + ctx.input.size = in_len; + ctx.input.pos = 0; + ctx.input.error = 0; + + /* Initialize output buffer */ + ctx.output.data = out_buf; + ctx.output.size = 0; + ctx.output.pos = 0; + ctx.output.max_size = *out_len; + ctx.output.error = 0; + + ctx.algorithm = algorithm; + + int result; + switch(algorithm) + { + case HA_ALGORITHM_ASC: + result = asc_unpack(&ctx); + break; + case HA_ALGORITHM_HSC: + result = hsc_unpack(&ctx); + break; + default: + return -1; + } + + *out_len = ctx.output.size; + return result; +} + +AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, + size_t in_len, + unsigned char * out_buf, + size_t * out_len) +{ + return ha_algorithm_decompress(HA_ALGORITHM_ASC, in_buf, in_len, out_buf, out_len); +} + +AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, + size_t in_len, + unsigned char * out_buf, + size_t * out_len) +{ + return ha_algorithm_decompress(HA_ALGORITHM_HSC, in_buf, in_len, out_buf, out_len); +} diff --git a/ha/hsc.c b/ha/hsc.c new file mode 100644 index 0000000..189c095 --- /dev/null +++ b/ha/hsc.c @@ -0,0 +1,568 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA HSC method +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#include "internal.h" +#include "acoder.h" +#include "hsc.h" +#include +#include + +static decompress_context_t *g_ctx = NULL; + +static void hsc_cleanup(void) +{ + if(ht != NULL) free(ht), ht = NULL; + if(fc != NULL) free(fc), fc = NULL; + if(fa != NULL) free(fa), fa = NULL; + if(ft != NULL) free(ft), ft = NULL; + if(fe != NULL) free(fe), fe = NULL; + if(nb != NULL) free(nb), nb = NULL; + if(hp != NULL) free(hp), hp = NULL; + if(elp != NULL) free(elp), elp = NULL; + if(eln != NULL) free(eln), eln = NULL; + if(cl != NULL) free(cl), cl = NULL; + if(cc != NULL) free(cc), cc = NULL; + if(rfm != NULL) free(rfm), rfm = NULL; + if(con != NULL) free(con), con = NULL; +} + +static int putbyte_to_buffer(unsigned char c) +{ + if(!g_ctx || g_ctx->output.pos >= g_ctx->output.max_size) + { + if(g_ctx) g_ctx->output.error = 1; + return -1; + } + + g_ctx->output.data[g_ctx->output.pos++] = c; + if(g_ctx->output.pos > g_ctx->output.size) { g_ctx->output.size = g_ctx->output.pos; } + return 0; +} + +static U16B make_context(unsigned char cl, S16B c); + +static int init_model(void) +{ + register S16B i; + S32B z, l, h, t; + + ht = malloc(HTLEN * sizeof(*ht)); + hp = malloc(NUMCON * sizeof(*hp)); + elp = malloc(NUMCON * sizeof(*elp)); + eln = malloc(NUMCON * sizeof(*eln)); + cl = malloc(NUMCON * sizeof(*cl)); + cc = malloc(NUMCON * sizeof(*cc)); + ft = malloc(NUMCON * sizeof(*ft)); + fe = malloc(NUMCON * sizeof(*fe)); + rfm = malloc(NUMCON * sizeof(*rfm)); + con = malloc(NUMCON * sizeof(*con)); + fc = malloc(NUMCFB * sizeof(*fc)); + fa = malloc(NUMCFB * sizeof(*fa)); + nb = malloc(NUMCFB * sizeof(*nb)); + + if(hp == NULL || elp == NULL || eln == NULL || cl == NULL || rfm == NULL || con == NULL || cc == NULL || ft == NULL + || fe == NULL || fc == NULL || fa == NULL || nb == NULL || ht == NULL) + { + hsc_cleanup(); + return -1; + } + + maxclen = MAXCLEN; + iec[0] = (IECLIM >> 1); + for(i = 1; i <= MAXCLEN; ++i) iec[i] = (IECLIM >> 1) - 1; + dropcnt = NUMCON / 4; + nec = 0; + nrel = 0; + hs[0] = 0; + + for(i = 0; i < HTLEN; ++i) ht[i] = NIL; + for(i = 0; i < NUMCON; ++i) + { + eln[i] = i + 1; + elp[i] = i - 1; + cl[i] = 0xff; + nb[i] = NIL; + } + elf = 0; + ell = NUMCON - 1; + + for(i = NUMCON; i < NUMCFB - 1; ++i) nb[i] = i + 1; + nb[i] = NIL; + fcfbl = NUMCON; + + curcon[3] = curcon[2] = curcon[1] = curcon[0] = 0; + cmsp = 0; + for(i = 0; i < 256; ++i) cmask[i] = 0; + + for(z = 10, i = 0; i < HTLEN; ++i) + { + h = z / (2147483647L / 16807L); + l = z % (2147483647L / 16807L); + if((t = 16807L * l - (2147483647L % 16807L) * h) > 0) z = t; + else z = t + 2147483647L; + hrt[i] = (U16B)z & (HTLEN - 1); + } + + return 0; +} + +static int init_unpack(decompress_context_t *ctx) +{ + g_ctx = ctx; + if(init_model() < 0) return -1; + ac_init_decode(ctx); + return 0; +} + +#define HASH(s,l,h) { \ + h = 0; \ + if (l) h = hrt[s[0]]; \ + if (l > 1) h = hrt[(s[1] + h) & (HTLEN - 1)]; \ + if (l > 2) h = hrt[(s[2] + h) & (HTLEN - 1)]; \ + if (l > 3) h = hrt[(s[3] + h) & (HTLEN - 1)]; \ +} + +#define move_context(c) curcon[3] = curcon[2], curcon[2] = curcon[1], \ + curcon[1] = curcon[0], curcon[0] = c + +static void release_cfblocks(void) +{ + register U16B i, j, d; + + do + { + do if(++nrel == NUMCON) nrel = 0; while(nb[nrel] == NIL); + for(i = 0; i <= usp; ++i) if((cps[i] & 0x7fff) == nrel) break; + } + while(i <= usp); + + for(i = nb[nrel], d = fa[nrel]; i != NIL; i = nb[i]) if(fa[i] < d) d = fa[i]; + ++d; + + if(fa[nrel] < d) + { + for(i = nb[nrel]; fa[i] < d && nb[i] != NIL; i = nb[i]); + fa[nrel] = fa[i]; + fc[nrel] = fc[i]; + j = nb[i]; + nb[i] = fcfbl; + fcfbl = nb[nrel]; + if((nb[nrel] = j) == NIL) + { + cc[nrel] = 0; + fe[nrel] = (ft[nrel] = fa[nrel]) < ESCTH ? 1 : 0; + return; + } + } + + fe[nrel] = (ft[nrel] = fa[nrel] /= d) < ESCTH ? 1 : 0; + cc[nrel] = 0; + + for(j = nrel, i = nb[j]; i != NIL;) + { + if(fa[i] < d) + { + nb[j] = nb[i]; + nb[i] = fcfbl; + fcfbl = i; + i = nb[j]; + } + else + { + ++cc[nrel]; + ft[nrel] += fa[i] /= d; + if(fa[i] < ESCTH) fe[nrel]++; + j = i; + i = nb[i]; + } + } +} + +static U16B make_context(unsigned char conlen, S16B c) +{ + register S16B i; + register U16B nc, fp; + + nc = ell; + ell = elp[nc]; + elp[elf] = nc; + eln[nc] = elf; + elf = nc; + + if(cl[nc] != 0xff) + { + if(cl[nc] == MAXCLEN && --dropcnt == 0) maxclen = MAXCLEN - 1; + HASH(con[nc], cl[nc], i); + if(ht[i] == nc) ht[i] = hp[nc]; + else + { + for(i = ht[i]; hp[i] != nc; i = hp[i]); + hp[i] = hp[nc]; + } + if(nb[nc] != NIL) + { + for(fp = nb[nc]; nb[fp] != NIL; fp = nb[fp]); + nb[fp] = fcfbl; + fcfbl = nb[nc]; + } + } + + nb[nc] = NIL; + fe[nc] = ft[nc] = fa[nc] = 1; + fc[nc] = c; + rfm[nc] = RFMINI; + cc[nc] = 0; + cl[nc] = conlen; + con[nc][0] = curcon[0]; + con[nc][1] = curcon[1]; + con[nc][2] = curcon[2]; + con[nc][3] = curcon[3]; + + HASH(curcon, conlen, i); + hp[nc] = ht[i]; + ht[i] = nc; + return nc; +} + +static void el_movefront(U16B cp) +{ + if(cp == elf) return; + if(cp == ell) ell = elp[cp]; + else + { + elp[eln[cp]] = elp[cp]; + eln[elp[cp]] = eln[cp]; + } + elp[elf] = cp; + eln[cp] = elf; + elf = cp; +} + +static void add_model(S16B c) +{ + register U16B i; + register S16B cp; + + while(usp != 0) + { + i = as[--usp]; + cp = cps[usp]; + if(cp & 0x8000) + { + cp &= 0x7fff; + if(fcfbl == NIL) release_cfblocks(); + nb[i] = fcfbl; + i = nb[i]; + fcfbl = nb[fcfbl]; + nb[i] = NIL; + fa[i] = 1; + fc[i] = c; + ++cc[cp]; + ++fe[cp]; + } + else if(++fa[i] == ESCTH) --fe[cp]; + + if((fa[i] << 1) < ++ft[cp] / (cc[cp] + 1)) --rfm[cp]; + else if(rfm[cp] < RFMINI) ++rfm[cp]; + + if(!rfm[cp] || ft[cp] >= MAXTVAL) + { + ++rfm[cp]; + fe[cp] = ft[cp] = 0; + for(i = cp; i != NIL; i = nb[i]) + { + if(fa[i] > 1) + { + ft[cp] += fa[i] >>= 1; + if(fa[i] < ESCTH) ++fe[cp]; + } + else + { + ++ft[cp]; + ++fe[cp]; + } + } + } + } +} + +static U16B find_next(void) +{ + register S16B i, k; + register U16B cp; + + for(i = cslen - 1; i >= 0; --i) + { + k = hs[i]; + for(cp = ht[k]; cp != NIL; cp = hp[cp]) + { + if(cl[cp] == i) + { + switch(i) + { + case 4: + if(curcon[3] != con[cp][3]) break; + case 3: + if(curcon[2] != con[cp][2]) break; + case 2: + if(curcon[1] != con[cp][1]) break; + case 1: + if(curcon[0] != con[cp][0]) break; + case 0: + cslen = i; + return cp; + } + } + } + } + return NIL; +} + +static U16B find_longest(void) +{ + hs[1] = hrt[curcon[0]]; + hs[2] = hrt[(curcon[1] + hs[1]) & (HTLEN - 1)]; + hs[3] = hrt[(curcon[2] + hs[2]) & (HTLEN - 1)]; + hs[4] = hrt[(curcon[3] + hs[3]) & (HTLEN - 1)]; + usp = 0; + while(cmsp) cmask[cmstack[--cmsp]] = 0; + cslen = MAXCLEN + 1; + return find_next(); +} + +static U16B adj_escape_prob(U16B esc, U16B cp) +{ + if(ft[cp] == 1) return iec[cl[cp]] >= (IECLIM >> 1) ? 2 : 1; + if(cc[cp] == 255) return 1; + if(cc[cp] && ((cc[cp] + 1) << 1) >= ft[cp]) + { + esc = (S16B)((S32B)esc * ((cc[cp] + 1) << 1) / ft[cp]); + if(cc[cp] + 1 == ft[cp]) esc += (cc[cp] + 1) >> 1; + } + return esc ? esc : 1; +} + +static S16B decode_first(U16B cp) +{ + register U16B c; + register U16B tv; + register U16B i; + register S16B sum, tot, esc, cf; + register unsigned char sv; + + esc = adj_escape_prob(fe[cp], cp); + tot = ft[cp]; + if(nec >= NECLIM) + { + if(tot <= NECTLIM && nec == NECMAX) sv = 2; + else sv = 1; + tot <<= sv; + tv = ac_threshold_val(tot + esc) >> sv; + for(c = cp, sum = 0;; c = nb[c]) + { + if(c == NIL) break; + if(sum + fa[c] <= tv) sum += fa[c]; + else + { + cf = fa[c] << sv; + break; + } + } + sum <<= sv; + } + else + { + tv = ac_threshold_val(tot + esc); + for(c = cp, sum = 0;; c = nb[c]) + { + if(c == NIL) break; + if(sum + fa[c] <= tv) sum += fa[c]; + else + { + cf = fa[c]; + break; + } + } + } + + usp = 1; + if(c != NIL) + { + ac_in(sum, sum + cf, tot + esc); + if(ft[cp] == 1 && iec[cl[cp]]) --iec[cl[cp]]; + as[0] = c; + cps[0] = cp; + c = fc[c]; + if(nec < NECMAX) ++nec; + } + else + { + ac_in(tot, tot + esc, tot + esc); + if(ft[cp] == 1 && iec[cl[cp]] < IECLIM) ++iec[cl[cp]]; + for(i = cp; i != NIL; sum = i, i = nb[i]) + { + cmstack[cmsp++] = fc[i]; + cmask[fc[i]] = 1; + } + cps[0] = 0x8000 | cp; + as[0] = sum; + c = ESC; + nec = 0; + } + return c; +} + +static S16B decode_rest(U16B cp) +{ + register U16B c; + register U16B tv; + register U16B i; + register S16B sum, tot, esc, cf; + + esc = tot = 0; + for(i = cp; i != NIL; i = nb[i]) + { + if(!cmask[fc[i]]) + { + tot += fa[i]; + if(fa[i] < ESCTH) ++esc; + } + } + esc = adj_escape_prob(esc, cp); + tv = ac_threshold_val(tot + esc); + + for(c = cp, sum = 0;; c = nb[c]) + { + if(c == NIL) break; + if(!cmask[fc[c]]) + { + if(sum + fa[c] <= tv) sum += fa[c]; + else + { + cf = fa[c]; + break; + } + } + } + + if(c != NIL) + { + ac_in(sum, sum + cf, tot + esc); + if(ft[cp] == 1 && iec[cl[cp]]) --iec[cl[cp]]; + as[usp] = c; + cps[usp++] = cp; + c = fc[c]; + ++nec; + } + else + { + ac_in(tot, tot + esc, tot + esc); + if(ft[cp] == 1 && iec[cl[cp]] < IECLIM) ++iec[cl[cp]]; + for(i = cp; i != NIL; sum = i, i = nb[i]) + { + if(!cmask[fc[i]]) + { + cmstack[cmsp++] = fc[i]; + cmask[fc[i]] = 1; + } + } + cps[usp] = 0x8000 | cp; + as[usp++] = sum; + c = ESC; + } + return c; +} + +static S16B decode_new(void) +{ + register S16B c; + register U16B tv, sum, tot; + + tot = 257 - cmsp; + tv = ac_threshold_val(tot); + for(c = sum = 0; c < 256; ++c) + { + if(cmask[c]) continue; + if(sum + 1 <= tv) ++sum; + else break; + } + ac_in(sum, sum + 1, tot); + return c; +} + +#define decode_byte(cp) (cmsp ? decode_rest(cp) : decode_first(cp)) + +static void flush_output(void) +{ + /* Output flushing is handled by the buffer management */ +} + +int hsc_unpack(decompress_context_t *ctx) +{ + S16B c; + U16B cp; + unsigned char ncmax, ncmin; + + if(init_unpack(ctx) < 0) return -1; + + for(;;) + { + if(ctx->input.error || ctx->output.error) break; + + cp = find_longest(); + ncmin = cp == NIL ? 0 : cl[cp] + 1; + ncmax = maxclen + 1; + + for(;;) + { + if(cp == NIL) + { + c = decode_new(); + break; + } + if((c = decode_byte(cp)) != ESC) + { + el_movefront(cp); + break; + } + cp = find_next(); + } + + if(c == ESC) break; + + add_model(c); + while(ncmax > ncmin) make_context(--ncmax, c); + + if(putbyte_to_buffer(c) < 0) break; + move_context(c); + + if(ctx->input.error || ctx->output.error) break; + } + + flush_output(); + hsc_cleanup(); + return (ctx->input.error || ctx->output.error) ? -1 : 0; +} diff --git a/ha/hsc.h b/ha/hsc.h new file mode 100644 index 0000000..b9cea7e --- /dev/null +++ b/ha/hsc.h @@ -0,0 +1,90 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA HSC method +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#ifndef HSC_H +#define HSC_H + +#include "internal.h" + +int hsc_unpack(decompress_context_t *ctx); + +#define IECLIM 32 +#define NECLIM 5 +#define NECTLIM 4 +#define NECMAX 10 +#define MAXCLEN 4 +#define NUMCON 10000 +#define NUMCFB 32760 +#define ESCTH 3 +#define MAXTVAL 8000 +#define RFMINI 4 +#define HTLEN 16384 +#define NIL 0xffff +#define ESC 256 + +typedef unsigned char Context[4]; + +/* Model data */ +static Context curcon; +static U16B * ht = NULL; +static U16B * hp = NULL; +static Context * con = NULL; +static unsigned char *cl = NULL; +static unsigned char *cc = NULL; +static U16B * ft = NULL; +static unsigned char *fe = NULL; +static U16B * elp = NULL; +static U16B * eln = NULL; +static U16B elf, ell; +static unsigned char *rfm = NULL; +static U16B * fa = NULL; +static unsigned char *fc = NULL; +static U16B * nb = NULL; +static U16B fcfbl; +static U16B nrel; + +/* Frequency mask system */ +static unsigned char cmask[256]; +static unsigned char cmstack[256]; +static S16B cmsp; + +/* Escape probability modifying system variables */ +static unsigned char nec; +static unsigned char iec[MAXCLEN + 1]; + +/* Update stack variables */ +static U16B usp; +static U16B cps[MAXCLEN + 1]; +static U16B as[MAXCLEN + 1]; + +/* Miscellaneous */ +static S16B dropcnt; +static unsigned char maxclen; +static U16B hrt[HTLEN]; +static U16B hs[MAXCLEN + 1]; +static S16B cslen; + +#endif /* HSC_H */ diff --git a/ha/internal.h b/ha/internal.h new file mode 100644 index 0000000..883ad9a --- /dev/null +++ b/ha/internal.h @@ -0,0 +1,45 @@ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#ifndef INTERNAL_H +#define INTERNAL_H + +#include +#include + +/* Type definitions matching original HA code */ +typedef int16_t S16B; +typedef uint16_t U16B; +typedef int32_t S32B; +typedef uint32_t U32B; + +/* Memory buffer management */ +typedef struct +{ + const unsigned char *data; + size_t size; + size_t pos; + int error; +} input_buffer_t; + +typedef struct +{ + unsigned char *data; + size_t size; + size_t pos; + size_t max_size; + int error; +} output_buffer_t; + +/* Decompression context */ +typedef struct +{ + input_buffer_t input; + output_buffer_t output; + int algorithm; +} decompress_context_t; + +#endif /* INTERNAL_H */ diff --git a/ha/swdict.c b/ha/swdict.c new file mode 100644 index 0000000..1ae0174 --- /dev/null +++ b/ha/swdict.c @@ -0,0 +1,99 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA sliding window dictionary +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#include "internal.h" +#include "swdict.h" +#include +#include + +U16B swd_bpos, swd_mlf; +S16B swd_char; + +static decompress_context_t *g_ctx = NULL; +static U16B cblen, bbf; +static unsigned char * b = NULL; + +void swd_cleanup(void) +{ + if(b != NULL) + { + free(b); + b = NULL; + } +} + +void swd_dinit(decompress_context_t *ctx, U16B bufl) +{ + g_ctx = ctx; + cblen = bufl; + + b = malloc(cblen * sizeof(unsigned char)); + if(b == NULL) + { + ctx->output.error = 1; + return; + } + + bbf = 0; +} + +static int putbyte_to_buffer(unsigned char c) +{ + if(!g_ctx || g_ctx->output.pos >= g_ctx->output.max_size) + { + if(g_ctx) g_ctx->output.error = 1; + return -1; + } + + g_ctx->output.data[g_ctx->output.pos++] = c; + if(g_ctx->output.pos > g_ctx->output.size) { g_ctx->output.size = g_ctx->output.pos; } + return 0; +} + +void swd_dpair(U16B l, U16B p) +{ + if(!g_ctx || g_ctx->output.error) return; + + if(bbf > p) p = bbf - 1 - p; + else p = cblen - 1 - p + bbf; + + while(l--) + { + b[bbf] = b[p]; + if(putbyte_to_buffer(b[p]) < 0) return; + if(++bbf == cblen) bbf = 0; + if(++p == cblen) p = 0; + } +} + +void swd_dchar(S16B c) +{ + if(!g_ctx || g_ctx->output.error) return; + + b[bbf] = c; + if(putbyte_to_buffer(c) < 0) return; + if(++bbf == cblen) bbf = 0; +} diff --git a/ha/swdict.h b/ha/swdict.h new file mode 100644 index 0000000..a2aabb4 --- /dev/null +++ b/ha/swdict.h @@ -0,0 +1,45 @@ +/*********************************************************************** + This file is part of HA, a general purpose file archiver. + Copyright (C) 1995 Harri Hirvola + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +************************************************************************ + HA sliding window dictionary +***********************************************************************/ + +/*********************************************************************** + Modified to work with memory buffers instead of files by + Copyright (C) 2005 Natalia Portillo +************************************************************************/ + +#ifndef SWDICT_H +#define SWDICT_H + +#include "internal.h" + +#define MINLEN 3 /* Minimum possible match length */ + +void swd_dinit(decompress_context_t *ctx, U16B bufl); + +void swd_cleanup(void); + +void swd_dpair(U16B l, U16B p); + +void swd_dchar(S16B c); + +extern U16B swd_bpos, swd_mlf; +extern S16B swd_char; + +#endif /* SWDICT_H */ diff --git a/library.h b/library.h index 5b2f375..45bbbf5 100644 --- a/library.h +++ b/library.h @@ -150,4 +150,16 @@ AARU_EXPORT int AARU_CALL pak_decompress_crush(const unsigned char *in_buf, size AARU_EXPORT int AARU_CALL pak_decompress_distill(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len); +/** + * HA Algorithm Types + */ +typedef enum { + HA_ALGORITHM_ASC = 0, /* ASC algorithm */ + HA_ALGORITHM_HSC = 1 /* HSC algorithm */ +} ha_algorithm_t; + +AARU_EXPORT int AARU_CALL ha_asc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len); + +AARU_EXPORT int AARU_CALL ha_hsc_decompress(const unsigned char *in_buf, size_t in_len, unsigned char *out_buf, size_t *out_len); + #endif // AARU_COMPRESSION_NATIVE_LIBRARY_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d6e5d5e..1ed61b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -66,11 +66,15 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pak_crush.bin file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/pak_distill.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ha_asc.bin + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/data/ha_hsc.bin + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/data/) + # 'Google_Tests_run' is the target name # 'test1.cpp tests2.cpp' are source files with tests add_executable(tests_run apple_rle.cpp crc32.c crc32.h adc.cpp bzip2.cpp lzip.cpp lzfse.cpp zstd.cpp lzma.cpp flac.cpp - zoo/lzd.cpp arc/pack.cpp lh5.cpp arc/squeeze.cpp arc/crunch.cpp - arc/squash.cpp - pak/crush.cpp - pak/distill.cpp) + zoo/lzd.cpp arc/pack.cpp lh5.cpp arc/squeeze.cpp arc/crunch.cpp arc/squash.cpp pak/crush.cpp + pak/distill.cpp ha.cpp) target_link_libraries(tests_run gtest gtest_main "Aaru.Compression.Native") diff --git a/tests/ha.cpp b/tests/ha.cpp new file mode 100644 index 0000000..4f28a82 --- /dev/null +++ b/tests/ha.cpp @@ -0,0 +1,133 @@ +/* + * This file is part of the Aaru Data Preservation Suite. + * Copyright (c) 2019-2025 Natalia Portillo. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include +#include + +#include "../library.h" +#include "crc32.h" +#include "gtest/gtest.h" + +#define EXPECTED_CRC32 0x66007dba + +static const uint8_t *buffer; + +class ha_ascFixture : public ::testing::Test +{ +public: + ha_ascFixture() + { + // initialization; + // can also be done in SetUp() + } + +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/ha_asc.bin", path); + + FILE *file = fopen(filename, "rb"); + buffer = (const uint8_t *)malloc(53654); + fread((void *)buffer, 1, 53654, file); + fclose(file); + } + + void TearDown() { free((void *)buffer); } + + ~ha_ascFixture() + { + // resources cleanup, no exceptions allowed + } + + // shared user data +}; + +TEST_F(ha_ascFixture, ha_asc) +{ + size_t destLen = 152089; + size_t srcLen = 53654; + auto * outBuf = (uint8_t *)malloc(53654); + + auto err = ha_asc_decompress(buffer, srcLen, outBuf, &destLen); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, 152089); + + auto crc = crc32_data(outBuf, 152089); + + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} + +class ha_hscFixture : public ::testing::Test +{ +public: + ha_hscFixture() + { + // initialization; + // can also be done in SetUp() + } + +protected: + void SetUp() + { + char path[PATH_MAX]; + char filename[PATH_MAX]; + + getcwd(path, PATH_MAX); + snprintf(filename, PATH_MAX, "%s/data/ha_hsc.bin", path); + + FILE *file = fopen(filename, "rb"); + buffer = (const uint8_t *)malloc(41839); + fread((void *)buffer, 1, 41839, file); + fclose(file); + } + + void TearDown() { free((void *)buffer); } + + ~ha_hscFixture() + { + // resources cleanup, no exceptions allowed + } + + // shared user data +}; + +TEST_F(ha_hscFixture, ha_hsc) +{ + size_t destLen = 152089; + size_t srcLen = 41839; + auto * outBuf = (uint8_t *)malloc(152089); + + auto err = ha_hsc_decompress(buffer, srcLen, outBuf, &destLen); + + EXPECT_EQ(err, 0); + EXPECT_EQ(destLen, 152089); + + auto crc = crc32_data(outBuf, 152089); + + free(outBuf); + + EXPECT_EQ(crc, EXPECTED_CRC32); +} \ No newline at end of file diff --git a/tests/lib b/tests/lib index 16f637f..614f05d 160000 --- a/tests/lib +++ b/tests/lib @@ -1 +1 @@ -Subproject commit 16f637fbf4ffc3f7a01fa4eceb7906634565242f +Subproject commit 614f05d1c018d9cd74279e1576dbb2663a479ab8