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.
 
 
 
 
 
 

595 lines
17 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.
* *
******************************************************************************
* Testing Notes *
******************************************************************************
* Current Owner: Tomsn *
******************************************************************************
* Released by Development: 10/01/90 *
*****************************************************************************/
/*****************************************************************************
*
* 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 "help.h"
#include "inc\zeckdat.h"
#include <malloc.h>
_subsystem( Compression );
void InitTree( void );
void DeleteNode( UINT16 );
typedef struct insertretval {
UINT16 uiBackwardsOffset; // backwards offset of the match from current pos.
UINT16 cbPatternLen; // the length of the match.
} INSERTRETVAL;
INSERTRETVAL InsertNode( RB rbByte );
// We support both _based and non-based forms for the tree:
#define _TB
#define ZECK_NULL NULL
// compression nodes for building tree used to find repetitions:
typedef struct nd _TB ND, _TB *QND, _TB * _TB *QQND;
struct nd {
RB rbVal; // 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.
GH ghRoots;
GH ghNodes; // 64K!
HANDLE hNodes;
GH ghSuppressBuf;
QQND qqndRoots = ZECK_NULL; // these may be based
QND qndNodes = ZECK_NULL;
QB qbSuppressBuf = NULL; // this is not based
QSUPPRESSZECK qsuppresszeckNext; // 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 'rbSrc' into the buffer at 'rbDest', suppressing compression
* for bytes specified by the qSuppress linked list.
*
* rbSrc - IN huge pointer to source buffer.
* rbDest- IN huge pointer to dest buffer.
* cbSrc - IN count of bytes to be compressed.
* cbDest- IN limit count of bytes to put in rbDest - 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.
*
* qulSrcUsed- OUT count of src bytes compressed into rbDest (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 rbDest buffer.
*
* RETURNS: length of compressed data put in rbDest, 0 for error.
*/
RB rbSrcBase; // used for suppression coordination based on 4K
// wrap around suppression buffer.
DWORD STDCALL LcbCompressZeck( RB rbSrc, RB rbDest, DWORD cbSrc, DWORD cbDest,
QUL qulSrcUsed, QSUPPRESSZECK qSuppress )
{
RB rbStop; // pts to last byte to compression.
#if 0
RB rbDestStop;// pts to last byte in dest buffer, used for limiting
// compression to result in a particular size
#endif
RB rbLast, rbOrgDest, rbBitFlagsSpot;
static BYTE const rgbBitFlags[] =
{ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80 };
UINT16 iBitdex;
BYTE bBitFlags;
INSERTRETVAL insretval;
ZECKPACKBLOCK zpb;
INT16 cbSuppress = 0;
BOOL fAllocatedGlobals;
BOOL fDestLimited;
if( qndNodes == ZECK_NULL ) {
fAllocatedGlobals = TRUE;
if( !FAllocateZeckGlobals() ) {
return( 0 ); // error
}
} else {
fAllocatedGlobals = FALSE;
}
ASSERT( qndNodes != ZECK_NULL && qqndRoots != ZECK_NULL
&& qbSuppressBuf != NULL );
InitTree();
rbOrgDest = rbDest; // save away beginning of dest buffer.
rbSrcBase = rbSrc; // for compression suppression in InsertNode().
fDestLimited = (cbDest != COMPRESS_CBNONE);
iBitdex = 8; // so we get fresh bitflags stuff 1st time through.
bBitFlags = 0;
rbBitFlagsSpot = rbDest; // so initial insertion won't hurt.
qsuppresszeckNext = qSuppress;
// 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:
rbStop = rbSrc + cbSrc - MAX_PATTERN_LEN;
rbLast = rbSrc + cbSrc;
while( rbSrc < rbStop
&& (!fDestLimited || (cbDest <= (DWORD)(rbLast - rbSrc) && cbDest > 0))
) {
// increment the bit flags dex:
++iBitdex;
if( iBitdex > 7 ) {
// it overflowed, insert bitflags into buffer and start anew:
*rbBitFlagsSpot = bBitFlags;
iBitdex = 0;
bBitFlags = 0;
rbBitFlagsSpot = rbDest++;
--cbDest;
if( !cbDest ) break;
/* For the help compiler we want to do a showprogress to detect ^C,
* print a dot, and allow interrupts to occur so the mouse works
* durning hcp. Yes, this is hokey and due to bogus dos extender
* technology.
*
* The 250 parameter == on dot printer for every 2K of compressed data.
*/
}
// Check for zeck compression block:
if( cbSuppress
|| (qsuppresszeckNext && rbSrc >= qsuppresszeckNext->rbSuppress) ) {
ASSERT( cbSuppress || rbSrc == qsuppresszeckNext->rbSuppress );
// record the destination position:
if( !cbSuppress ) {
qsuppresszeckNext->rbNewpos = rbDest;
qsuppresszeckNext->iBitdex = iBitdex;
cbSuppress = qsuppresszeckNext->cbSuppress;
if( !cbSuppress ) {
cbSuppress = 1;
}
}
// copy the raw byte & mark the suppression buffer:
DeleteNode((INT16)iCurrent);
qbSuppressBuf[ iCurrent ] = TRUE;
*rbDest++ = *rbSrc++;
--cbDest;
++iCurrent;
if( iCurrent >= RING_BUF_LEN ) {
iCurrent = 0;
}
--cbSuppress;
if( !cbSuppress ) {
qsuppresszeckNext = qsuppresszeckNext->next;
}
} else {
insretval = InsertNode( rbSrc );
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 (qsuppresszeckNext &&
rbSrc + insretval.cbPatternLen > qsuppresszeckNext->rbSuppress) {
// prune the match:
insretval.cbPatternLen = (UINT16)
(qsuppresszeckNext->rbSuppress - rbSrc);
}
// 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 =
ENCODE_FROM_BACKWARDS( insretval.uiBackwardsOffset );
zpb.cbPatternLen = ENCODE_FROM_PATTERNLEN(insretval.cbPatternLen);
// insert it a byte at a time (since they're huge):
*rbDest++ = zpb.bytes[0];
*rbDest++ = zpb.bytes[1];
cbDest -= 2;
// mark flags byte:
bBitFlags |= rgbBitFlags[iBitdex];
// Insert nodes for the body of the pattern:
for(--insretval.cbPatternLen; insretval.cbPatternLen;
--insretval.cbPatternLen ) {
if( ++rbSrc >= rbStop ) { // don't insert past rbStop...
// inc past pattern...
rbSrc += insretval.cbPatternLen -1;
break;
}
InsertNode( rbSrc );
}
++rbSrc;
}
else { // copy raw byte:
copy_raw:
*rbDest++ = *rbSrc++;
--cbDest;
}
}
}
// copy in the last raw bytes:
while( rbSrc < rbLast && (!fDestLimited || cbDest > 0) ) {
++iBitdex;
if( iBitdex > 7 ) {
// it overflowed, insert bitflags into buffer and start anew:
*rbBitFlagsSpot = bBitFlags;
iBitdex = 0;
bBitFlags = 0;
rbBitFlagsSpot = rbDest++;
--cbDest;
#if 0
if( rbDest >= rbDestStop ) break;
#else
if( fDestLimited && cbDest <= 0 ) break;
#endif
}
/* 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( qsuppresszeckNext && rbSrc == qsuppresszeckNext->rbSuppress ) {
qsuppresszeckNext->rbNewpos = rbDest;
qsuppresszeckNext->iBitdex = iBitdex;
qsuppresszeckNext = qsuppresszeckNext->next;
}
*rbDest++ = *rbSrc++;
--cbDest;
}
*rbBitFlagsSpot = bBitFlags;
if( fAllocatedGlobals )
FreeZeckGlobals();
if( qulSrcUsed ) {
*qulSrcUsed = (DWORD)(rbSrc - rbSrcBase);
}
ASSERT( fDestLimited || rbSrc == rbLast );
return( rbDest - rbOrgDest );
}
/***************************************************************************
*
- InitTree
-
* Purpose:
* Initialize the tree used in the compression
*
* Globals Used:
* QND qndNodes, QQND qqndRoots, QB qbRingBuf
*
* Initialize all root ptrs and tree nodes to NULL.
*
***************************************************************************/
void InitTree()
{
UINT16 i;
iCurrent = 0;
for (i = 0; i < 256; i++)
qqndRoots[i] = ZECK_NULL;
for( i = 0; i < RING_BUF_LEN; i++ ) {
qndNodes[i].qndRight = ZECK_NULL;
qndNodes[i].qndLeft = ZECK_NULL;
qndNodes[i].qqndPar = ZECK_NULL;
qndNodes[i].rbVal = 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:
* UINT ind - index of the node to delete
*
* Returns:
* nothing
*
* Globals Used:
* QND qndNodes
*
* +++
*
* Notes:
*
***************************************************************************/
void DeleteNode(UINT16 ind)
{
QND qndDel = (QND) &qndNodes[ind];
QND qnd;
if (qndDel->qqndPar == ZECK_NULL) {
return; /* not in tree */
}
// if the node is a leaf, the insert ND is easy
if (qndDel->qndRight == ZECK_NULL) {
qnd = qndDel->qndLeft;
} else if (qndDel->qndLeft == ZECK_NULL) {
qnd = qndDel->qndRight;
} else { // node to be deleted is an interior node
qnd = qndDel->qndLeft;
if (qnd->qndRight != ZECK_NULL) {
do {
qnd = qnd->qndRight;
} while (qnd->qndRight != ZECK_NULL);
*(qnd->qqndPar) = qnd->qndLeft;
if( qnd->qndLeft != ZECK_NULL ) qnd->qndLeft->qqndPar = qnd->qqndPar;
qnd->qndLeft = qndDel->qndLeft;
if( qnd->qndLeft != ZECK_NULL ) qnd->qndLeft->qqndPar = &(qnd->qndLeft);
}
qnd->qndRight = qndDel->qndRight;
if( qnd->qndRight != ZECK_NULL ) qnd->qndRight->qqndPar = &(qnd->qndRight);
}
if( qnd != ZECK_NULL ) qnd->qqndPar = qndDel->qqndPar;
*(qndDel->qqndPar) = qnd;
qndDel->qqndPar = ZECK_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.
*
***************************************************************************/
INSERTRETVAL InsertNode( RB rbByte )
{
QQND qqnd;
QND qndNew;
INT16 fComp; /* must be signed */
UINT16 cbMatchND, cbMatchCur;
INSERTRETVAL insretval;
RB rbThis, rbLook, rbBestVal;
insretval.uiBackwardsOffset = 0; // indicating no match.
// delete previous string at this position of the circular buffer:
DeleteNode((INT16)iCurrent);
// clear the suppression buffer since insertion means it's not suppressed:
qbSuppressBuf[iCurrent] = FALSE;
qqnd = (QQND)&qqndRoots[*rbByte]; // start with tree index by first in string
qndNew = (QND)&qndNodes[iCurrent];
qndNew->qndLeft = qndNew->qndRight = ZECK_NULL;
qndNew->rbVal = rbByte;
//goto first;
rbBestVal = NULL;
cbMatchCur = 0;
do {
if( *qqnd == ZECK_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 = rbByte;
rbLook = (*qqnd)->rbVal;
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) - rbSrcBase) % 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)->rbVal;
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 != ZECK_NULL ) {
if( (qndNew->qndLeft = (*qqnd)->qndLeft) != ZECK_NULL ) {
(*qqnd)->qndLeft->qqndPar = &(qndNew->qndLeft);
}
if( (qndNew->qndRight = (*qqnd)->qndRight) != ZECK_NULL ) {
(*qqnd)->qndRight->qqndPar = &(qndNew->qndRight);
}
// insert into left/right side of parent
qndNew->qqndPar = qqnd;
(*qqnd)->qqndPar = ZECK_NULL;
*qqnd = qndNew;
}
ret:
// translate the index of the match into a backwards offset:
if( rbBestVal ) {
insretval.uiBackwardsOffset = (UINT16) (rbByte - 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.
*
***************************************************************************/
BOOL STDCALL FAllocateZeckGlobals()
{
qndNodes = ZECK_NULL;
// DO not use GhAlloc() since it pads the space with debugging info
// and we are allocating exactly 64K here and cannot afford the padding:
hNodes = GhAlloc(GMEM_FIXED, ((long) RING_BUF_LEN) * sizeof(ND));
qndNodes = (QND) PtrFromGh(hNodes);
if ((ghRoots = GhAlloc(GPTR, 256 * sizeof(QND))) != NULL)
qqndRoots = (QQND) (PtrFromGh(ghRoots));
if (qndNodes == NULL || ghRoots == NULL) {
FreeZeckGlobals();
return(FALSE);
}
if ((ghSuppressBuf = GhAlloc(GPTR, RING_BUF_LEN * sizeof(BYTE))) != NULL)
qbSuppressBuf = (QB)(PtrFromGh(ghSuppressBuf));
if (ghSuppressBuf == NULL) {
FreeZeckGlobals();
return(FALSE);
}
return(TRUE);
}
/***************************************************************************
*
- 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 != ZECK_NULL) {
FreeGh(hNodes);
}
qqndRoots = NULL;
qbSuppressBuf = NULL;
if (ghNodes != NULL)
FreeGh(ghNodes);
if (ghRoots != NULL)
FreeGh(ghRoots);
if (ghSuppressBuf != NULL)
FreeGh(ghSuppressBuf);
ghNodes = NULL;
ghRoots = NULL;
ghSuppressBuf = NULL;
qndNodes = ZECK_NULL;
}