819 lines
21 KiB
C++
819 lines
21 KiB
C++
|
/*****************************************************************************
|
||
|
* *
|
||
|
* ZECK2.C *
|
||
|
* *
|
||
|
* Copyright (C) Microsoft Corporation 1990. *
|
||
|
* All Rights reserved. *
|
||
|
* *
|
||
|
******************************************************************************
|
||
|
* *
|
||
|
* Module Intent *
|
||
|
* *
|
||
|
* Zeck compression routines for bitmaps & topic 2K blocks.
|
||
|
*
|
||
|
* Note: this is a 2nd version based on t-SteveF's stuff in this directory.
|
||
|
* This new version is designed to work with both topic 2K blocks and
|
||
|
* (possibly huge) bitmaps. It retains the ability to suppress compression
|
||
|
* to allow back patching into the topic.
|
||
|
*
|
||
|
* It does NOT retain the ability to be called repeatedly to resume
|
||
|
* previous compression states.
|
||
|
* *
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Revision History: Created 09/20/90 by Tomsn
|
||
|
* 12/17/90 Use based pointers for the tree to save space.
|
||
|
* 02/04/91 Maha - changed ints to INT
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
|
||
|
#ifdef _DEBUG
|
||
|
#undef THIS_FILE
|
||
|
static char THIS_FILE[] = __FILE__;
|
||
|
#endif
|
||
|
|
||
|
#include "zeckdat.h"
|
||
|
|
||
|
typedef struct insertretval {
|
||
|
DWORD uiBackwardsOffset; // backwards offset of the match from current pos.
|
||
|
DWORD cbPatternLen; // the length of the match.
|
||
|
} INSERTRETVAL;
|
||
|
|
||
|
static INSERTRETVAL INLINE InsertNode(PBYTE pbByte);
|
||
|
static void STDCALL InitTree(void);
|
||
|
static void INLINE DeleteNode(void);
|
||
|
|
||
|
// We support both _based and non-based forms for the tree:
|
||
|
|
||
|
// compression nodes for building tree used to find repetitions:
|
||
|
typedef struct nd ND, *QND, **QQND;
|
||
|
|
||
|
struct nd {
|
||
|
PBYTE pbVal; // pointer to buff location
|
||
|
QND qndRight; // left and right node
|
||
|
QND qndLeft;
|
||
|
QQND qqndPar; // parent node
|
||
|
};
|
||
|
|
||
|
#define RING_BUF_LEN 4096 // used for node recycling.
|
||
|
#define GRIND_UPDATE 1024 // when to update grinder
|
||
|
|
||
|
QQND qqndRoots;
|
||
|
QND qndNodes;
|
||
|
PBYTE qbSuppressBuf;
|
||
|
|
||
|
QSUPPRESSZECK pSuppressNext; // next suppress guy to watch out for.
|
||
|
static int iCurrent; // current insertion index into the ring buffer.
|
||
|
|
||
|
/* LcbCompressZeck -
|
||
|
*
|
||
|
* This is the only entry point into zeck compression. Compresses 'cbSrc'
|
||
|
* bytes at 'pbSrc' into the buffer at 'pbDst', suppressing compression
|
||
|
* for bytes specified by the qSuppress linked list.
|
||
|
*
|
||
|
* pbSrc - IN huge pointer to source buffer.
|
||
|
* pbDst- IN huge pointer to dest buffer.
|
||
|
* cbSrc - IN count of bytes to be compressed.
|
||
|
* cbDest- IN limit count of bytes to put in pbDst - used to create
|
||
|
* the 4K topic blocks.
|
||
|
*
|
||
|
* NOTE: if cbDest != COMPRESS_CBNONE it will try to fill the whole dest
|
||
|
* buffer EVEN IF THAT MEANS PERFORMING LESS COMPRESSION. This is done
|
||
|
* to handle the 4K topic blocks which must be 4K and no less.
|
||
|
*
|
||
|
* pcbSrcUsed- OUT count of src bytes compressed into pbDst (needed when
|
||
|
* a cbDest limit is used).
|
||
|
* qSuppress IN OUT linked list of compression suppression specifiers,
|
||
|
* the out value is where the suppression ranges ended
|
||
|
* up in the pbDst buffer.
|
||
|
*
|
||
|
* RETURNS: length of compressed data put in pbDst, 0 for error.
|
||
|
*/
|
||
|
|
||
|
PBYTE pbSrcBase; // used for suppression coordination based on 4K
|
||
|
// wrap around suppression buffer.
|
||
|
|
||
|
/*
|
||
|
* [ralphw] I no longer allow COMPRESS_CBNONE (0) to be passed for
|
||
|
* cbDest. If you really don't care about checking the destination,
|
||
|
* then pass in 2147483647 (INT_MAX) for cbDest.
|
||
|
*/
|
||
|
|
||
|
static const BYTE abBitFlags[] =
|
||
|
{ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
|
||
|
|
||
|
int STDCALL LcbCompressZeck(PBYTE pbSrc, PBYTE pbDst, int cbSrc, int cbDest,
|
||
|
int* pcbSrcUsed, QSUPPRESSZECK pSuppress)
|
||
|
{
|
||
|
PBYTE pbStop; // pts to last byte to compress
|
||
|
PBYTE pbLast, pbOrgDst, pbBitFlagsSpot;
|
||
|
UINT iBitdex;
|
||
|
BYTE bBitFlags;
|
||
|
INSERTRETVAL insretval;
|
||
|
ZECKPACKBLOCK zpb;
|
||
|
int cbSuppress = 0;
|
||
|
cGrind = 0;
|
||
|
|
||
|
FAllocateZeckGlobals();
|
||
|
|
||
|
InitTree();
|
||
|
|
||
|
pbOrgDst = pbDst; // save away beginning of dest buffer.
|
||
|
pbSrcBase = pbSrc; // for compression suppression in InsertNode().
|
||
|
|
||
|
iBitdex = 8; // so we get fresh bitflags stuff 1st time through.
|
||
|
bBitFlags = 0;
|
||
|
pbBitFlagsSpot = pbDst; // so initial insertion won't hurt.
|
||
|
pSuppressNext = pSuppress;
|
||
|
|
||
|
// Stop all compression MAX_PATTERN_LEN away from end of buffer so we
|
||
|
// don't accidentally find a pattern running off the end of the buffer:
|
||
|
|
||
|
pbStop = pbSrc + cbSrc - MAX_PATTERN_LEN;
|
||
|
pbLast = pbSrc + cbSrc;
|
||
|
|
||
|
while (pbSrc < pbStop && cbDest > 0) {
|
||
|
|
||
|
// increment the bit flags dex:
|
||
|
|
||
|
++iBitdex;
|
||
|
if (iBitdex > 7) {
|
||
|
// it overflowed, insert bitflags into buffer and start anew:
|
||
|
*pbBitFlagsSpot = bBitFlags;
|
||
|
iBitdex = 0;
|
||
|
bBitFlags = 0;
|
||
|
pbBitFlagsSpot = pbDst++;
|
||
|
--cbDest;
|
||
|
if (cbDest <= 0)
|
||
|
break;
|
||
|
if (++cGrind == GRIND_UPDATE) {
|
||
|
cGrind = 0;
|
||
|
doGrind();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for zeck compression block:
|
||
|
|
||
|
if (cbSuppress ||
|
||
|
(pSuppressNext && pbSrc >= pSuppressNext->rbSuppress)) {
|
||
|
ASSERT(cbSuppress || pbSrc == pSuppressNext->rbSuppress);
|
||
|
|
||
|
// record the destination position:
|
||
|
|
||
|
if (!cbSuppress) {
|
||
|
pSuppressNext->rbNewpos = pbDst;
|
||
|
pSuppressNext->iBitdex = iBitdex;
|
||
|
cbSuppress = pSuppressNext->cbSuppress;
|
||
|
if (!cbSuppress) {
|
||
|
cbSuppress = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// copy the raw byte & mark the suppression buffer:
|
||
|
|
||
|
DeleteNode();
|
||
|
qbSuppressBuf[iCurrent] = TRUE;
|
||
|
*pbDst++ = *pbSrc++;
|
||
|
--cbDest;
|
||
|
++iCurrent;
|
||
|
if (iCurrent >= RING_BUF_LEN) {
|
||
|
iCurrent = 0;
|
||
|
}
|
||
|
--cbSuppress;
|
||
|
if (!cbSuppress) {
|
||
|
pSuppressNext = pSuppressNext->next;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
insretval = InsertNode(pbSrc);
|
||
|
if (insretval.uiBackwardsOffset != 0
|
||
|
&& insretval.cbPatternLen >= MIN_PATTERN_LEN) {
|
||
|
|
||
|
// must have room in dest buffer for patter & two-byte code:
|
||
|
|
||
|
if (cbDest < 2)
|
||
|
goto copy_raw;
|
||
|
|
||
|
// make sure we don't run into a suppression zone:
|
||
|
|
||
|
if (pSuppressNext &&
|
||
|
pbSrc + insretval.cbPatternLen >
|
||
|
pSuppressNext->rbSuppress) {
|
||
|
|
||
|
// prune the match:
|
||
|
|
||
|
insretval.cbPatternLen =
|
||
|
(pSuppressNext->rbSuppress - pbSrc);
|
||
|
}
|
||
|
|
||
|
// make sure it's still worth it:
|
||
|
|
||
|
if (insretval.cbPatternLen < MIN_PATTERN_LEN)
|
||
|
goto copy_raw;
|
||
|
|
||
|
// a pattern match has been found:
|
||
|
|
||
|
ASSERT(insretval.uiBackwardsOffset <= RING_BUF_LEN);
|
||
|
zpb.uiBackwardsOffset = (WORD)
|
||
|
ENCODE_FROM_BACKWARDS(insretval.uiBackwardsOffset);
|
||
|
zpb.cbPatternLen = (WORD)
|
||
|
ENCODE_FROM_PATTERNLEN(insretval.cbPatternLen);
|
||
|
|
||
|
*pbDst++ = zpb.bytes[0];
|
||
|
*pbDst++ = zpb.bytes[1];
|
||
|
cbDest -= 2;
|
||
|
|
||
|
// mark flags byte:
|
||
|
|
||
|
bBitFlags |= abBitFlags[iBitdex];
|
||
|
|
||
|
// Insert nodes for the body of the pattern:
|
||
|
|
||
|
/*
|
||
|
* [ralphw] There are now two almost identical loops
|
||
|
* here. The first one is what usually gets called and saves
|
||
|
* us from having to compare pbSrc with pbStop on every
|
||
|
* iteration.
|
||
|
*/
|
||
|
|
||
|
if (pbSrc < pbStop - MAX_PATTERN_LEN) {
|
||
|
for (--insretval.cbPatternLen; insretval.cbPatternLen;
|
||
|
--insretval.cbPatternLen) {
|
||
|
#ifdef _DEBUG
|
||
|
ASSERT(pbSrc + 1 < pbStop);
|
||
|
#endif
|
||
|
InsertNode(++pbSrc);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (--insretval.cbPatternLen; insretval.cbPatternLen;
|
||
|
--insretval.cbPatternLen) {
|
||
|
if (++pbSrc >= pbStop) { // dont insert past pbStop
|
||
|
// inc past pattern...
|
||
|
|
||
|
pbSrc += insretval.cbPatternLen - 1;
|
||
|
break;
|
||
|
}
|
||
|
InsertNode(pbSrc);
|
||
|
}
|
||
|
}
|
||
|
++pbSrc;
|
||
|
}
|
||
|
else { // copy raw byte:
|
||
|
copy_raw:
|
||
|
*pbDst++ = *pbSrc++;
|
||
|
--cbDest;
|
||
|
}
|
||
|
}
|
||
|
} // while (pbSrc < pbStop && cbDest > 0)
|
||
|
|
||
|
// copy in the last raw bytes:
|
||
|
|
||
|
while(pbSrc < pbLast && cbDest > 0) {
|
||
|
++iBitdex;
|
||
|
if (iBitdex > 7) {
|
||
|
// it overflowed, insert bitflags into buffer and start anew:
|
||
|
*pbBitFlagsSpot = bBitFlags;
|
||
|
iBitdex = 0;
|
||
|
bBitFlags = 0;
|
||
|
pbBitFlagsSpot = pbDst++;
|
||
|
--cbDest;
|
||
|
if (cbDest <= 0)
|
||
|
break;
|
||
|
}
|
||
|
/*
|
||
|
* Check for compression suppression zone -- we don't have to
|
||
|
* suppress compression (since we're not even trying), but we must
|
||
|
* update the rbNewPos pointer.
|
||
|
*/
|
||
|
|
||
|
if (pSuppressNext && pbSrc == pSuppressNext->rbSuppress) {
|
||
|
pSuppressNext->rbNewpos = pbDst;
|
||
|
pSuppressNext->iBitdex = iBitdex;
|
||
|
pSuppressNext = pSuppressNext->next;
|
||
|
}
|
||
|
*pbDst++ = *pbSrc++;
|
||
|
--cbDest;
|
||
|
}
|
||
|
*pbBitFlagsSpot = bBitFlags;
|
||
|
|
||
|
if (pcbSrcUsed) {
|
||
|
*pcbSrcUsed = (DWORD) (pbSrc - pbSrcBase);
|
||
|
}
|
||
|
|
||
|
// ASSERT(pbSrc == pbLast); // no, we may only compress until cbDest == 0
|
||
|
|
||
|
ConfirmOrDie(cbDest >= 0); // This would be really, really bad...
|
||
|
|
||
|
return (pbDst - pbOrgDst);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
|
||
|
FUNCTION: InitTree
|
||
|
|
||
|
PURPOSE: Initialize the tree used in the compression
|
||
|
|
||
|
PARAMETERS:
|
||
|
void
|
||
|
|
||
|
RETURNS:
|
||
|
|
||
|
COMMENTS:
|
||
|
|
||
|
MODIFICATION DATES:
|
||
|
18-Jun-1994 [ralphw] - now assumes lcCalloc is called before
|
||
|
we enter, so we don't need to do so much work to initialize.
|
||
|
|
||
|
***************************************************************************/
|
||
|
|
||
|
static void STDCALL InitTree(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
iCurrent = 0;
|
||
|
|
||
|
/*
|
||
|
* [ralphw] Since qqndRoots and qndNodes are lcCalloc'd, they should
|
||
|
* already have been set to zero.
|
||
|
*/
|
||
|
|
||
|
ASSERT(!qqndRoots[0] && !qqndRoots[255] && !qndNodes[0].qndRight
|
||
|
&& !qndNodes[RING_BUF_LEN - 1].qndRight);
|
||
|
|
||
|
// for (i = 0; i < 256; i++)
|
||
|
// qqndRoots[i] = NULL;
|
||
|
|
||
|
for (i = 0; i < RING_BUF_LEN; i++) {
|
||
|
// qndNodes[i].qndRight = NULL;
|
||
|
// qndNodes[i].qndLeft = NULL;
|
||
|
// qndNodes[i].qqndPar = NULL;
|
||
|
// qndNodes[i].pbVal = NULL;
|
||
|
|
||
|
qbSuppressBuf[i] = TRUE; // initially all is supressed to prevent
|
||
|
// matches from running into unset buffer area.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- DeleteNode
|
||
|
-
|
||
|
* Purpose:
|
||
|
* Delete a specified node from the tree
|
||
|
*
|
||
|
* Arguments:
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*
|
||
|
* Globals Used:
|
||
|
* QND qndNodes
|
||
|
*
|
||
|
* +++
|
||
|
*
|
||
|
* Notes:
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
static void INLINE DeleteNode(void)
|
||
|
{
|
||
|
QND qndDel = (QND) &qndNodes[iCurrent];
|
||
|
QND qnd;
|
||
|
|
||
|
ASSERT(iCurrent < RING_BUF_LEN);
|
||
|
|
||
|
if (qndDel->qqndPar == NULL)
|
||
|
return; // not in tree
|
||
|
|
||
|
// if the node is a leaf, the insert ND is easy
|
||
|
|
||
|
if (qndDel->qndRight == NULL)
|
||
|
qnd = qndDel->qndLeft;
|
||
|
else if (qndDel->qndLeft == NULL)
|
||
|
qnd = qndDel->qndRight;
|
||
|
else { // node to be deleted is an interior node
|
||
|
qnd = qndDel->qndLeft;
|
||
|
|
||
|
if (qnd->qndRight != NULL) {
|
||
|
do {
|
||
|
qnd = qnd->qndRight;
|
||
|
} while (qnd->qndRight != NULL);
|
||
|
|
||
|
*(qnd->qqndPar) = qnd->qndLeft;
|
||
|
if (qnd->qndLeft != NULL)
|
||
|
qnd->qndLeft->qqndPar = qnd->qqndPar;
|
||
|
|
||
|
qnd->qndLeft = qndDel->qndLeft;
|
||
|
if (qnd->qndLeft != NULL)
|
||
|
qnd->qndLeft->qqndPar = &(qnd->qndLeft);
|
||
|
}
|
||
|
qnd->qndRight = qndDel->qndRight;
|
||
|
if (qnd->qndRight != NULL)
|
||
|
qnd->qndRight->qqndPar = &(qnd->qndRight);
|
||
|
}
|
||
|
if (qnd != NULL)
|
||
|
qnd->qqndPar = qndDel->qqndPar;
|
||
|
*(qndDel->qqndPar) = qnd;
|
||
|
qndDel->qqndPar = NULL;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- InsertNode
|
||
|
-
|
||
|
* Purpose:
|
||
|
* Inserts string of length cbStrMax, qbRingBuf[r..r+cbStrMax-1], into
|
||
|
* one of the trees (qqndRoots[*iString]'th tree) and returns the
|
||
|
* longest-match position and length via the global variables iMatchCur and
|
||
|
* cbMatchCur. If cbMatchCur = cbStrMax, then removes the old node in favor
|
||
|
* of the new one, because the old one will be deleted sooner.
|
||
|
*
|
||
|
* Arguments:
|
||
|
* UINT iString - index in qbRingBuf of the string to insert
|
||
|
*
|
||
|
* Returns:
|
||
|
* nothing
|
||
|
*
|
||
|
* Globals Used:
|
||
|
* QND qndNodes, QQND qqndRoots, QB qbRingBuf
|
||
|
* UINT iMatchCur - index in qbRingBuf of longest match
|
||
|
* UINT cbMatchCur - length of longest match
|
||
|
*
|
||
|
* +++
|
||
|
*
|
||
|
* Notes:
|
||
|
* There is a one to one relationship with the i'th position in the
|
||
|
* qbRingBuf and the i'th position in the qndNodes.
|
||
|
*
|
||
|
* We must take care not to use bytes which have not yet been initialized
|
||
|
* in qbRingBuff when finding patters (this differs from previous versions).
|
||
|
* iMax faciliates this check.
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
static INSERTRETVAL INLINE InsertNode(PBYTE pbByte)
|
||
|
{
|
||
|
QQND qqnd;
|
||
|
QND qndNew;
|
||
|
int fComp; // must be signed
|
||
|
UINT cbMatchND, cbMatchCur;
|
||
|
INSERTRETVAL insretval;
|
||
|
PBYTE rbThis, rbLook, rbBestVal;
|
||
|
|
||
|
insretval.uiBackwardsOffset = 0; // indicating no match.
|
||
|
|
||
|
// delete previous string at this position of the circular buffer:
|
||
|
|
||
|
DeleteNode();
|
||
|
|
||
|
// clear the suppression buffer since insertion means it's not suppressed
|
||
|
|
||
|
qbSuppressBuf[iCurrent] = FALSE;
|
||
|
|
||
|
// start with tree index by first in string
|
||
|
|
||
|
qqnd = (QQND) &qqndRoots[*pbByte];
|
||
|
qndNew = (QND) &qndNodes[iCurrent];
|
||
|
ASSERT(iCurrent < RING_BUF_LEN);
|
||
|
|
||
|
qndNew->qndLeft = qndNew->qndRight = NULL;
|
||
|
qndNew->pbVal = pbByte;
|
||
|
|
||
|
// goto first;
|
||
|
|
||
|
rbBestVal = NULL;
|
||
|
cbMatchCur = 0;
|
||
|
|
||
|
do {
|
||
|
if (*qqnd == NULL) {
|
||
|
*qqnd = qndNew; // insert it.
|
||
|
qndNew->qqndPar = qqnd;
|
||
|
goto ret;
|
||
|
}
|
||
|
// compare the string at the current node with the string
|
||
|
// that we are looking for.
|
||
|
rbThis = pbByte;
|
||
|
rbLook = (*qqnd)->pbVal;
|
||
|
for (cbMatchND = 0; cbMatchND <= MAX_PATTERN_LEN; cbMatchND++) {
|
||
|
if ((fComp = (signed char) rbThis[cbMatchND]
|
||
|
- (signed char) rbLook[cbMatchND]) != 0)
|
||
|
break; // no match.
|
||
|
if (qbSuppressBuf[((rbLook + cbMatchND) - pbSrcBase) %
|
||
|
RING_BUF_LEN]) {
|
||
|
break; // running into compression suppression zone.
|
||
|
}
|
||
|
}
|
||
|
// if the length of the matched string is greater then the
|
||
|
// current, make the iMatchCur point the qnd
|
||
|
if (cbMatchND > cbMatchCur) {
|
||
|
rbBestVal = (*qqnd)->pbVal;
|
||
|
cbMatchCur = cbMatchND;
|
||
|
}
|
||
|
|
||
|
// Follow the tree down to the leaves depending on the result
|
||
|
// of the last string compare. When you come the a leaf in the
|
||
|
// tree, you are done and insert the node.
|
||
|
|
||
|
if (fComp >= 0) {
|
||
|
qqnd = &((*qqnd)->qndRight);
|
||
|
} else {
|
||
|
qqnd = &((*qqnd)->qndLeft);
|
||
|
}
|
||
|
|
||
|
// Search for strings while a less then maxium length string
|
||
|
// is found
|
||
|
} while (cbMatchCur <= MAX_PATTERN_LEN);
|
||
|
|
||
|
// replace an older ND with the new node in the tree,
|
||
|
// by replacing the current qnd with the new node qndNew
|
||
|
|
||
|
if (*qqnd != NULL) {
|
||
|
if ((qndNew->qndLeft = (*qqnd)->qndLeft) != NULL) {
|
||
|
(*qqnd) ->qndLeft->qqndPar = &(qndNew->qndLeft);
|
||
|
}
|
||
|
if ((qndNew->qndRight = (*qqnd)->qndRight) != NULL) {
|
||
|
(*qqnd)->qndRight->qqndPar = &(qndNew->qndRight);
|
||
|
}
|
||
|
|
||
|
// insert into left/right side of parent
|
||
|
|
||
|
qndNew->qqndPar = qqnd;
|
||
|
(*qqnd) ->qqndPar = NULL;
|
||
|
*qqnd = qndNew;
|
||
|
}
|
||
|
ret:
|
||
|
|
||
|
// translate the index of the match into a backwards offset:
|
||
|
|
||
|
if (rbBestVal) {
|
||
|
insretval.uiBackwardsOffset = (UINT) (pbByte - rbBestVal);
|
||
|
insretval.cbPatternLen = cbMatchCur > MAX_PATTERN_LEN ?
|
||
|
MAX_PATTERN_LEN : cbMatchCur;
|
||
|
}
|
||
|
|
||
|
// increment iCurrent:
|
||
|
|
||
|
++iCurrent;
|
||
|
if (iCurrent >= RING_BUF_LEN) {
|
||
|
iCurrent = 0;
|
||
|
}
|
||
|
return(insretval);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- FAllocateZeckGlobals
|
||
|
-
|
||
|
* Purpose: Allocate global ring buffer & pattern match tree nodes.
|
||
|
*
|
||
|
* Arguments: none.
|
||
|
*
|
||
|
* Returns: TRUE if successful, FALSE if failure.
|
||
|
*
|
||
|
* Globals Used: qndNodes, qqndRoots, qbRingBuff.
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
void STDCALL FAllocateZeckGlobals(void)
|
||
|
{
|
||
|
if (qndNodes)
|
||
|
ZeroMemory(qndNodes, RING_BUF_LEN * sizeof(ND));
|
||
|
else
|
||
|
qndNodes = (QND) lcCalloc(RING_BUF_LEN * sizeof(ND));
|
||
|
|
||
|
if (qqndRoots)
|
||
|
ZeroMemory(qqndRoots, 256 * sizeof(QND));
|
||
|
else
|
||
|
qqndRoots = (QQND) lcCalloc(256 * sizeof(QND));
|
||
|
|
||
|
if (qbSuppressBuf)
|
||
|
ZeroMemory(qbSuppressBuf, RING_BUF_LEN * sizeof(BYTE));
|
||
|
else
|
||
|
qbSuppressBuf = (PBYTE) lcCalloc(RING_BUF_LEN * sizeof(BYTE));
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- FreeZeckGlobals
|
||
|
-
|
||
|
* Purpose: Free the global ring buffer and pattern tree nodes.
|
||
|
*
|
||
|
* Arguments: None.
|
||
|
*
|
||
|
* Returns: Nothing.
|
||
|
*
|
||
|
* Globals Used: qndNodes, qqndRoots, qbRingBuff.
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
|
||
|
void STDCALL FreeZeckGlobals()
|
||
|
{
|
||
|
if (qndNodes)
|
||
|
lcClearFree(&qndNodes);
|
||
|
if (qqndRoots)
|
||
|
lcClearFree(&qqndRoots);
|
||
|
if (qbSuppressBuf)
|
||
|
lcClearFree(&qbSuppressBuf);
|
||
|
}
|
||
|
|
||
|
int STDCALL LcbUncompressZeck(PBYTE pbSrc, PBYTE pbDst, int cbSrc)
|
||
|
{
|
||
|
BYTE bBitFlags, bBitShift;
|
||
|
PBYTE pbLast, pbOrgDst;
|
||
|
ZECKPACKBLOCK zpb;
|
||
|
|
||
|
bBitShift = 0;
|
||
|
pbLast = pbSrc + cbSrc;
|
||
|
pbOrgDst = pbDst; // save away origional dest.
|
||
|
|
||
|
#ifdef DUMP
|
||
|
rbOrgSrc = pbSrc;
|
||
|
#endif
|
||
|
|
||
|
while (pbSrc < pbLast) {
|
||
|
if (!bBitShift) { // overflowed, get the next flags byte:
|
||
|
bBitFlags = *pbSrc++;
|
||
|
bBitShift = 1;
|
||
|
if (pbSrc >= pbLast)
|
||
|
break;
|
||
|
}
|
||
|
if (bBitFlags & bBitShift) {
|
||
|
int cbPatternLen;
|
||
|
PBYTE pbPattern;
|
||
|
|
||
|
// is a zeck encoding pack:
|
||
|
|
||
|
zpb.bytes[0] = *pbSrc++;
|
||
|
zpb.bytes[1] = *pbSrc++;
|
||
|
cbPatternLen = PATTERNLEN_FROM_ENCODE(zpb.cbPatternLen);
|
||
|
pbPattern = pbDst - BACKWARDS_FROM_ENCODE(zpb.uiBackwardsOffset);
|
||
|
|
||
|
for (; cbPatternLen > 0; --cbPatternLen)
|
||
|
*pbDst++ = *pbPattern++;
|
||
|
}
|
||
|
else {
|
||
|
*pbDst++ = *pbSrc++; // just copy raw byte in:
|
||
|
}
|
||
|
|
||
|
// bump up the bit mask flag:
|
||
|
|
||
|
bBitShift <<= 1;
|
||
|
}
|
||
|
return (pbDst - pbOrgDst);
|
||
|
}
|
||
|
|
||
|
/* Backpatch support -------------------------------------------------
|
||
|
*
|
||
|
* As we compress, the suppression zones get the zeck-code bits
|
||
|
* mingled in. So, while the compression-suppression zones are
|
||
|
* not compressed, they must be backpatched in very special manner
|
||
|
* to handle the mingled zeck-code bits. These routines perform this
|
||
|
* special backpatching.
|
||
|
*
|
||
|
* Currently only long value backpatching is supported.
|
||
|
*
|
||
|
* We use the special suppresszeck.iCodeBits backwards offset to
|
||
|
* determine where the code bits are. If the offset is zero, then
|
||
|
* the 1st byte of code bits immediately preceeds rbNewpos, and
|
||
|
* subsequent bytes of code bits come every 8 bytes.
|
||
|
*
|
||
|
* NOTE: magic -- if the suppresszeck.iBitdex == BITDEX_NONE, then no
|
||
|
* compression was done & simply backpatch using direct access.
|
||
|
*/
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- Name: VMemBackpatchZeck
|
||
|
-
|
||
|
* Purpose:
|
||
|
* Backpatches a zeck compressed block with longwords. Used to update the
|
||
|
* topic size and VA next & prev ptrs in several structures. Special
|
||
|
* magic is performed to deal with code-bytes which are within the
|
||
|
* zeck-compressed image.
|
||
|
*
|
||
|
* Arguments:
|
||
|
* qsuppresszeck - a suppresszeck node specifiying the beginning of the
|
||
|
* struct to backpatch.
|
||
|
* ulOffset - offset into structure of long to backpatch (relative to
|
||
|
* fcl).
|
||
|
* iBitdex - special bitdex value giving the backwards offset of zeck
|
||
|
* code byte.
|
||
|
*
|
||
|
* Returns: Nothing.
|
||
|
*
|
||
|
************************************************************************/
|
||
|
|
||
|
VOID STDCALL VMemBackpatchZeck(QSUPPRESSZECK qsuppresszeck, DWORD ulOffset,
|
||
|
int value)
|
||
|
{
|
||
|
PBYTE qbDest, pbSrc;
|
||
|
UINT iBitdex;
|
||
|
|
||
|
ASSERT(qsuppresszeck->rbNewpos);
|
||
|
|
||
|
iBitdex = qsuppresszeck->iBitdex;
|
||
|
qbDest = qsuppresszeck->rbNewpos + ulOffset;
|
||
|
|
||
|
if (iBitdex == (WORD) BITDEX_NONE) { // if no compression:
|
||
|
*(int*) (qsuppresszeck->rbNewpos + ulOffset) = value;
|
||
|
}
|
||
|
else {
|
||
|
pbSrc = (PBYTE) &value;
|
||
|
qbDest += ulOffset / 8;
|
||
|
iBitdex += (UINT) (ulOffset % 8);
|
||
|
if (iBitdex > 7) {
|
||
|
++qbDest;
|
||
|
iBitdex = iBitdex - 8;
|
||
|
ASSERT(iBitdex < 8);
|
||
|
}
|
||
|
|
||
|
// insert the backpatch:
|
||
|
|
||
|
// REVIEW: if iBitdex < 4, looks like we could just drop the long
|
||
|
// value in, and update iBitdex, pbDest, and pbSrc by 4.
|
||
|
|
||
|
for (int cbSrc = 4; cbSrc; --cbSrc) {
|
||
|
if (iBitdex > 7) {
|
||
|
|
||
|
// skip past that code-bits byte:
|
||
|
|
||
|
++qbDest;
|
||
|
iBitdex = 0;
|
||
|
}
|
||
|
*qbDest++ = *pbSrc++;
|
||
|
++iBitdex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
*
|
||
|
- Name: FDiskBackpatchZeck
|
||
|
-
|
||
|
* Purpose:
|
||
|
* Backpatches a zeck compressed file with longwords. Used to update the
|
||
|
* topic size and VA next & prev ptrs in several structures. Special
|
||
|
* magic is performed to deal with code-bytes which are within the
|
||
|
* zeck-compressed image.
|
||
|
*
|
||
|
* Arguments:
|
||
|
* hf - Handle to filesystem file to backpatch (hfTopic)
|
||
|
* fcl - Beginning of structure to backpatch.
|
||
|
* ulOffset - offset into structure of long to backpatch (relative to
|
||
|
* fcl).
|
||
|
* iBitdex - special bitdex value giving the backwards offset of zeck
|
||
|
* code byte.
|
||
|
*
|
||
|
* Returns: FALSE on error, TRUE otherwise.
|
||
|
*
|
||
|
************************************************************************/
|
||
|
|
||
|
void STDCALL FDiskBackpatchZeck(HF hf, DWORD fcl, DWORD ulOffset,
|
||
|
int iBitdex, DWORD value)
|
||
|
{
|
||
|
DWORD fclT;
|
||
|
|
||
|
fcl += ulOffset;
|
||
|
fclT = LSeekHf(hf, 0, SEEK_CUR);
|
||
|
|
||
|
if (iBitdex == (WORD) BITDEX_NONE) { // if no compression:
|
||
|
LSeekHf(hf, fcl, SEEK_SET);
|
||
|
LcbWriteHf(hf, &value, sizeof(DWORD));
|
||
|
}
|
||
|
else {
|
||
|
PBYTE pbSrc = (PBYTE) &value;
|
||
|
fcl += ulOffset / 8;
|
||
|
iBitdex += (UINT) (ulOffset % 8);
|
||
|
if (iBitdex > 7) { // check for bitdex rollover.
|
||
|
fcl += 1;
|
||
|
iBitdex = iBitdex - 8;
|
||
|
ASSERT(iBitdex < 8);
|
||
|
}
|
||
|
LSeekHf(hf, fcl, SEEK_SET);
|
||
|
|
||
|
// insert the backpatch:
|
||
|
|
||
|
// REVIEW: if iBitdex < 4, we should be able to simply write
|
||
|
// out pbSrc as a single call
|
||
|
|
||
|
for (int cbSrc = 4; cbSrc; --cbSrc) {
|
||
|
if (iBitdex > 7) {
|
||
|
|
||
|
// skip past that code-bits byte:
|
||
|
|
||
|
LSeekHf(hf, 1, SEEK_CUR);
|
||
|
iBitdex = 0;
|
||
|
}
|
||
|
LcbWriteHf(hf, pbSrc, 1);
|
||
|
++pbSrc;
|
||
|
++iBitdex;
|
||
|
}
|
||
|
}
|
||
|
LSeekHf(hf, fclT, SEEK_SET);
|
||
|
}
|