#include "vchhdr.h"
typedef struct _vchuff_s
{ ssize_t freq[VCH_SIZE];
ssize_t size[VCH_SIZE];
Vcbit_t bits[VCH_SIZE];
ssize_t maxs;
Vchtrie_t* trie;
int type;
} Vchuff_t;
#define VCH_HASFREQ (1<<15)
#define VCH_HASSIZE (1<<14)
#if __STD_C
int vchcopy(Vcodex_t* vc, ssize_t* freq, ssize_t* size , ssize_t maxs)
#else
int vchcopy(vc, freq, size, maxs )
Vcodex_t* vc;
ssize_t* freq;
ssize_t* size;
ssize_t maxs;
#endif
{
Vchuff_t *huff = vcgetmtdata(vc, Vchuff_t*);
huff->type = 0;
huff->maxs = 0;
if(freq)
{ memcpy(huff->freq, freq, VCH_SIZE*sizeof(freq[0]));
huff->type |= VCH_HASFREQ;
}
if(size && maxs >= 0 && maxs < 32)
{ memcpy(huff->size, size, VCH_SIZE*sizeof(size[0]));
huff->maxs = maxs;
huff->type |= VCH_HASSIZE;
}
return 0;
}
#if __STD_C
static ssize_t vchuff(Vcodex_t* vc, const Void_t* data, size_t dtsz, Void_t** out)
#else
static ssize_t vchuff(vc, data, dtsz, out)
Vcodex_t* vc;
Void_t* data;
size_t dtsz;
Void_t** out;
#endif
{
reg Vcbit_t b;
reg ssize_t n;
ssize_t s;
Vcchar_t *output, *dt, *enddt;
Vcio_t io;
Vchuff_t *huff = vcgetmtdata(vc, Vchuff_t*);
ssize_t *freq = huff->freq;
ssize_t *size = huff->size;
Vcbit_t *bits = huff->bits;
if(dtsz == 0)
return 0;
if(!(huff->type&VCH_HASFREQ) )
{ CLRTABLE(freq, VCH_SIZE);
ADDFREQ(freq, Vcchar_t*, data, dtsz);
huff->type = 0;
}
if(!(huff->type&VCH_HASSIZE) &&
(huff->maxs = vchsize(VCH_SIZE,freq,size,NIL(int*))) < 0)
return -1;
huff->type = 0;
if(huff->maxs > 0)
{ DOTPRODUCT(s, freq, size, VCH_SIZE);
s = (s+7)/8;
}
else s = 1;
s += vcsizeu(dtsz) + 1;
s += VCH_SIZE;
if(!(output = vcbuffer(vc, NIL(Vcchar_t*), s, 0)) )
return -1;
vcioinit(&io, output, s);
huff->maxs = vchbits(VCH_SIZE, size, bits);
vcioputu(&io, dtsz);
vcioputc(&io, huff->maxs);
DEBUG_PRINT(2,"Vchuff: inputsz=%d ",dtsz);
enddt = (dt = (Vcchar_t*)data) + dtsz;
if(huff->maxs == 0)
vcioputc(&io, *dt);
else
{
if((s = vchputcode(VCH_SIZE, size, huff->maxs, vcionext(&io), vciomore(&io))) <= 0)
return -1;
else vcioskip(&io,s);
DEBUG_PRINT(2,"headsz=%d ",vciosize(&io));
vciosetb(io, b, n, VC_ENCODE);
for(; dt < enddt; ++dt)
vcioaddb(&io, b, n, bits[*dt], size[*dt]);
vcioendb(&io, b, n, VC_ENCODE);
}
dt = output;
s = vciosize(&io); DEBUG_PRINT(2,"cmpsz=%d\n",s);
if(vcrecode(vc, &output, &s, 0, 0) < 0 )
return -1;
if(dt != output)
vcbuffer(vc, dt, -1, -1);
if(out)
*out = output;
return s;
}
#if __STD_C
static ssize_t vcunhuff(Vcodex_t* vc, const Void_t* orig, size_t dtsz, Void_t** out)
#else
static ssize_t vcunhuff(vc, orig, dtsz, out)
Vcodex_t* vc;
Void_t* orig;
size_t dtsz;
Void_t** out;
#endif
{
reg Vcbit_t b;
ssize_t n, p, sz, ntop;
short *node, *size;
Vcchar_t *o, *endo, *output, *data;
Vcio_t io;
Vchuff_t *huff = vcgetmtdata(vc, Vchuff_t*);
if(dtsz == 0)
return 0;
data = (Vcchar_t*)orig; sz = (ssize_t)dtsz;
if(vcrecode(vc, &data, &sz, 0, 0) < 0 )
return -1;
dtsz = sz;
vcioinit(&io, data, dtsz);
sz = (ssize_t)vciogetu(&io);
if(!(output = vcbuffer(vc, NIL(Vcchar_t*), sz, 0)) )
return -1;
endo = (o = output)+sz;
if((sz = vciogetc(&io)) == 0 )
{ n = vciogetc(&io);
while(o < endo)
*o++ = n;
}
else
{ if((n = vchgetcode(VCH_SIZE, huff->size, sz, vcionext(&io), vciomore(&io))) < 0)
return -1;
else vcioskip(&io,n);
if(vchbits(VCH_SIZE, huff->size, huff->bits) < 0)
return -1;
if(huff->trie)
vchdeltrie(huff->trie);
if(!(huff->trie = vchbldtrie(VCH_SIZE, huff->size, huff->bits)) )
return -1;
node = huff->trie->node;
size = huff->trie->size;
ntop = huff->trie->ntop;
vciosetb(&io, b, n, VC_DECODE);
for(sz = ntop, p = 0;; )
{ vciofilb(&io, b, n, sz);
p += (b >> (VC_BITSIZE-sz));
if(size[p] > 0)
{ vciodelb(&io, b, n, size[p]);
*o = (Vcchar_t)node[p];
if((o += 1) >= endo)
break;
sz = ntop; p = 0;
}
else if(size[p] == 0)
return -1;
else
{ vciodelb(&io, b, n, sz);
sz = -size[p]; p = node[p];
}
}
vcioendb(&io, b, n, VC_DECODE);
}
if(data != orig)
vcbuffer(vc, data, -1, -1);
if(out)
*out = output;
return o-output;
}
#if __STD_C
static int huffevent(Vcodex_t* vc, int type, Void_t* params)
#else
static int huffevent(vc, type, params)
Vcodex_t* vc;
int type;
Void_t* params;
#endif
{
Vchuff_t *huff;
if(type == VC_OPENING)
{ if(!(huff = (Vchuff_t*)malloc(sizeof(Vchuff_t))) )
return -1;
huff->trie = NIL(Vchtrie_t*);
huff->maxs = 0;
huff->type = 0;
vcsetmtdata(vc, huff);
return 0;
}
else if(type == VC_CLOSING)
{ if((huff = vcgetmtdata(vc, Vchuff_t*)) )
{ if(huff->trie)
vchdeltrie(huff->trie);
free(huff);
}
vcsetmtdata(vc, NIL(Vchuff_t*));
return 0;
}
else return 0;
}
Vcmethod_t _Vchuffman =
{ vchuff,
vcunhuff,
huffevent,
"huffman", "Huffman encoding.",
"[-version?huffman (AT&T Research) 2003-01-01]" USAGE_LICENSE,
NIL(Vcmtarg_t*),
1024*1024,
0
};
VCLIB(Vchuffman)