|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 1998.
//
// File: COMPRESS.CXX
//
// Contents: Compressor/Decompressor of data
//
// Classes: CCompress, CDecompress
//
// History: 12-Jun-91 BartoszM Created
// 20-Jun-91 reviewed
// 07-Aug-91 BartoszM Introduced Blocks
// 28-May-92 KyleP Added compression.
// 09-Dec-97 dlee Added PROPID and LZ compression
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include "compress.hxx"
const BYTE pidId2Byte = 0x00; // next 2 bytes is the PROPID
const BYTE pidId4Byte = 0xff; // next 4 bytes is the PROPID
const flagKeyPrefix = 0x1f; // mask; 0x1f if prefix len stored later
const flagKeyCompressed = 0x20; // 1 == unicode compressed, 0 == not
const flagKeyZeroFirst = 0x40; // 1 == compressed & put zeros first
const flagKeyPidContents = 0x80; // 1 == pidContents, 0 == pid stored
template<class T> BOOL isOdd(T value) { return 0 != (value & 1); }
//+-------------------------------------------------------------------------
//
// Member: CBlock::GetFirstKey, public
//
// Effects: Stores the first key of the block in [key]
//
// Arguments: [key] -- Key buffer to be filled in.
//
// Requires: A key must start on this block.
//
// History: 28-May-92 KyleP Crated
//
//--------------------------------------------------------------------------
void CBlock::GetFirstKey( CKeyBuf& key ) { DeCompress();
unsigned off = _offFirstKey; Win4Assert( off != offInvalid );
BYTE flags = _pData[off++];
Win4Assert( 0 == ( flags & flagKeyPrefix ) );
unsigned cbSuffix = _pData[ off++ ];
BYTE * pbKey = key.GetWritableBuf();
if ( 0 != ( flags & flagKeyCompressed ) ) { Win4Assert( 0 != ( flags & flagKeyZeroFirst ) );
unsigned cbKey = 1 + cbSuffix * 2; key.SetCount( cbKey );
*pbKey++ = STRING_KEY; for ( unsigned i = 0; i < cbSuffix; i++ ) { *pbKey++ = 0; *pbKey++ = _pData[ off++ ]; } } else { key.SetCount( cbSuffix ); memcpy( pbKey, _pData + off, cbSuffix ); off += cbSuffix; }
if ( 0 != ( flags & flagKeyPidContents ) ) { key.SetPid( pidContents ); } else { BYTE pidInfo = *(_pData+off); off++;
PROPID pid;
if ( pidId2Byte == pidInfo ) { USHORT usPid; LoadUSHORT( _pData + off, usPid ); pid = usPid ; } else if ( pidId4Byte == pidInfo ) LoadULONG( _pData + off, pid ); else if ( pidInfo > pidIdMaxSmallPid ) pid = pidInfo - pidIdMaxSmallPid + pidNewPidBase - 1; else pid = pidInfo;
Win4Assert( pid != pidAll );
key.SetPid ( pid ); } } //GetFirstKey
// wordlist compression is now good enough that rtl doesn't give us much
// given its cost including the 32k scratch pad and 3% total cpu overhead.
#define CI_COMPRESS_SORT_BLOCKS 0
#if CI_COMPRESS_SORT_BLOCKS
// prevent multiple simultaneous compressions / decompressions
CStaticMutexSem g_mtxCompression; BYTE g_CompressionWorkspace[ 32784 ]; // don't ask
#endif //CI_COMPRESS_SORT_BLOCKS
//+-------------------------------------------------------------------------
//
// Function: DeCompress
//
// Effects: Decompresses the buffer
//
// History: 4-Dec-97 dlee Crated
//
//--------------------------------------------------------------------------
void CBlock::DeCompress() { #if CI_COMPRESS_SORT_BLOCKS
CLock lock( g_mtxCompression );
if ( !_fCompressed ) return;
//
// Alpha decompress touches bytes up to a quadword byte boundary
// beyond what it should. This is a feature.
//
XArray<BYTE> aOut( AlignBlock( _cbInUse, sizeof LONGLONG ) ); ULONG cbOut = 0;
NTSTATUS s = RtlDecompressBuffer( COMPRESSION_FORMAT_LZNT1, aOut.GetPointer(), (ULONG) _cbInUse, _pData, (ULONG) _cbCompressed, &cbOut );
if ( NT_ERROR( s ) ) { ciDebugOut(( DEB_WARN, "failed to decompress 0x%x CBlock: 0x%x, cbOut: 0x%x\n", this, s, cbOut )); Win4Assert( FALSE ); THROW( CException( s ) ); }
ciDebugOut(( DEB_ITRACE, "decompressed from %d to %d\n", (ULONG) _cbCompressed, (ULONG) _cbInUse ));
Win4Assert( cbOut == _cbInUse );
_fCompressed = FALSE; delete [] _pData; _pData = aOut.Acquire();
#else
Win4Assert( !_fCompressed );
#endif
} //DeCompress
//+-------------------------------------------------------------------------
//
// Function: Compress
//
// Effects: Creates a new, smaller block from an old block
//
// History: 4-Dec-97 dlee Crated
//
//--------------------------------------------------------------------------
#if CIDBG == 1
ULONG g_cbUnCompressedTotal = 0; ULONG g_cbUnCompressed = 0; ULONG g_cbCompressed = 0;
#endif // CIDBG == 1
void CBlock::Compress() { Win4Assert( !_fCompressed );
#if CI_COMPRESS_SORT_BLOCKS
#if CIDBG == 1
// Assert the workspace is big enough
ULONG cbWork = 0, cbFragment;
RtlGetCompressionWorkSpaceSize( COMPRESSION_FORMAT_LZNT1, &cbWork, &cbFragment );
// cbWork is 32784 on current builds
Win4Assert( cbWork <= sizeof g_CompressionWorkspace );
#endif // CIDBG == 1
// Compress the data since it may not be needed for a long time.
LONGLONG aOut[ cbInitialBlock / sizeof LONGLONG ]; ULONG cbOut = 0;
CLock lock( g_mtxCompression );
NTSTATUS s = RtlCompressBuffer( COMPRESSION_FORMAT_LZNT1, _pData, _cbInUse, (BYTE *) aOut, sizeof aOut, cbInitialBlock, &cbOut, g_CompressionWorkspace );
//
// ignore failures to compress -- leave it uncompressed
//
if ( NT_SUCCESS( s ) && ( cbOut < _cbInUse ) ) { BYTE * pNew = new BYTE[ cbOut ]; RtlCopyMemory( pNew, aOut, cbOut ); delete [] _pData; _pData = pNew; Win4Assert( cbOut <= 0xffff ); _cbCompressed = (USHORT) cbOut; _fCompressed = TRUE;
ciDebugOut(( DEB_ITRACE, "shrink block %d to %d\n", (ULONG) _cbInUse, (ULONG) _cbCompressed ));
#if CIDBG == 1
g_cbUnCompressedTotal += cbInitialBlock; g_cbUnCompressed += _cbInUse; g_cbCompressed += _cbCompressed;
#endif // CIDBG == 1
} else if ( NT_ERROR( s ) ) { ciDebugOut(( DEB_WARN, "didn't compress: 0x%x\n", s )); }
#else
// Make the buffer as big as it needs to be
#if CIDBG == 1
g_cbUnCompressedTotal += cbInitialBlock; g_cbUnCompressed += _cbInUse;
#endif // CIDBG == 1
XArray<BYTE> aTmp( _cbInUse );
RtlCopyMemory( aTmp.GetPointer(), _pData, _cbInUse ); delete [] _pData; _pData = aTmp.Acquire();
#endif
//
// pitch the memory out of the working set
//
VirtualUnlock( _pData, _fCompressed ? _cbCompressed : _cbInUse ); } //Compress
//+-------------------------------------------------------------------------
//
// Member: CBlock::CompressList, public
//
// Effects: Shrinks blocks in the list, saving memory
//
// History: 4-Dec-97 dlee Crated
//
//--------------------------------------------------------------------------
void CBlock::CompressList() { CBlock * pCur = this;
while ( 0 != pCur ) { pCur->Compress(); pCur = pCur->_pNext; } } //CompressList
//+---------------------------------------------------------------------------
//
// Member: CCompress::CCompress, public
//
// Synopsis: initialize data
//
// Arguments: [fMultipleWid] -- TRUE if > 1 WorkId will be stored.
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP One step construction.
//
//----------------------------------------------------------------------------
CCompress::CCompress () : _cKeyBlock( 0 ), _widCount( 0 ), _occCount( 0 ), _pWidCount( 0 ), _pOccCount( 0 ) { _block = new CBlock; _buf = _block->Buffer(); _cur = _buf;
_lastKey.SetCount( 0 ); // No prefix from previous key.
}
//+---------------------------------------------------------------------------
//
// Member: CCompress::GetFirstBlock, public
//
// Synopsis: stores first entry and initializes cursors
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
CBlock * CCompress::GetFirstBlock() { return _block; }
//+---------------------------------------------------------------------------
//
// Member: CCompress::~CCompress, public
//
// Synopsis: write last wid and occ counts
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Restructured. Added compression.
//
//----------------------------------------------------------------------------
CCompress::~CCompress() { IPutOccCount(); IPutWidCount();
_block->_cbInUse = (USHORT)(_cur - _buf); }
//+---------------------------------------------------------------------------
//
// Member: CCompress::PutKey, public
//
// Synopsis: store compressed key
//
// Arguments: [cb] - size of key
// [buf] - key buffer
// [pid] - property id
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Restructured. Added compression.
//
//----------------------------------------------------------------------------
void CCompress::PutKey ( unsigned cb, const BYTE* buf, PROPID pid ) { ciAssert ( cb != 0 );
IPutWidCount(); IPutKey( cb, buf, pid ); IAllocWidCount(); }
//+---------------------------------------------------------------------------
//
// Member: CCompress::PutWid, public
//
// Synopsis: store compressed work id
//
// Arguments: [wid] - work id
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Restructured. Added compression.
//
//----------------------------------------------------------------------------
void CCompress::PutWid ( WORKID wid ) { ciAssert( wid > 0 && wid < 256 ); // Has to fit in a byte
ciAssert( _widCount < 255 ); // Has to fit in a byte
IPutOccCount(); IPutWid( wid ); IAllocOccCount(); }
//+---------------------------------------------------------------------------
//
// Member: CCompress::PutOcc, public
//
// Synopsis: store compressed occurrence
//
// Arguments: [occ] - occurrence
//
// History: 12-Jun-91 BartoszM Created
// 20-May-92 KyleP Occurrence compression
// 28-May-92 KyleP Restructured. Added compression.
//
//----------------------------------------------------------------------------
void CCompress::PutOcc ( OCCURRENCE occ ) { IPutOcc( occ ); }
//+---------------------------------------------------------------------------
//
// Member: CCompress::AllocNewBlock, private
//
// Synopsis: Allocate and link in new block
//
// History: 07-Aug-91 BartoszM Created
//
//----------------------------------------------------------------------------
void CCompress::AllocNewBlock () { // ciDebugOut (( DEB_ITRACE, "Compress:: Alloc new block\n" ));
_block->_cbInUse = (USHORT)(_cur - _buf); _block->_pNext = new CBlock; _block = _block->_pNext; _buf = _block->_pData; _cur = _buf; }
//+-------------------------------------------------------------------------
//
// Member: CCompress::IPutWidCount, private
//
// Synopsis: Backpatches the workid count.
//
// History: 28-May-92 KyleP Created
//
//--------------------------------------------------------------------------
void CCompress::IPutWidCount() { if ( _pWidCount ) *_pWidCount = (BYTE)_widCount;
_widCount = 0; }
//+-------------------------------------------------------------------------
//
// Member: CCompress::IPutKey, private
//
// Synopsis: Stores a key.
//
// Arguments: [cb] -- Count in bytes of [buf]
// [buf] -- Key.
// [pid] -- Property ID.
//
// History: 28-May-92 KyleP Created
//
// Notes: Format for a key is:
// 1 BYTE -- flags, prefix byte count if < 0xf
// 1 BYTE -- # bytes prefix (from previous key) (optional)
// 1 BYTE -- # bytes suffix (from this key)
// n BYTE -- The suffix (possibly Unicode compressed)
// 1-5 bytes -- Property Id. (if not pidContents)
//
//--------------------------------------------------------------------------
void CCompress::IPutKey( unsigned cb, BYTE const * buf, PROPID pid ) { //
// Calculate the common prefix from the previous key.
//
UINT mincb = __min( _lastKey.Count(), cb );
UINT cPrefix;
for ( cPrefix = 0; (cPrefix < mincb) && ((_lastKey.GetBuf())[cPrefix] == buf[cPrefix]); cPrefix++) continue;
UINT cSuffix = cb - cPrefix;
memcpy( _lastKey.GetWritableBuf() + cPrefix, buf + cPrefix, cSuffix ); _lastKey.SetCount( cb ); _lastKey.SetPid( pid );
//
// Out of space?
//
if ( !KeyWillFit ( cSuffix ) ) AllocNewBlock();
if ( _block->_offFirstKey == offInvalid ) { BackPatch ( (unsigned)(_cur - _buf) ); cPrefix = 0; cSuffix = cb; }
// See if the prefix length can be stored in the flag byte
BYTE flags = ( cPrefix < flagKeyPrefix ) ? cPrefix : flagKeyPrefix;
// pidContents is stored as a bit flag
if ( pidContents == pid ) flags |= flagKeyPidContents;
// Check if the key can be Unicode compressed.
BOOL fCompressible = FALSE; BYTE abCompressed[ MAXKEYSIZE / 2 ]; unsigned cbCompressed = 0;
if ( STRING_KEY == *buf ) { Win4Assert( isOdd( cPrefix ) != isOdd( cSuffix ) );
BYTE const * pbCompressCheck = buf; unsigned cSuffixTmp = cSuffix;
if ( 0 == cPrefix ) { pbCompressCheck++; cSuffixTmp--; } else { pbCompressCheck += cPrefix; }
BYTE const * pbCheck = pbCompressCheck; BYTE const * pbAfter = buf + cb; fCompressible = TRUE;
if ( isOdd( cSuffixTmp ) ) { while ( pbCheck < pbAfter ) { abCompressed[ cbCompressed++ ] = *pbCheck++;
if ( pbCheck < pbAfter ) { if ( 0 != *pbCheck ) { fCompressible = FALSE; break; } else pbCheck++; } } } else { while ( pbCheck < pbAfter ) { if ( 0 != *pbCheck ) { fCompressible = FALSE; break; }
abCompressed[ cbCompressed++ ] = * ( pbCheck + 1 ); pbCheck += 2; }
if ( fCompressible ) flags |= flagKeyZeroFirst; }
if ( fCompressible ) flags |= flagKeyCompressed; }
// Store the flags
*_cur++ = flags;
// Store the prefix length if it didn't fit in the flags field
if ( cPrefix >= flagKeyPrefix ) *_cur++ = (BYTE) cPrefix;
//
// Store the key.
//
if ( fCompressible ) { *_cur++ = (BYTE) cbCompressed; memcpy( _cur, abCompressed, cbCompressed ); _cur += cbCompressed; } else { *_cur++ = (BYTE) cSuffix; memcpy( _cur, buf + cPrefix, cSuffix ); _cur += cSuffix; }
//
// store the pid in 1, 3, or 5 bytes
// note: pids 0 and 0xff are reserved as markers
//
// pids 1 - 0x40 are stored as is in 1 byte
// pids 0x1000 to 0x10bd are stored as 1 byte 0x41 to 0xfe
// other pids < 0x10000 are stored as pidID2Byte followed by 2 bytes
// pids >= 0x10000 are stored as pidID4Byte followed by 4 bytes
//
Win4Assert( pidAll != pid );
if ( pidContents != pid ) { if ( pid <= pidIdMaxSmallPid ) { *_cur++ = (BYTE) pid; } else if ( ( pid >= pidNewPidBase ) && ( pid < ( pidNewPidBase + 0xfd - pidIdMaxSmallPid ) ) ) { *_cur++ = (BYTE) ( pid - pidNewPidBase + pidIdMaxSmallPid + 1 ); } else if ( pid < 0x10000 ) { // 1 byte id plus 2 bytes of pid
*_cur++ = pidId2Byte; _cur += StoreUSHORT( _cur, (USHORT) pid ); } else { // 1 byte id plus 4 bytes of pid
*_cur++ = pidId4Byte; _cur += StoreULONG( _cur, pid ); } } } //IPutKey
//+-------------------------------------------------------------------------
//
// Member: CCompress::IAllocWidCount, private
//
// Synopsis: Allocates space for WorkId count.
//
// History: 28-May-92 KyleP Created
//
//--------------------------------------------------------------------------
void CCompress::IAllocWidCount() { //
// space for wid count
// Note: the actual BYTE space has been pre-reserved in PutKey -- there
// is room for the wid, so we don't have to check and AllocNewBlock.
//
Win4Assert( WidWillFit() );
_pWidCount = _cur; _cur++; _lastWid = widInvalid; }
//+-------------------------------------------------------------------------
//
// Member: CCompress::IPutWid, private
//
// Synopsis: Stores a WorkId
//
// Arguments: [wid] -- WorkId
//
// History: 28-May-92 KyleP Created
//
//--------------------------------------------------------------------------
void CCompress::IPutWid( WORKID wid ) { _widCount++;
if ( ! WidAndOccCountWillFit() ) AllocNewBlock();
_lastWid = wid; *_cur = BYTE( wid ); _cur++; }
//+---------------------------------------------------------------------------
//
// Member: CCompress::IPutOccCount, public
//
// Synopsis: Write previous occurrence count and allocate new one.
//
// History: 20-May-92 KyleP Created
//
//----------------------------------------------------------------------------
void CCompress::IPutOccCount() { //
// Write previous occurence count
//
if ( _pOccCount ) StoreUINT( _pOccCount, _occCount );
_occCount = 0; _lastOcc = 0; }
//+-------------------------------------------------------------------------
//
// Member: CCompress::IAllocOccCount, private
//
// Synopsis: Allocates space for occurrence count.
//
// History: 28-May-92 KyleP Created
//
//--------------------------------------------------------------------------
void CCompress::IAllocOccCount() { //
// Allocate space for new occ count. Note that for the many-wid
// compressor this has been pre-reserved and will succeed.
//
if ( ! OccCountWillFit() ) AllocNewBlock();
_pOccCount = _cur; _cur += sizeof (unsigned); }
//+-------------------------------------------------------------------------
//
// Member: CCompress::IPutOcc, private
//
// Synopsis: Stores an occurrence delta
//
// Arguments: [occ] -- Occurrence
//
// History: 28-May-92 KyleP Created
//
// Notes: Format for an occurrence delta is:
//
// delta <= 255 -- Store as BYTE
// delta <= 65535 -- Store as 0 BYTE + 2 BYTES
// delta > 65535 -- Store as 3 0 BYTES + 4 BYTES
//
//--------------------------------------------------------------------------
void CCompress::IPutOcc( OCCURRENCE occ ) { _occCount++; OCCURRENCE occDelta = occ - _lastOcc; _lastOcc = occ;
ciAssert( occDelta != 0 );
if ( !OccWillFit( occDelta ) ) AllocNewBlock();
if ( occDelta <= (1 << 8) - 1 ) { *_cur = BYTE( occDelta ); _cur++; } else if ( occDelta <= (1 << 16) - 1 ) { ciAssert( sizeof( USHORT ) == 2 ); *_cur++ = 0; _cur += StoreUSHORT( _cur, (USHORT)occDelta ); } else { ciAssert( sizeof( ULONG ) == 4 );
*_cur++ = 0; _cur += StoreUSHORT( _cur, 0 ); _cur += StoreULONG( _cur, occDelta ); } }
//+---------------------------------------------------------------------------
//
// Member: COneWidCompress::PutKey, public
//
// Synopsis: store compressed key
//
// Arguments: [cb] - size of key
// [buf] - key buffer
// [pid] - property id
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
void COneWidCompress::PutKey ( unsigned cb, const BYTE* buf, PROPID pid ) { ciAssert ( cb != 0 );
IPutOccCount(); IPutKey( cb, buf, pid ); IAllocOccCount(); }
//+---------------------------------------------------------------------------
//
// Member: COneWidCompress::PutWid, public
//
// Synopsis: store compressed work id
//
// Arguments: [wid] - work id
//
// History: 28-May-92 KyleP Created
//
// Notes: Storing a work id is an illegal operation for the one
// wid compressor. This method can actually disappear in
// the retail version.
//
//----------------------------------------------------------------------------
#if CIDBG == 1
void COneWidCompress::PutWid ( WORKID ) { ciAssert( FALSE ); }
#endif // CIDBG == 1
//+---------------------------------------------------------------------------
//
// Member: COneWidCompress::PutOcc, public
//
// Synopsis: store compressed occurrence
//
// Arguments: [occ] - occurrence
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
void COneWidCompress::PutOcc ( OCCURRENCE occ ) { IPutOcc( occ ); }
//+-------------------------------------------------------------------------
//
// Member: CDecompress::Init, public
//
// Synopsis: Initialize decompressor.
//
// Effects: Loads the first Key, WorkId, etc. in the block.
//
// Arguments: [block] -- Block to decompress.
//
// Requires: A key must start in [block].
//
// History: 28-May-92 KyleP Created
//
// Notes: CDecompress needs a two stage construction because we
// don't know at cursor creation time what block will be
// decompressed yet we want the decompressor inside the
// cursor (e.g. no pointer)
//
//--------------------------------------------------------------------------
void CDecompress::Init ( CBlock* block ) { block->DeCompress();
_block = block; _buf = _block->_pData; _cbInUse = _block->_cbInUse; ciAssert ( _block->_offFirstKey != offInvalid );
_cur = _buf + _block->_offFirstKey;
LoadKey(); LoadWidCount(); LoadWid(); LoadOccCount(); LoadOcc(); }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::GetNextKey, public
//
// Synopsis: Advance to next key, cache it, and return it
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
//----------------------------------------------------------------------------
const CKeyBuf* CDecompress::GetNextKey() { if ( !KeyExists() ) return 0;
//
// skip remaining work id's
//
while ( NextWorkId() != widInvalid ) continue; // Null body
LoadKey();
if ( KeyExists() ) { LoadWidCount(); LoadWid(); LoadOccCount(); LoadOcc(); }
return GetKey(); }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::NextWorkId, public
//
// Synopsis: Advance to next work id, cache it, and return it
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
//----------------------------------------------------------------------------
WORKID CDecompress::NextWorkId() { ciAssert ( KeyExists() );
//
// Skip the remaining occurrences
//
while ( NextOccurrence() != OCC_INVALID ); // Null body
if ( _widCountLeft == 0 ) { _curWid = widInvalid; _curOcc = OCC_INVALID; } else { LoadWid(); LoadOccCount(); LoadOcc(); }
return _curWid; }
void CDecompress::RatioFinished (ULONG& denom, ULONG& num) { denom = _widCount; Win4Assert ( _widCount >= _widCountLeft ); num = _widCount - _widCountLeft; }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::NextOccurrence, public
//
// Synopsis: Advance to next occurrence, return it
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
//----------------------------------------------------------------------------
OCCURRENCE CDecompress::NextOccurrence() { ciAssert ( KeyExists() );
if ( _occCountLeft == 0 ) _curOcc = OCC_INVALID; else LoadOcc();
return _curOcc; }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::LoadNextBlock, private
//
// Synopsis: Follow link to next block
//
// History: 07-Aug-91 BartoszM Created
//
//----------------------------------------------------------------------------
BOOL CDecompress::LoadNextBlock () { //
// don't recompress _block here -- other queries or merges may be
// using the block, and there is no refcount.
//
if ( _block->_pNext == 0 ) { _curKey.SetCount(0); return FALSE; }
_block = _block->_pNext;
_block->DeCompress();
_buf = _block->_pData; _cbInUse = _block->_cbInUse; _cur = _buf;
return TRUE; }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::LoadKey, private
//
// Synopsis: Cache key wid and occurrence under cursor
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
// Notes: End of block can be either before the beginning
// of the key or after wid count.
//
//----------------------------------------------------------------------------
void CDecompress::LoadKey() { if ( EndBlock() && !LoadNextBlock() ) return;
BYTE flags = *_cur++;
UINT cPrefix = ( flagKeyPrefix & flags ); if ( flagKeyPrefix == cPrefix ) cPrefix = *_cur++;
UINT cSuffix = *_cur++; BYTE * pbKey = _curKey.GetWritableBuf() + cPrefix; UINT cbKey = cPrefix;
if ( 0 != ( flagKeyCompressed & flags ) ) { cbKey += cSuffix * 2;
if ( 0 == cPrefix ) { cbKey++; *pbKey++ = STRING_KEY; }
BOOL fZeroFirst = ( 0 != ( flags & flagKeyZeroFirst ) );
if ( !fZeroFirst ) { *pbKey++ = *_cur++; cbKey--; cSuffix--; }
Win4Assert( isOdd( cbKey ) );
for ( unsigned i = 0; i < cSuffix; i++ ) { *pbKey++ = 0; *pbKey++ = *_cur++; }
} else { memcpy( pbKey, _cur, cSuffix ); _cur += cSuffix; cbKey += cSuffix; }
_curKey.SetCount( cbKey );
if ( 0 != ( flags & flagKeyPidContents ) ) { _curKey.SetPid( pidContents ); } else { BYTE pidInfo = *_cur++;
PROPID pid;
if ( pidId2Byte == pidInfo ) { USHORT usPid; _cur += LoadUSHORT( _cur, usPid ); pid = usPid; } else if ( pidId4Byte == pidInfo ) _cur += LoadULONG( _cur, pid ); else if ( pidInfo > pidIdMaxSmallPid ) pid = pidInfo - pidIdMaxSmallPid + pidNewPidBase - 1; else pid = pidInfo;
Win4Assert( pid != pidAll );
_curKey.SetPid( pid ); } } //LoadKey
//+-------------------------------------------------------------------------
//
// Member: CDecompress::LoadWidCount, private
//
// Synopsis: Loads workid count.
//
// History: 28-May-92 KyleP Created
//
//--------------------------------------------------------------------------
void CDecompress::LoadWidCount() { if ( EndBlock() ) LoadNextBlock();
_widCount = *_cur; _widCountLeft = _widCount; _cur++; }
//+---------------------------------------------------------------------------
//
// Member: CDecompress::LoadWid, private
//
// Synopsis: Cache work id and occurrence under cursor
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
//----------------------------------------------------------------------------
void CDecompress::LoadWid () { if ( EndBlock() ) LoadNextBlock();
ciAssert ( KeyExists() );
_curWid = *_cur; _cur++; _widCountLeft--; }
//+---------------------------------------------------------------------------
//
// Member: CDeCompress::LoadOccCount, private
//
// Synopsis: Cache occurrence count under cursor
//
// History: 21-May-92 KyleP Created
//
//----------------------------------------------------------------------------
void CDecompress::LoadOccCount() { if ( EndBlock() ) LoadNextBlock();
ciAssert ( KeyExists() );
_cur += LoadUINT( _cur, _occCount );
_occCountLeft = _occCount; _curOcc = 0; }
//+---------------------------------------------------------------------------
//
// Member: CDeCompress::LoadOcc, private
//
// Synopsis: Cache occurrence under cursor
//
// Requires: valid occurrence under the cursor
//
// History: 12-Jun-91 BartoszM Created
// 28-May-92 KyleP Added compression. Restructured.
//
//----------------------------------------------------------------------------
void CDecompress::LoadOcc() { if ( EndBlock() ) LoadNextBlock();
ciAssert ( KeyExists() );
OCCURRENCE occDelta;
occDelta = *_cur++;
if ( occDelta == 0 ) { _cur += LoadUSHORT( _cur, (USHORT &)occDelta );
if ( occDelta == 0 ) { _cur += LoadULONG( _cur, occDelta ); } }
_curOcc += occDelta; _occCountLeft--; }
//+-------------------------------------------------------------------------
//
// Member: COneWidDecompress::Init, public
//
// Synopsis: Initialize decompressor.
//
// Effects: Loads the first Key, Occurrence, etc. in the block.
//
// Arguments: [block] -- Block to decompress.
//
// Requires: A key must start in [block].
//
// History: 28-May-92 KyleP Created
//
// Notes: CDecompress needs a two stage construction because we
// don't know at cursor creation time what block will be
// decompressed yet we want the decompressor inside the
// cursor (e.g. no pointer)
//
//--------------------------------------------------------------------------
void COneWidDecompress::Init ( CBlock* block ) { block->DeCompress();
_block = block; _buf = _block->_pData; _cbInUse = _block->_cbInUse; ciAssert ( _block->_offFirstKey != offInvalid );
_cur = _buf + _block->_offFirstKey;
LoadKey(); LoadOccCount(); LoadOcc(); }
//+---------------------------------------------------------------------------
//
// Member: COneWidDecompress::GetNextKey, public
//
// Synopsis: Advance to next key, cache it, and return it
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
const CKeyBuf* COneWidDecompress::GetNextKey() { if ( !KeyExists() ) return 0;
//
// skip remaining occurrences
//
while ( NextOccurrence() != OCC_INVALID ); // Null body
LoadKey();
if ( KeyExists() ) { LoadOccCount(); LoadOcc(); }
return GetKey(); }
//+---------------------------------------------------------------------------
//
// Member: COneWidDecompress::NextWorkId, public
//
// Synopsis: Advance to next work id, cache it, and return it
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
WORKID COneWidDecompress::NextWorkId() { ciAssert( FALSE );
return( widInvalid ); }
//+---------------------------------------------------------------------------
//
// Member: COneWidDecompress::NextOccurrence, public
//
// Synopsis: Advance to next occurrence, return it
//
// History: 28-May-92 KyleP Created
//
//----------------------------------------------------------------------------
OCCURRENCE COneWidDecompress::NextOccurrence() { ciAssert ( KeyExists() );
if ( _occCountLeft == 0 ) _curOcc = OCC_INVALID; else LoadOcc ();
return _curOcc; }
|