Files
Aaru.Compression.Native/ha/hsc.c

569 lines
14 KiB
C

/***********************************************************************
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 <stdlib.h>
#include <string.h>
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;
}