Initial revision
This commit is contained in:
parent
218ca90543
commit
2186d5f3f9
572 changed files with 2 additions and 29666 deletions
520
src/win95/huffman.cpp
Normal file
520
src/win95/huffman.cpp
Normal file
|
@ -0,0 +1,520 @@
|
|||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "malloc.h"
|
||||
#include "huffman.hpp"
|
||||
|
||||
/* KJL 17:12:25 17/09/98 - Huffman compression/decompression routines */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int Symbol;
|
||||
unsigned int Count;
|
||||
} HuffItem;
|
||||
|
||||
typedef struct HuffNode // 16-byte node structure
|
||||
{
|
||||
union
|
||||
{ // the FIRST four bytes
|
||||
struct HuffNode *zero; // points to the "zero" branch or...
|
||||
unsigned int value; // holds the value of an end node
|
||||
};
|
||||
|
||||
union
|
||||
{ // the SECOND four bytes
|
||||
struct HuffNode *one; // points to the "one" branch or...
|
||||
struct HuffNode *link; // points to next end node in list
|
||||
};
|
||||
|
||||
struct HuffNode *parent; // the THIRD four bytes, parent node
|
||||
|
||||
union
|
||||
{ // the FOURTH four bytes
|
||||
unsigned int bits; // the bit pattern of this end node
|
||||
struct
|
||||
{
|
||||
unsigned char flag;
|
||||
unsigned char curdepth;
|
||||
unsigned char maxdepth;
|
||||
unsigned char unused;
|
||||
};
|
||||
};
|
||||
|
||||
} HuffNode;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long wid;
|
||||
long bits;
|
||||
|
||||
} HuffEncode;
|
||||
|
||||
static HuffItem SymbolCensus[257];
|
||||
static HuffNode TreeNodes[2*257];
|
||||
static int Depths[MAX_DEPTH+1];
|
||||
static HuffEncode EncodingTable[257];
|
||||
|
||||
#define AllocateMemory malloc
|
||||
|
||||
|
||||
/* KJL 17:16:03 17/09/98 - Compression */
|
||||
static void PerformSymbolCensus(unsigned char *sourcePtr, int length);
|
||||
#ifdef __WATCOMC__
|
||||
static int HuffItemsSortSub(const void *cmp1, const void *cmp2);
|
||||
#else
|
||||
static int __cdecl HuffItemsSortSub(const void *cmp1, const void *cmp2);
|
||||
#endif
|
||||
static void SortCensusData(void);
|
||||
static void BuildHuffmanTree(void);
|
||||
static void MakeHuffTreeFromHuffItems(HuffNode *base, HuffItem *source, int count);
|
||||
static void MakeCodeLengthsFromHuffTree(int *dest, HuffNode *source, int maxdepth);
|
||||
static int HuffDepthsAdjust(int *depth, int maxdepth);
|
||||
static void MakeHuffmanEncodeTable(HuffEncode *encodetable, HuffItem *item, int *depths);
|
||||
static int HuffEncodeBytes(int *dest, unsigned char *source, int count, HuffEncode *table);
|
||||
|
||||
|
||||
extern HuffmanPackage *HuffmanCompression(unsigned char *sourcePtr, int length)
|
||||
{
|
||||
HuffmanPackage *outpackage;
|
||||
|
||||
|
||||
// Step 1: Perform the symbol census
|
||||
PerformSymbolCensus(sourcePtr,length);
|
||||
// Step 2: Sorting the census data
|
||||
SortCensusData();
|
||||
// Step 3: Building the Huffman tree
|
||||
BuildHuffmanTree();
|
||||
// Step 4: Making the code lengths table
|
||||
MakeCodeLengthsFromHuffTree(Depths, TreeNodes, MAX_DEPTH);
|
||||
// Step 5: Limiting code lengths
|
||||
HuffDepthsAdjust(Depths, MAX_DEPTH);
|
||||
// Step 6: Making the encoding table
|
||||
MakeHuffmanEncodeTable(EncodingTable,&SymbolCensus[256],Depths);
|
||||
// Step 7: Encoding data
|
||||
outpackage = (HuffmanPackage*)AllocateMemory(sizeof(HuffmanPackage)+length);
|
||||
strncpy(outpackage->Identifier,COMPRESSED_RIF_IDENTIFIER,8);
|
||||
outpackage->CompressedDataSize = HuffEncodeBytes((int*)(outpackage+1), sourcePtr, length, EncodingTable);
|
||||
outpackage->UncompressedDataSize = length;
|
||||
for (int n = 0; n < MAX_DEPTH; n++)
|
||||
{
|
||||
outpackage->CodelengthCount[n] = Depths[n + 1];
|
||||
}
|
||||
for (n = 0; n < 256; n++)
|
||||
{
|
||||
outpackage->ByteAssignment[n] = SymbolCensus[n + 1].Symbol;
|
||||
}
|
||||
return outpackage;
|
||||
}
|
||||
|
||||
static void PerformSymbolCensus(unsigned char *sourcePtr, int length)
|
||||
{
|
||||
// init array
|
||||
for (int i=0; i<257; i++)
|
||||
{
|
||||
SymbolCensus[i].Symbol = i;
|
||||
SymbolCensus[i].Count = 0;
|
||||
}
|
||||
|
||||
// count 'em
|
||||
do
|
||||
{
|
||||
SymbolCensus[*sourcePtr++].Count++;
|
||||
}
|
||||
while (--length);
|
||||
}
|
||||
|
||||
#ifdef __WATCOMC__
|
||||
static int HuffItemsSortSub(const void *cmp1, const void *cmp2)
|
||||
#else
|
||||
static int __cdecl HuffItemsSortSub(const void *cmp1, const void *cmp2)
|
||||
#endif
|
||||
{
|
||||
if (((HuffItem *)cmp1)->Count > ((HuffItem *)cmp2)->Count)
|
||||
return 1;
|
||||
if (((HuffItem *)cmp1)->Count < ((HuffItem *)cmp2)->Count)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
static void SortCensusData(void)
|
||||
{
|
||||
qsort(SymbolCensus, 257, sizeof(HuffItem), HuffItemsSortSub);
|
||||
}
|
||||
|
||||
static void BuildHuffmanTree(void)
|
||||
{
|
||||
MakeHuffTreeFromHuffItems(TreeNodes,SymbolCensus,257);
|
||||
}
|
||||
|
||||
static void MakeHuffTreeFromHuffItems(HuffNode *base, HuffItem *source, int count)
|
||||
{
|
||||
HuffNode *movdest, *temp;
|
||||
int n, upperlim, lowerlim, index;
|
||||
unsigned int sum;
|
||||
|
||||
if (!count) return;
|
||||
|
||||
movdest = base + 1;
|
||||
temp = base + count;
|
||||
memset(temp, 0, count * sizeof(HuffNode));
|
||||
for (n = 0; n < count; n++)
|
||||
{
|
||||
temp[n].bits = source[n].Count;
|
||||
}
|
||||
while (upperlim = --count)
|
||||
{
|
||||
if (temp[0].zero)
|
||||
temp[0].zero->parent = temp[0].one->parent = movdest;
|
||||
if (temp[1].zero)
|
||||
temp[1].zero->parent = temp[1].one->parent = movdest + 1;
|
||||
movdest[0] = *temp++;
|
||||
movdest[1] = *temp;
|
||||
sum = movdest[0].bits + movdest[1].bits;
|
||||
lowerlim = 1;
|
||||
|
||||
while (lowerlim != upperlim)
|
||||
{
|
||||
index = (lowerlim + upperlim) >> 1;
|
||||
if (sum >= temp[index].bits)
|
||||
{
|
||||
lowerlim = index + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
upperlim = index;
|
||||
}
|
||||
}
|
||||
index = lowerlim - 1;
|
||||
memmove(temp, temp + 1, index * sizeof(HuffNode));
|
||||
temp[index].bits = sum;
|
||||
temp[index].zero = movdest;
|
||||
temp[index].one = movdest + 1;
|
||||
movdest += 2;
|
||||
}
|
||||
base[0] = temp[0];
|
||||
if (base[0].zero)
|
||||
base[0].zero->parent = base[0].one->parent = base;
|
||||
}
|
||||
|
||||
static void MakeCodeLengthsFromHuffTree(int *dest, HuffNode *source, int maxdepth)
|
||||
{
|
||||
int n, depth;
|
||||
HuffNode *back;
|
||||
|
||||
for (n = 0; n < maxdepth + 1; n++)
|
||||
dest[n] = 0;
|
||||
depth = 0;
|
||||
while (1)
|
||||
{
|
||||
while (source->one)
|
||||
{
|
||||
source = source->one;
|
||||
depth++;
|
||||
}
|
||||
|
||||
if (depth > maxdepth) dest[maxdepth]++;
|
||||
else dest[depth]++;
|
||||
|
||||
do
|
||||
{
|
||||
back = source;
|
||||
source = source->parent;
|
||||
if (!depth--)
|
||||
return;
|
||||
}
|
||||
while (back == source->zero);
|
||||
|
||||
source = source->zero;
|
||||
depth++;
|
||||
}
|
||||
}
|
||||
|
||||
static int HuffDepthsAdjust(int *depth, int maxdepth)
|
||||
{
|
||||
unsigned int n, m, items, sum, goal, gain, busts;
|
||||
unsigned int promotions, excess, hi;
|
||||
|
||||
goal = 1 << maxdepth;
|
||||
for (n = 0, sum = 0, items = 0; n <= maxdepth; n++)
|
||||
{
|
||||
items += depth[n];
|
||||
sum += (goal >> n) * depth[n];
|
||||
}
|
||||
if (items > goal)
|
||||
return -1; // failure
|
||||
for (n = maxdepth - 1; sum > goal; n--)
|
||||
{
|
||||
if (depth[n])
|
||||
{
|
||||
gain = (1 << (maxdepth - n)) - 1;
|
||||
busts = (sum - goal + gain - 1) / gain;
|
||||
busts = depth[n] < busts ? depth[n] : busts;
|
||||
depth[n] -= busts;
|
||||
depth[maxdepth] += busts;
|
||||
sum -= busts * gain;
|
||||
}
|
||||
}
|
||||
excess = goal - sum;
|
||||
for (n = 0; excess; n++)
|
||||
{
|
||||
hi = 1 << (maxdepth - n);
|
||||
for (m = n + 1; m <= maxdepth; m++)
|
||||
{
|
||||
gain = hi - (1 << (maxdepth - m));
|
||||
if (excess < gain)
|
||||
break;
|
||||
if (depth[m])
|
||||
{
|
||||
promotions = excess / gain;
|
||||
promotions = depth[m] > promotions ? promotions : depth[m];
|
||||
depth[n] += promotions;
|
||||
depth[m] -= promotions;
|
||||
excess -= promotions * gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
static void MakeHuffmanEncodeTable(HuffEncode *encodetable, HuffItem *item, int *depths)
|
||||
{
|
||||
unsigned int d, bitwidth, depthbit, bt, cur;
|
||||
int *dep;
|
||||
|
||||
dep = depths + 1; // skip depth zero
|
||||
bitwidth = 0; // start from small bitwidths
|
||||
cur = 0; // current bit pattern
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
bitwidth++; // go deeper
|
||||
depthbit = 1 << (bitwidth - 1); // keep depth marker
|
||||
d = *dep++; // get count here
|
||||
}
|
||||
while (!d); // until count non-zero
|
||||
while (d--)
|
||||
{ // for all on this level
|
||||
encodetable[item->Symbol].wid = bitwidth; // record width
|
||||
encodetable[item->Symbol].bits = cur; // record bits
|
||||
item--; // count backwards an item
|
||||
bt = depthbit; // bt is a temp value
|
||||
while (1)
|
||||
{
|
||||
cur ^= bt; // do an add modulo 1
|
||||
if ((cur & bt) || !bt) // break if now a 1
|
||||
break; // or out of bits
|
||||
bt >>= 1; // do next bit position
|
||||
}
|
||||
}
|
||||
}
|
||||
while (cur); // until cur exhausted
|
||||
}
|
||||
|
||||
static int HuffEncodeBytes(int *dest, unsigned char *source, int count, HuffEncode *table)
|
||||
{
|
||||
int *start;
|
||||
int wid, val, available;
|
||||
unsigned int accum, bits;
|
||||
unsigned char *sourcelim, *sourceend;
|
||||
|
||||
if (!count) return 0;
|
||||
|
||||
start = dest;
|
||||
sourcelim = sourceend = source + count;
|
||||
available = 32;
|
||||
if (sourcelim - 32 < sourcelim)
|
||||
{
|
||||
sourcelim -= 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
sourcelim = source;
|
||||
}
|
||||
if (source < sourcelim)
|
||||
{
|
||||
do
|
||||
{
|
||||
goto lpstart;
|
||||
do
|
||||
{
|
||||
accum = (accum >> wid) | (bits << (32 - wid));
|
||||
lpstart: val = *source++;
|
||||
wid = table[val].wid;
|
||||
bits = table[val].bits;
|
||||
}
|
||||
while ((available -= wid) >= 0);
|
||||
|
||||
wid += available;
|
||||
if (wid) accum = (accum >> wid) | (bits << (32 - wid));
|
||||
*dest++ = accum;
|
||||
wid -= available;
|
||||
accum = bits << (32 - wid);
|
||||
available += 32;
|
||||
}
|
||||
while (source < sourcelim);
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
if (source < sourceend)
|
||||
{
|
||||
val = *source++;
|
||||
}
|
||||
else if (source == sourceend)
|
||||
{
|
||||
val = 0x100; // terminator
|
||||
source++;
|
||||
}
|
||||
else break; // done
|
||||
|
||||
wid = table[val].wid;
|
||||
bits = table[val].bits;
|
||||
|
||||
if ((available -= wid) < 0)
|
||||
{
|
||||
wid += available;
|
||||
if (wid)
|
||||
accum = (accum >> wid) | (bits << (32 - wid));
|
||||
*dest++ = accum;
|
||||
wid -= available;
|
||||
accum = bits << (32 - wid);
|
||||
available += 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
accum = (accum >> wid) | (bits << (32 - wid));
|
||||
}
|
||||
}
|
||||
*dest++ = accum >> available;
|
||||
return (dest - start) * 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* KJL 17:16:24 17/09/98 - Decompression */
|
||||
static int DecodeTable[1<<MAX_DEPTH];
|
||||
|
||||
static void MakeHuffmanDecodeTable(int *depth, int depthmax, unsigned char *list);
|
||||
static int HuffmanDecode(unsigned char *dest, int *source, int *table, int length);
|
||||
|
||||
|
||||
extern char *HuffmanDecompress(HuffmanPackage *inpackage)
|
||||
{
|
||||
unsigned char *uncompressedData = NULL;
|
||||
// Step 1: Make the decoding table
|
||||
MakeHuffmanDecodeTable(inpackage->CodelengthCount, MAX_DEPTH, inpackage->ByteAssignment);
|
||||
|
||||
// Step 2: Decode data
|
||||
uncompressedData = (unsigned char*)AllocateMemory(inpackage->UncompressedDataSize+16);
|
||||
if (uncompressedData)
|
||||
{
|
||||
HuffmanDecode(uncompressedData,(int*)(inpackage+1),DecodeTable,inpackage->UncompressedDataSize);
|
||||
}
|
||||
|
||||
return (char*)uncompressedData;
|
||||
}
|
||||
|
||||
static void MakeHuffmanDecodeTable(int *depth, int depthmax, unsigned char *list)
|
||||
{
|
||||
int thisdepth, depthbit, repcount, repspace, lenbits, temp, count;
|
||||
int *outp;
|
||||
int o = 0;
|
||||
unsigned char *p;
|
||||
int *outtbl = DecodeTable;
|
||||
|
||||
lenbits = 0;
|
||||
repcount = 1 << depthmax;
|
||||
repspace = 1;
|
||||
thisdepth = 0;
|
||||
depthbit = 4;
|
||||
p = (unsigned char *)list + 255;
|
||||
while (1)
|
||||
{
|
||||
do
|
||||
{
|
||||
lenbits++;
|
||||
depthbit <<= 1;
|
||||
repspace <<= 1;
|
||||
repcount >>= 1;
|
||||
}
|
||||
while (!(thisdepth = *depth++));
|
||||
do
|
||||
{
|
||||
if (p < list)
|
||||
{
|
||||
temp = 0xff;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = lenbits | (*p-- << 8);
|
||||
}
|
||||
outp = outtbl + (o >> 2);
|
||||
count = repcount;
|
||||
do
|
||||
{
|
||||
*outp = temp;
|
||||
outp += repspace;
|
||||
}
|
||||
while (--count);
|
||||
temp = depthbit;
|
||||
do
|
||||
{
|
||||
temp >>= 1;
|
||||
if (temp & 3) return;
|
||||
o ^= temp;
|
||||
}
|
||||
while (!(o & temp));
|
||||
}
|
||||
while (--thisdepth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define EDXMASK ((((1 << (MAX_DEPTH + 1)) - 1) ^ 1) ^ -1)
|
||||
|
||||
static int HuffmanDecode(unsigned char *dest, int *source, int *table, int length)
|
||||
{
|
||||
unsigned char *start;
|
||||
int available, reserve, fill, wid;
|
||||
unsigned int bits=0, resbits;
|
||||
unsigned char *p;
|
||||
|
||||
start = dest;
|
||||
available = 0;
|
||||
reserve = 0;
|
||||
wid = 0;
|
||||
do
|
||||
{
|
||||
available += wid;
|
||||
fill = 31 - available;
|
||||
bits <<= fill;
|
||||
if (fill > reserve)
|
||||
{
|
||||
fill -= reserve;
|
||||
available += reserve;
|
||||
if (reserve)
|
||||
{
|
||||
bits = (bits >> reserve) | (resbits << (32 - reserve));
|
||||
}
|
||||
resbits = *source++;
|
||||
reserve = 32;
|
||||
}
|
||||
bits = (bits >> fill) | (resbits << (32 - fill));
|
||||
resbits >>= fill;
|
||||
reserve -= fill;
|
||||
available = 31;
|
||||
goto lpent;
|
||||
do
|
||||
{
|
||||
bits >>= wid;
|
||||
*dest++ = p[1];
|
||||
lpent: p = (unsigned char *)(((short *)table)+(bits & ~EDXMASK));
|
||||
}
|
||||
while ((available -= (wid = *p)) >= 0 && (dest-start)!=length);
|
||||
|
||||
}
|
||||
while (available > -32 && (dest-start)!=length);
|
||||
return dest - start;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue