Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

818 lines
21 KiB

/*****************************************************************************
* *
* 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);
}