|
|
/*++
Copyright (C) 1994-2001 Microsoft Corporation
Module Name:
MRCICODE.CPP
Abstract:
MRCI 1 & MRCI 2 maxcompress and decompress functions
History:
--*/
#include "precomp.h"
#include "mrcicode.h" /* prototype verification */
#ifndef NDEBUG
#define NDEBUG /* turn off assertions */
#endif
#include <assert.h> /* use NDEBUG to inhibit */
#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 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 */
/*
* (compress) Reset the hash tables between blocks. */
void CBaseMrciCompression::inithash(void) { unsigned *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. */
void CBaseMrciCompression::charbuf(unsigned c) { if (cCompressed-- == 0) /* make sure there's room */ { throw 1; /* Data expanding! */ }
*pCompressed++ = (unsigned char) c; /* Put character into buffer */ }
/*
* (compress) Write n bits to the compressed bitstream. */
void CBaseMrciCompression::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. */
void CBaseMrciCompression::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. */
void CBaseMrciCompression::mrci1outsingle(unsigned ch) { ch = (ch << 2) | ((ch & 0x80) ? 1 : 2);
putbits(ch,9); }
/*
* (MRCI2 compress) Encode a literal into the compressed stream. */
void CBaseMrciCompression::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. */
void CBaseMrciCompression::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. */
void CBaseMrciCompression::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 CBaseMrciCompression::Mrci1MaxCompress(unsigned char *pchbase,unsigned cchunc, unsigned char *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 *pch; /* Char pointer */ unsigned char *pch2; /* Char pointer */ unsigned char *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 */
try { 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 */ } } catch(int i) { return -1; }
return(pCompressed - pchcmpBase); /* Return compressed size */ }
/*
* (MRCI2) MaxCompress */
unsigned CBaseMrciCompression::Mrci2MaxCompress(unsigned char *pchbase,unsigned cchunc, unsigned char *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 *pch; /* Char pointer */ unsigned char *pch2; /* Char pointer */ unsigned char *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 */
try { 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 */ } } catch(int i) { return -1; }
return(pCompressed - pchcmpBase); /* Return compressed size */ }
/*
* (decompress) Get a single bit from the compressed input stream. */
unsigned CBaseMrciCompression::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 */ { throw 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. */
unsigned CBaseMrciCompression::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 */ { throw 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) */
void CBaseMrciCompression::expandstring(unsigned char **ppchout,unsigned disp, unsigned cb) { unsigned char *source; unsigned char *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 CBaseMrciCompression::Mrci1Decompress(unsigned char *pchin,unsigned cchin, unsigned char *pchdecBase,unsigned cchdecMax) { unsigned b; /* A byte */ unsigned length; /* Length of match */ unsigned disp; /* Displacement */ unsigned char *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 */
try { 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 */ } } catch(int i) { return -1; }
return((pchout - pchdecBase)); /* Return decompressed size */ }
/*
* (MRCI2) Decompress */
unsigned CBaseMrciCompression::Mrci2Decompress(unsigned char *pchin,unsigned cchin, unsigned char *pchdecBase,unsigned cchdecMax) { unsigned length; /* Length of match */ unsigned disp; /* Displacement */ unsigned char *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 */
try { 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 */ } } catch(int i) { return -1; }
return((pchout - pchdecBase)); /* Return decompressed size */ }
|