1091 lines
33 KiB
C
1091 lines
33 KiB
C
/*
|
|
* Microsoft Confidential
|
|
* Copyright (c) 1994 Microsoft Corporation
|
|
* All Rights Reserved.
|
|
*
|
|
* MRCICODE.C
|
|
*
|
|
* MRCI 1 & MRCI 2 maxcompress and decompress functions
|
|
*/
|
|
#include "pch.h"
|
|
#include "mrcicode.h" /* prototype verification */
|
|
|
|
#include <assert.h> /* use NDEBUG to inhibit */
|
|
#include <setjmp.h> /* fast overflow recovery */
|
|
|
|
#define LOGCHASH (13) /* Log of max. no. of hash buckets */
|
|
#define CHASH (1U << LOGCHASH) /* Reasonably large table */
|
|
|
|
#define hash(w) ((w) & (CHASH - 1))
|
|
/* Simply toss the high-order bits */
|
|
#define word(p) ((p)[0] + (((p)[1]) << 8))
|
|
/* Return word at location */
|
|
|
|
#define BITMASK(x) ((1 << x) - 1) /* returns lower 'x' bits set */
|
|
|
|
#define LOGDISPSMALL (6) /* Number of bits in small disp */
|
|
#define LOGDISPMED (8) /* Number of bits in medium disp */
|
|
#define LOGDISPBIG (12) /* Number of bits in big displacement */
|
|
|
|
#define MAXDISPSMALL ((1 << LOGDISPSMALL) - 1)
|
|
/* Maximum small displacement */
|
|
#define MAXDISPMED ((1 << LOGDISPMED) + MAXDISPSMALL)
|
|
/* Maximum medium displacement */
|
|
#define MAXDISPBIG ((1 << LOGDISPBIG) + MAXDISPMED)
|
|
/* Maximum big displacement */
|
|
|
|
#define MINDISPSMALL (0) /* Minimum small displacement */
|
|
#define MINDISPMED (MAXDISPSMALL + 1)
|
|
/* Minimum medium displacement */
|
|
#define MINDISPBIG (MAXDISPMED + 1)/* Minimum big displacement */
|
|
|
|
#define DISPMAX (MAXDISPBIG - 1)/* MAXDISPBIG is our end marker */
|
|
|
|
#define MINMATCH1 (2) /* Minimum match length for MRCI1 */
|
|
#define MINMATCH2 (3) /* Minimum match length for MRCI2 */
|
|
#define MAXMATCH (512) /* Maximum match length */
|
|
|
|
#define EOB (0) /* length used to mean end of block */
|
|
|
|
#define SECTOR (512) /* blocking factor */
|
|
|
|
#define SIG_SIZE (4) /* # of block type prefix bytes */
|
|
|
|
|
|
/* local variables */
|
|
|
|
static unsigned abits; /* Array of bits */
|
|
static unsigned cbitsleft; /* Number of bits in abits */
|
|
static unsigned char FAR *pCompressed; /* pointer into compressed data */
|
|
static unsigned cCompressed; /* # bytes remaining @ pCompressed */
|
|
static jmp_buf bailout; /* longjmp if cCompressed exceeded */
|
|
|
|
static unsigned ahash[CHASH]; /* Hash table */
|
|
static unsigned alink[MAXDISPBIG]; /* Links */
|
|
|
|
|
|
/* compression internal functions */
|
|
|
|
static void inithash(void);
|
|
static void charbuf(unsigned c);
|
|
static void putbits(unsigned bits,unsigned cbits);
|
|
static void outlength(unsigned cb);
|
|
|
|
static void mrci1outsingle(unsigned ch);
|
|
static void mrci1outstring(unsigned disp,unsigned cb);
|
|
|
|
static void mrci2outsingle(unsigned ch);
|
|
static void mrci2outstring(unsigned disp,unsigned cb);
|
|
|
|
|
|
/* decompression internal functions */
|
|
|
|
static unsigned getbit(void);
|
|
static unsigned getbits(unsigned cbits);
|
|
static void expandstring(unsigned char FAR **ppchout,unsigned disp,
|
|
unsigned cb);
|
|
|
|
|
|
/*
|
|
* (compress) Reset the hash tables between blocks.
|
|
*/
|
|
|
|
static void inithash(void)
|
|
{
|
|
unsigned FAR *entry;
|
|
int i;
|
|
|
|
entry = ahash;
|
|
i = CHASH;
|
|
|
|
do
|
|
{
|
|
*entry++ = (unsigned) -1; /* Mark all entries as empty */
|
|
} while (--i);
|
|
}
|
|
|
|
|
|
/*
|
|
* (compress) Add a character to compressed output buffer.
|
|
*/
|
|
|
|
static void charbuf(unsigned c)
|
|
{
|
|
if (cCompressed-- == 0) /* make sure there's room */
|
|
{
|
|
longjmp(bailout,1); /* Data expanding! */
|
|
}
|
|
|
|
*pCompressed++ = (unsigned char) c; /* Put character into buffer */
|
|
}
|
|
|
|
|
|
/*
|
|
* (compress) Write n bits to the compressed bitstream.
|
|
*/
|
|
|
|
static void putbits(unsigned ab,unsigned cbits)
|
|
{
|
|
do /* Loop to emit bits */
|
|
{
|
|
if (cbits > cbitsleft) /* if not enough space */
|
|
{
|
|
cbits -= cbitsleft; /* doing partial */
|
|
|
|
abits |= (ab << (8 - cbitsleft));
|
|
/* Put bits in output buffer */
|
|
|
|
ab >>= cbitsleft; /* clip sent bits */
|
|
|
|
charbuf(abits); /* Emit the buffer */
|
|
cbitsleft = 8; /* Reset buffer count */
|
|
abits = 0; /* Reset buffer */
|
|
}
|
|
else /* can do all in one pass */
|
|
{
|
|
abits |= ((ab & BITMASK(cbits)) << (8 - cbitsleft));
|
|
/* Put bits in output buffer */
|
|
|
|
cbitsleft -= cbits; /* used up some buffer */
|
|
|
|
if (cbitsleft == 0) /* If buffer full */
|
|
{
|
|
charbuf(abits); /* Emit the buffer */
|
|
cbitsleft = 8; /* Reset buffer count */
|
|
abits = 0; /* Reset buffer */
|
|
}
|
|
|
|
break; /* we've done all cbits */
|
|
}
|
|
} while (cbits); /* repeat until done */
|
|
}
|
|
|
|
|
|
/*
|
|
* (compress) Encode a length into the compressed stream.
|
|
*/
|
|
|
|
static void outlength(unsigned cb)
|
|
{
|
|
unsigned alogbits, clogbits;
|
|
unsigned avaluebits, cvaluebits;
|
|
|
|
assert(cb >= 2); /* Length must be at least two */
|
|
assert(cb <= MAXMATCH);
|
|
|
|
if (cb <= 2)
|
|
{
|
|
alogbits = 1;
|
|
clogbits = 1;
|
|
cvaluebits = 0;
|
|
}
|
|
else if (cb <= 4)
|
|
{
|
|
alogbits = 1 << 1;
|
|
clogbits = 2;
|
|
avaluebits = cb - 3;
|
|
cvaluebits = 1;
|
|
}
|
|
else if (cb <= 8)
|
|
{
|
|
alogbits = 1 << 2;
|
|
clogbits = 3;
|
|
avaluebits = cb - 5;
|
|
cvaluebits = 2;
|
|
}
|
|
else if (cb <= 16)
|
|
{
|
|
alogbits = 1 << 3;
|
|
clogbits = 4;
|
|
avaluebits = cb - 9;
|
|
cvaluebits = 3;
|
|
}
|
|
else if (cb <= 32)
|
|
{
|
|
alogbits = 1 << 4;
|
|
clogbits = 5;
|
|
avaluebits = cb - 17;
|
|
cvaluebits = 4;
|
|
}
|
|
else if (cb <= 64)
|
|
{
|
|
alogbits = 1 << 5;
|
|
clogbits = 6;
|
|
avaluebits = cb - 33;
|
|
cvaluebits = 5;
|
|
}
|
|
else if (cb <= 128)
|
|
{
|
|
alogbits = 1 << 6;
|
|
clogbits = 7;
|
|
avaluebits = cb - 65;
|
|
cvaluebits = 6;
|
|
}
|
|
else if (cb <= 256)
|
|
{
|
|
alogbits = 1 << 7;
|
|
clogbits = 8;
|
|
avaluebits = cb - 129;
|
|
cvaluebits = 7;
|
|
}
|
|
else /* (cb <= 512) */
|
|
{
|
|
alogbits = 1 << 8;
|
|
clogbits = 9;
|
|
avaluebits = cb - 257;
|
|
cvaluebits = 8;
|
|
}
|
|
|
|
putbits(alogbits,clogbits);
|
|
|
|
if (cvaluebits)
|
|
{
|
|
putbits(avaluebits,cvaluebits);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI1 compress) Encode a literal into the compressed stream.
|
|
*/
|
|
|
|
static void mrci1outsingle(unsigned ch)
|
|
{
|
|
ch = (ch << 2) | ((ch & 0x80) ? 1 : 2);
|
|
|
|
putbits(ch,9);
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI2 compress) Encode a literal into the compressed stream.
|
|
*/
|
|
|
|
static void mrci2outsingle(unsigned ch)
|
|
{
|
|
if (ch & 0x80)
|
|
{
|
|
putbits((ch << 2) | 3,9);
|
|
}
|
|
else
|
|
{
|
|
putbits(ch << 1,8);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI1 compress) Encode a match into the compressed stream.
|
|
*/
|
|
|
|
static void mrci1outstring(unsigned disp,unsigned cb)
|
|
{
|
|
assert(((cb >= MINMATCH1) && (disp != 0) && (disp < MAXDISPBIG)) ||
|
|
((cb == EOB) && (disp == MAXDISPBIG)));
|
|
|
|
if (disp <= MAXDISPSMALL)
|
|
{
|
|
putbits(((disp - MINDISPSMALL) << 2),LOGDISPSMALL + 2);
|
|
/* Put small displacement */
|
|
}
|
|
else if (disp <= MAXDISPMED)
|
|
{
|
|
putbits(((disp - MINDISPMED) << 3) | 3,LOGDISPMED + 3);
|
|
/* Put medium displacement */
|
|
}
|
|
else
|
|
{
|
|
putbits(((disp - MINDISPBIG) << 3) | 7,LOGDISPBIG + 3);
|
|
/* Put big displacement */
|
|
}
|
|
|
|
if (cb != EOB) /* If not an end marker */
|
|
{
|
|
outlength(cb); /* Emit the match length */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI2 compress) Encode a match into the compressed stream.
|
|
*/
|
|
|
|
static void mrci2outstring(unsigned disp,unsigned cb)
|
|
{
|
|
assert(((cb >= MINMATCH2) && (disp != 0) && (disp < MAXDISPBIG)) ||
|
|
((cb == EOB) && (disp == MAXDISPBIG)));
|
|
|
|
if (disp <= MAXDISPSMALL)
|
|
{
|
|
putbits(((disp - MINDISPSMALL) << 3) | 1,LOGDISPSMALL + 3);
|
|
/* Put small displacement */
|
|
}
|
|
else if (disp <= MAXDISPMED)
|
|
{
|
|
putbits(((disp - MINDISPMED) << 4) | 5,LOGDISPMED + 4);
|
|
/* Put medium displacement */
|
|
}
|
|
else
|
|
{
|
|
putbits(((disp - MINDISPBIG) << 4) | 13,LOGDISPBIG + 4);
|
|
/* Put big displacement */
|
|
}
|
|
|
|
if (cb != EOB) /* If not an end marker */
|
|
{
|
|
outlength(cb - 1); /* Emit the match length */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI1) MaxCompress
|
|
*/
|
|
|
|
unsigned Mrci1MaxCompress(unsigned char FAR *pchbase,unsigned cchunc,
|
|
unsigned char FAR *pchcmpBase,unsigned cchcmpMax)
|
|
{
|
|
unsigned cchbest; /* Length of best match */
|
|
unsigned cchmatch; /* Length of this match */
|
|
unsigned ibest; /* Position of best match */
|
|
unsigned icur; /* Current position */
|
|
unsigned ihash; /* Hash table index */
|
|
unsigned ilink; /* Link index */
|
|
unsigned char FAR *pch; /* Char pointer */
|
|
unsigned char FAR *pch2; /* Char pointer */
|
|
unsigned char FAR *pchend; /* End of input (-> last valid) */
|
|
unsigned cch; /* per-pass limit */
|
|
|
|
cbitsleft = 8; /* Buffer is empty */
|
|
abits = 0;
|
|
pCompressed = pchcmpBase; /* Initialize pointer */
|
|
|
|
if (cchunc < cchcmpMax)
|
|
{
|
|
cCompressed = cchunc; /* limit to source size */
|
|
}
|
|
else
|
|
{
|
|
cCompressed = cchcmpMax; /* limit to max size offered */
|
|
}
|
|
|
|
if (cCompressed < SIG_SIZE)
|
|
{
|
|
return((unsigned) -1);
|
|
}
|
|
|
|
*pCompressed++ = 'D';
|
|
*pCompressed++ = 'S';
|
|
*pCompressed++ = '\x00';
|
|
*pCompressed++ = '\x01';
|
|
|
|
cCompressed -= SIG_SIZE;
|
|
|
|
pch = pchbase; /* Initialize */
|
|
|
|
if (cchunc-- == 0)
|
|
{
|
|
return(0); /* Do nothing to empty buffer */
|
|
}
|
|
|
|
inithash(); /* Initialize tables */
|
|
|
|
if (setjmp(bailout) != 0) /* If failure */
|
|
{
|
|
return((unsigned) -1); /* Data expanded */
|
|
}
|
|
|
|
cchbest = 0; /* no match yet */
|
|
icur = 0; /* Initialize */
|
|
|
|
for (cch = SECTOR - 1; cch <= (cchunc + SECTOR - 1); cch += SECTOR)
|
|
{
|
|
assert(cchbest == 0); /* must always start with no match */
|
|
|
|
if (cch > cchunc)
|
|
{
|
|
cch = cchunc; /* limit to exact req count */
|
|
}
|
|
|
|
pchend = &pchbase[cch]; /* Remember end of buffer */
|
|
|
|
while (icur < cch) /* While at least two chars left */
|
|
{
|
|
/* update hash tables for this character */
|
|
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur; /* Remember position */
|
|
alink[icur % MAXDISPBIG] = ilink;
|
|
/* Chain on rest of list */
|
|
|
|
/* walk hash chain looking for matches */
|
|
|
|
while (ilink < icur && icur - ilink <= DISPMAX)
|
|
{ /* While link is valid and in range */
|
|
pch = &pchbase[icur]; /* Point at first byte */
|
|
pch2 = &pchbase[ilink]; /* Point at first byte */
|
|
|
|
if (pch[cchbest] == pch2[cchbest] && word(pch) == word(pch2))
|
|
{ /* If we have a possible best match */
|
|
pch += 2; /* Skip first pair */
|
|
pch2 += 2; /* Skip first pair */
|
|
|
|
while (pch <= pchend) /* Loop to find end of match */
|
|
{
|
|
if (*pch != *pch2++)
|
|
{
|
|
break; /* Break if mismatch */
|
|
}
|
|
pch++; /* Skip matching character */
|
|
}
|
|
|
|
if ((cchmatch = (unsigned)(pch - pchbase) - icur) > cchbest)
|
|
{ /* If new best match */
|
|
cchbest = cchmatch; /* Remember length */
|
|
ibest = ilink; /* Remember position */
|
|
|
|
assert((pch-1) <= pchend);
|
|
|
|
if (pch > pchend)
|
|
{
|
|
break; /* Break if we can't do any better */
|
|
}
|
|
}
|
|
}
|
|
|
|
assert((alink[ilink % MAXDISPBIG] == (unsigned) -1) ||
|
|
(alink[ilink % MAXDISPBIG] < ilink));
|
|
|
|
ilink = alink[ilink % MAXDISPBIG];
|
|
/* Get next link */
|
|
} /* until end of hash chain reached */
|
|
|
|
if (cchbest >= MINMATCH1) /* If we have a string match */
|
|
{
|
|
mrci1outstring(icur - ibest,cchbest);
|
|
/* Describe matching string */
|
|
#ifdef VXD
|
|
if (icur + cchbest >= cch ) /* If end of sector reached */
|
|
#else
|
|
if (icur + cchbest >= cchunc) /* If end of buffer reached */
|
|
#endif
|
|
{
|
|
icur += cchbest; /* Advance the index */
|
|
cchbest = 0; /* reset for next match */
|
|
break; /* Done if buffer exhausted */
|
|
}
|
|
|
|
icur++; /* Skip to first unhashed pair */
|
|
#ifdef VXD
|
|
/* avoid re-seeding all of a big match */
|
|
|
|
if (cchbest > MAXDISPSMALL)
|
|
{ /* If big match */
|
|
icur += cchbest - MAXDISPSMALL - 1;
|
|
/* Skip ahead */
|
|
cchbest = MAXDISPSMALL + 1;
|
|
/* Use shorter length */
|
|
}
|
|
#endif
|
|
/* update hash tables for each add't char in string */
|
|
|
|
ibest = icur % MAXDISPBIG; /* Get current link table index */
|
|
|
|
while (--cchbest != 0) /* Loop to reseed link table */
|
|
{
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur++; /* Remember position */
|
|
alink[ibest] = ilink; /* Chain on rest of list */
|
|
|
|
if (++ibest < MAXDISPBIG)
|
|
{
|
|
continue; /* Loop if we haven't wrapped yet */
|
|
}
|
|
|
|
ibest = 0; /* Wrap to zero */
|
|
}
|
|
|
|
assert(cchbest == 0); /* Counter must be 0 */
|
|
}
|
|
else
|
|
{
|
|
mrci1outsingle(pchbase[icur++]);
|
|
/* Else output single character */
|
|
cchbest = 0; /* Reset counter */
|
|
}
|
|
}
|
|
|
|
assert(icur == cch || icur == cch + 1);
|
|
/* Must be at or past last character */
|
|
if (icur == cch)
|
|
{
|
|
#ifndef VXD
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur; /* Remember position */
|
|
alink[icur % MAXDISPBIG] = ilink;
|
|
/* Chain on rest of list */
|
|
#endif
|
|
mrci1outsingle(pchbase[icur++]); /* Output last character */
|
|
}
|
|
|
|
assert(icur == cch + 1); /* Must be past last character */
|
|
|
|
mrci1outstring(MAXDISPBIG,EOB); /* Put out an end marker */
|
|
}
|
|
|
|
if (cbitsleft != 8)
|
|
{
|
|
charbuf(abits); /* Flush bit buffer */
|
|
}
|
|
|
|
if ((unsigned) (pCompressed - pchcmpBase) > cchunc)
|
|
{
|
|
return((unsigned) -1); /* data expanded or not smaller */
|
|
}
|
|
|
|
return(pCompressed - pchcmpBase); /* Return compressed size */
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI2) MaxCompress
|
|
*/
|
|
|
|
unsigned Mrci2MaxCompress(unsigned char FAR *pchbase,unsigned cchunc,
|
|
unsigned char FAR *pchcmpBase,unsigned cchcmpMax)
|
|
{
|
|
unsigned cchbest; /* Length of best match */
|
|
unsigned cchmatch; /* Length of this match */
|
|
unsigned ibest; /* Position of best match */
|
|
unsigned icur; /* Current position */
|
|
unsigned ihash; /* Hash table index */
|
|
unsigned ilink; /* Link index */
|
|
unsigned char FAR *pch; /* Char pointer */
|
|
unsigned char FAR *pch2; /* Char pointer */
|
|
unsigned char FAR *pchend; /* End of input (-> last valid) */
|
|
unsigned cch; /* per-pass limit */
|
|
|
|
cbitsleft = 8; /* Buffer is empty */
|
|
abits = 0;
|
|
pCompressed = pchcmpBase; /* Initialize pointer */
|
|
|
|
if (cchunc < cchcmpMax)
|
|
{
|
|
cCompressed = cchunc; /* limit to source size */
|
|
}
|
|
else
|
|
{
|
|
cCompressed = cchcmpMax; /* limit to max size offered */
|
|
}
|
|
|
|
if (cCompressed < SIG_SIZE)
|
|
{
|
|
return((unsigned) -1);
|
|
}
|
|
|
|
*pCompressed++ = 'J';
|
|
*pCompressed++ = 'M';
|
|
*pCompressed++ = '\x00';
|
|
*pCompressed++ = '\x01';
|
|
|
|
cCompressed -= SIG_SIZE;
|
|
|
|
pch = pchbase; /* Initialize */
|
|
|
|
if (cchunc-- == 0)
|
|
{
|
|
return(0); /* Do nothing to empty buffer */
|
|
}
|
|
|
|
inithash(); /* Initialize tables */
|
|
|
|
if (setjmp(bailout) != 0) /* If failure */
|
|
{
|
|
return((unsigned) -1); /* Data expanded */
|
|
}
|
|
|
|
cchbest = 0; /* no match yet */
|
|
icur = 0; /* Initialize */
|
|
|
|
for (cch = SECTOR - 1; cch <= (cchunc + SECTOR - 1); cch += SECTOR)
|
|
{
|
|
assert(cchbest == 0); /* must always start with no match */
|
|
|
|
if (cch > cchunc)
|
|
{
|
|
cch = cchunc; /* limit to exact req count */
|
|
}
|
|
|
|
pchend = &pchbase[cch]; /* Remember end of buffer */
|
|
|
|
while (icur < cch) /* While at least two chars left */
|
|
{
|
|
/* update hash tables for this character */
|
|
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur; /* Remember position */
|
|
alink[icur % MAXDISPBIG] = ilink;
|
|
/* Chain on rest of list */
|
|
|
|
/* walk hash chain looking for matches */
|
|
|
|
while (ilink < icur && icur - ilink <= DISPMAX)
|
|
{ /* While link is valid and in range */
|
|
pch = &pchbase[icur]; /* Point at first byte */
|
|
pch2 = &pchbase[ilink]; /* Point at first byte */
|
|
|
|
if (pch[cchbest] == pch2[cchbest] && word(pch) == word(pch2))
|
|
{ /* If we have a possible best match */
|
|
pch += 2; /* Skip first pair */
|
|
pch2 += 2; /* Skip first pair */
|
|
|
|
while (pch <= pchend) /* Loop to find end of match */
|
|
{
|
|
if (*pch != *pch2++)
|
|
{
|
|
break; /* Break if mismatch */
|
|
}
|
|
pch++; /* Skip matching character */
|
|
}
|
|
|
|
if ((cchmatch = (unsigned)(pch - pchbase) - icur) > cchbest)
|
|
{ /* If new best match */
|
|
cchbest = cchmatch; /* Remember length */
|
|
ibest = ilink; /* Remember position */
|
|
|
|
assert((pch-1) <= pchend);
|
|
|
|
if (pch > pchend)
|
|
{
|
|
break; /* Break if we can't do any better */
|
|
}
|
|
}
|
|
}
|
|
|
|
assert((alink[ilink % MAXDISPBIG] == (unsigned) -1) ||
|
|
(alink[ilink % MAXDISPBIG] < ilink));
|
|
|
|
ilink = alink[ilink % MAXDISPBIG];
|
|
/* Get next link */
|
|
} /* until end of hash chain reached */
|
|
|
|
if (cchbest >= MINMATCH2) /* If we have a string match */
|
|
{
|
|
mrci2outstring(icur - ibest,cchbest);
|
|
/* Describe matching string */
|
|
#ifdef VXD
|
|
if (icur + cchbest >= cch ) /* If end of sector reached */
|
|
#else
|
|
if (icur + cchbest >= cchunc) /* If end of buffer reached */
|
|
#endif
|
|
{
|
|
icur += cchbest; /* Advance the index */
|
|
cchbest = 0; /* reset for next match */
|
|
break; /* Done if buffer exhausted */
|
|
}
|
|
|
|
icur++; /* Skip to first unhashed pair */
|
|
#ifdef VXD
|
|
/* avoid re-seeding all of a big match */
|
|
|
|
if (cchbest > MAXDISPSMALL)
|
|
{ /* If big match */
|
|
icur += cchbest - MAXDISPSMALL - 1;
|
|
/* Skip ahead */
|
|
cchbest = MAXDISPSMALL + 1;
|
|
/* Use shorter length */
|
|
}
|
|
#endif
|
|
/* update hash tables for each add't char in string */
|
|
|
|
ibest = icur % MAXDISPBIG; /* Get current link table index */
|
|
|
|
while (--cchbest != 0) /* Loop to reseed link table */
|
|
{
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur++; /* Remember position */
|
|
alink[ibest] = ilink; /* Chain on rest of list */
|
|
|
|
if (++ibest < MAXDISPBIG)
|
|
{
|
|
continue; /* Loop if we haven't wrapped yet */
|
|
}
|
|
|
|
ibest = 0; /* Wrap to zero */
|
|
}
|
|
|
|
assert(cchbest == 0); /* Counter must be 0 */
|
|
}
|
|
else
|
|
{
|
|
mrci2outsingle(pchbase[icur++]);
|
|
/* Else output single character */
|
|
cchbest = 0; /* Reset counter */
|
|
}
|
|
}
|
|
|
|
assert(icur == cch || icur == cch + 1);
|
|
/* Must be at or past last character */
|
|
if (icur == cch)
|
|
{
|
|
#ifndef VXD
|
|
ihash = hash(word(&pchbase[icur]));
|
|
/* Get hash index */
|
|
ilink = ahash[ihash]; /* Get link index */
|
|
ahash[ihash] = icur; /* Remember position */
|
|
alink[icur % MAXDISPBIG] = ilink;
|
|
/* Chain on rest of list */
|
|
#endif
|
|
mrci2outsingle(pchbase[icur++]); /* Output last character */
|
|
}
|
|
|
|
assert(icur == cch + 1); /* Must be past last character */
|
|
|
|
mrci2outstring(MAXDISPBIG,EOB); /* Put out an end marker */
|
|
}
|
|
|
|
if (cbitsleft != 8)
|
|
{
|
|
charbuf(abits); /* Flush bit buffer */
|
|
}
|
|
|
|
if ((unsigned) (pCompressed - pchcmpBase) > cchunc)
|
|
{
|
|
return((unsigned) -1); /* data expanded or not smaller */
|
|
}
|
|
|
|
return(pCompressed - pchcmpBase); /* Return compressed size */
|
|
}
|
|
|
|
|
|
/*
|
|
* (decompress) Get a single bit from the compressed input stream.
|
|
*/
|
|
|
|
static unsigned getbit(void)
|
|
{
|
|
unsigned bit; /* Bit */
|
|
|
|
if (cbitsleft) /* If bits available */
|
|
{
|
|
cbitsleft--; /* Decrement bit count */
|
|
|
|
bit = abits & 1; /* Get a bit */
|
|
|
|
abits >>= 1; /* Remove it */
|
|
}
|
|
else /* no bits available */
|
|
{
|
|
if (cCompressed-- == 0) /* If buffer empty */
|
|
{
|
|
longjmp(bailout,1); /* input overrun */
|
|
}
|
|
|
|
cbitsleft = 7; /* Reset count */
|
|
|
|
abits = *pCompressed++; /* Get a byte */
|
|
|
|
bit = abits & 1; /* Get a bit */
|
|
|
|
abits >>= 1; /* Remove it */
|
|
}
|
|
|
|
return(bit); /* Return the bit */
|
|
}
|
|
|
|
|
|
/*
|
|
* (decompress) Get multiple bits from the compressed input stream.
|
|
*/
|
|
|
|
static unsigned getbits(unsigned cbits)
|
|
{
|
|
unsigned bits; /* Bits to return */
|
|
unsigned cbitsdone; /* number of bits added so far */
|
|
unsigned cbitsneeded; /* number of bits still needed */
|
|
|
|
if (cbits <= cbitsleft) /* If we have enough bits */
|
|
{
|
|
bits = abits; /* Get the bits */
|
|
cbitsleft -= cbits; /* Decrement bit count */
|
|
abits >>= cbits; /* Remove used bits */
|
|
}
|
|
else /* If we'll need to read more bits */
|
|
{
|
|
bits = 0; /* No bits set yet */
|
|
cbitsdone = 0; /* no bits added yet */
|
|
cbitsneeded = cbits; /* bits needed */
|
|
|
|
do
|
|
{
|
|
if (cbitsleft == 0) /* If no bits ready */
|
|
{
|
|
if (cCompressed-- == 0) /* count down used */
|
|
{
|
|
longjmp(bailout,1); /* if input overrun */
|
|
}
|
|
|
|
cbitsleft = 8; /* Reset count */
|
|
|
|
abits = *pCompressed++; /* Get 8 new bits */
|
|
}
|
|
|
|
bits |= (abits << cbitsdone); /* copy bits for output */
|
|
|
|
if (cbitsleft >= cbitsneeded) /* if enough now */
|
|
{
|
|
cbitsleft -= cbitsneeded; /* reduce bits remaining available */
|
|
abits >>= cbitsneeded; /* discard used bits */
|
|
break; /* got them */
|
|
}
|
|
else /* if not enough yet */
|
|
{
|
|
cbitsneeded -= cbitsleft; /* reduce bits still needed */
|
|
cbitsdone += cbitsleft; /* increase shift for future bits */
|
|
cbitsleft = 0; /* reduce bits remaining available */
|
|
}
|
|
} while (cbitsneeded); /* go back if more bits needed */
|
|
}
|
|
|
|
return(bits & BITMASK(cbits)); /* Return the bits */
|
|
}
|
|
|
|
|
|
/*
|
|
* (decompress) Expand a match.
|
|
*
|
|
* Note: source overwrite is required (so we can't memcpy or memmove)
|
|
*/
|
|
|
|
static void expandstring(unsigned char FAR **ppchout,unsigned disp,
|
|
unsigned cb)
|
|
{
|
|
unsigned char FAR *source;
|
|
unsigned char FAR *target;
|
|
|
|
assert(cb != 0);
|
|
|
|
target = *ppchout; /* where the bytes go */
|
|
source = target - disp; /* where the bytes come from */
|
|
|
|
*ppchout += cb; /* Update the output pointer */
|
|
|
|
while (cb--)
|
|
{
|
|
*target++ = *source++;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI1) Decompress
|
|
*/
|
|
|
|
unsigned Mrci1Decompress(unsigned char FAR *pchin,unsigned cchin,
|
|
unsigned char FAR *pchdecBase,unsigned cchdecMax)
|
|
{
|
|
unsigned b; /* A byte */
|
|
unsigned length; /* Length of match */
|
|
unsigned disp; /* Displacement */
|
|
unsigned char FAR *pchout; /* Output buffer pointer */
|
|
|
|
abits = 0; /* Bit buffer is empty */
|
|
cbitsleft = 0; /* No bits read yet */
|
|
pCompressed = pchin; /* setup source pointer */
|
|
cCompressed = cchin; /* setup source counter */
|
|
|
|
if ((cCompressed <= SIG_SIZE) || /* must have a signature */
|
|
(*pCompressed++ != 'D') || (*pCompressed++ != 'S'))
|
|
{
|
|
return((unsigned) -1); /* Data corrupted */
|
|
}
|
|
|
|
pCompressed += 2; /* ignore flags */
|
|
cCompressed -= SIG_SIZE;
|
|
|
|
pchout = pchdecBase; /* Point at output buffer */
|
|
|
|
if (setjmp(bailout) != 0) /* If failure */
|
|
{
|
|
return((unsigned) -1); /* Data corrupted */
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
b = getbits(2); /* get two bits */
|
|
|
|
if (b == 1) /* If single byte 128..255 */
|
|
{ /* Get the rest of byte */
|
|
*pchout++ = (unsigned char) (getbits(7) | 0x80);
|
|
continue; /* Next token */
|
|
}
|
|
|
|
if (b == 2) /* If single byte 0..127 */
|
|
{ /* Get the rest of byte */
|
|
*pchout++ = (unsigned char) getbits(7);
|
|
continue; /* Next token */
|
|
}
|
|
|
|
if (b == 0)
|
|
{
|
|
disp = getbits(6) + MINDISPSMALL;
|
|
}
|
|
else /* b == 3 */
|
|
{
|
|
if (getbit() == 0)
|
|
{
|
|
disp = getbits(8) + MINDISPMED;
|
|
}
|
|
else
|
|
{
|
|
disp = getbits(12) + MINDISPBIG;
|
|
}
|
|
}
|
|
|
|
if (disp == MAXDISPBIG)
|
|
{
|
|
if ((unsigned) (pchout - pchdecBase) >= cchdecMax)
|
|
{
|
|
break; /* End marker found */
|
|
}
|
|
else
|
|
{
|
|
continue; /* End sector found */
|
|
}
|
|
}
|
|
|
|
length = 0; /* Initialize */
|
|
|
|
while (getbit() == 0)
|
|
{
|
|
length++; /* Count the leading zeroes */
|
|
}
|
|
|
|
assert(b <= 15); /* Cannot be too big */
|
|
|
|
if (length)
|
|
{
|
|
length = getbits(length) + (1 << length) + 1;
|
|
}
|
|
else
|
|
{
|
|
length = 2;
|
|
}
|
|
|
|
expandstring(&pchout,disp,length); /* Copy the match */
|
|
}
|
|
|
|
return((pchout - pchdecBase)); /* Return decompressed size */
|
|
}
|
|
|
|
|
|
/*
|
|
* (MRCI2) Decompress
|
|
*/
|
|
|
|
unsigned Mrci2Decompress(unsigned char FAR *pchin,unsigned cchin,
|
|
unsigned char FAR *pchdecBase,unsigned cchdecMax)
|
|
{
|
|
unsigned length; /* Length of match */
|
|
unsigned disp; /* Displacement */
|
|
unsigned char FAR *pchout; /* Output buffer pointer */
|
|
|
|
abits = 0; /* Bit buffer is empty */
|
|
cbitsleft = 0; /* No bits read yet */
|
|
pCompressed = pchin; /* setup source pointer */
|
|
cCompressed = cchin; /* setup source counter */
|
|
|
|
if ((cCompressed <= SIG_SIZE) || /* must have a signature */
|
|
(*pCompressed++ != 'J') || (*pCompressed++ != 'M'))
|
|
{
|
|
return((unsigned) -1); /* Data corrupted */
|
|
}
|
|
|
|
pCompressed += 2; /* ignore flags */
|
|
cCompressed -= SIG_SIZE;
|
|
|
|
pchout = pchdecBase; /* Point at output buffer */
|
|
|
|
if (setjmp(bailout) != 0) /* If failure */
|
|
{
|
|
return((unsigned) -1); /* Data corrupted */
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (getbit() == 0) /* literal 00..7F */
|
|
{
|
|
*pchout++ = (unsigned char) getbits(7);
|
|
|
|
continue; /* Next token */
|
|
}
|
|
|
|
if (getbit() == 1) /* literal 80..FF */
|
|
{
|
|
*pchout++ = (unsigned char)(getbits(7) | 0x80);
|
|
|
|
continue; /* Next token */
|
|
}
|
|
|
|
if (getbit() == 0)
|
|
{
|
|
disp = getbits(6) + MINDISPSMALL;
|
|
}
|
|
else
|
|
{
|
|
if (getbit() == 0)
|
|
{
|
|
disp = getbits(8) + MINDISPMED;
|
|
}
|
|
else
|
|
{
|
|
disp = getbits(12) + MINDISPBIG;
|
|
}
|
|
}
|
|
|
|
if (disp == MAXDISPBIG)
|
|
{
|
|
if ((unsigned) (pchout - pchdecBase) >= cchdecMax)
|
|
{
|
|
break; /* End marker found */
|
|
}
|
|
else
|
|
{
|
|
continue; /* End sector found */
|
|
}
|
|
}
|
|
|
|
length = 0; /* Initialize */
|
|
|
|
while (getbit() == 0)
|
|
{
|
|
length++; /* Count the leading zeroes */
|
|
}
|
|
|
|
assert(length <= 15); /* Cannot be too big */
|
|
|
|
if (length)
|
|
{
|
|
length = getbits(length) + (1 << length) + 1;
|
|
}
|
|
else
|
|
{
|
|
length = 2;
|
|
}
|
|
|
|
expandstring(&pchout,disp,length + 1); /* Copy the match */
|
|
}
|
|
|
|
return((pchout - pchdecBase)); /* Return decompressed size */
|
|
}
|