//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1991 - 1997. // // File: COMPRESS.HXX // // 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 // //---------------------------------------------------------------------------- #pragma once #include const USHORT offInvalid = 0xffff; const ULONG cbInitialBlock = 8192; //+--------------------------------------------------------------------------- // // Class: CBlock // // Purpose: Block of data in sort chunk // // History: 7-Aug-91 BartoszM Created // //---------------------------------------------------------------------------- class CBlock { public: CBlock() : _pNext(0), _fCompressed(FALSE), _cbCompressed(0), _cbInUse(0), _offFirstKey(offInvalid) { _pData = new BYTE[ cbInitialBlock ]; } ~CBlock() { delete [] _pData; } void GetFirstKey ( CKeyBuf & key ); void CompressList(); void Compress(); void DeCompress(); BYTE * Buffer() { return _pData; } USHORT InUse() { return _cbInUse; } #ifdef CIEXTMODE void CiExtDump(void *ciExtSelf); #endif CBlock * _pNext; BYTE * _pData; // the actual data USHORT _fCompressed; // whether _pData is RtlCompressed USHORT _cbCompressed; // size when compressed USHORT _cbInUse; // size of actual uncompressed data in buffer USHORT _offFirstKey; // offset to first key in buffer }; //+--------------------------------------------------------------------------- // // Class: CCompress // // Purpose: Compress data into a series of blocks // // History: 12-Jun-91 BartoszM Created // 13-May-92 KyleP Added compression. // // Notes: The following compression schemes are used: // o Prefix compressed keys // o 1 Byte Wid/WidCount (max 255 Wid/compressor) // o 1 byte/2 byte/4 byte occurrence deltas. // //---------------------------------------------------------------------------- class CCompress { public: CCompress(); ~CCompress(); CBlock * GetFirstBlock(); void PutKey ( unsigned cb, const BYTE* buf, PROPID pid ); void PutWid ( WORKID wid ); void PutOcc ( OCCURRENCE occ ); BOOL SameWid ( WORKID wid ) const { return ( wid == _lastWid ); } BOOL SamePid ( PROPID pid ) const { return ( pid == _lastKey.Pid() ); } inline BOOL SameKey ( unsigned cb, const BYTE * buf ) const; unsigned KeyBlockCount () { return _cKeyBlock; } #ifdef CIEXTMODE void CiExtDump(void *ciExtSelf); #endif protected: inline BOOL KeyWillFit( unsigned cb ) const; inline BOOL WidAndOccCountWillFit() const; inline BOOL WidWillFit() const; inline BOOL OccWillFit( OCCURRENCE occDelta ) const; inline BOOL OccCountWillFit() const; // // The Internal put methods are used as the building blocks for // PutKey/Wid/Occ in both CCompress and COneWidCompress. // void IPutKey( unsigned cb, const BYTE * buf, PROPID pid ); void IPutWid( WORKID wid ); void IAllocWidCount(); void IPutWidCount(); void IPutOcc( OCCURRENCE occ ); void IAllocOccCount(); void IPutOccCount(); void AllocNewBlock (); void BackPatch ( unsigned off ) { _block->_offFirstKey = (WORD)off; _cKeyBlock++; } WORKID _lastWid; OCCURRENCE _lastOcc; // pointers for back-patching the counts BYTE * _pWidCount; BYTE * _pOccCount; unsigned _widCount; unsigned _occCount; CBlock * _block; // current block unsigned _cKeyBlock; // count of blocks with keys BYTE* _buf; // current buffer to write to BYTE* _cur; // current position in buffer // // For prefix compression. // CKeyBuf _lastKey; }; //+--------------------------------------------------------------------------- // // Class: COneWidCompress // // Purpose: Compress data for single workid into a series of blocks. // // History: 26-May-92 KyleP Created // // Notes: In addition to the compression schemes used in CCompress // no WorkId or WorkId count is stored at all. // //---------------------------------------------------------------------------- class COneWidCompress : public CCompress { public: void PutKey ( unsigned cb, const BYTE* buf, PROPID pid ); #if CIDBG == 1 void PutWid ( WORKID wid ); #endif // CIDBG == 1 void PutOcc ( OCCURRENCE occ ); }; //+--------------------------------------------------------------------------- // // Class: CDecompress // // Purpose: Decompress data // // History: 12-Jun-91 BartoszM Created // 28-May-92 KyleP Added compression // // Notes: CDecompress uncompresses buffers compressed with CCompress. // //---------------------------------------------------------------------------- class CDecompress { public: inline const CKeyBuf* GetKey() const; inline WORKID WorkId() const; inline OCCURRENCE Occurrence() const; // // Init and GetNext* will be over-ridden in COneWidDecompress. // void Init (CBlock* block ); const CKeyBuf* GetNextKey(); WORKID NextWorkId(); OCCURRENCE NextOccurrence(); ULONG WorkIdCount() { return _widCount; } ULONG OccurrenceCount() { return _occCount; } void RatioFinished ( ULONG& denom, ULONG& num ); #ifdef CIEXTMODE void CiExtDump(void *ciExtSelf); #endif protected: // // The Load methods are used as building blocks for both CDecompress // and COneWidDecompress. // BOOL LoadNextBlock (); void LoadKey(); void LoadWidCount(); void LoadWid(); void LoadOccCount(); void LoadOcc(); BOOL KeyExists() const { return _curKey.Count() != 0; } BOOL EndBlock() const { return ( (unsigned)(_cur - _buf) >= _cbInUse ); } CBlock* _block; // current block unsigned _cbInUse; // actual number of bytes in buffer const BYTE* _buf; // buffer to read from const BYTE* _cur; // current position within buffer BYTE _widCount; // work id count unsigned _occCount; // occurrence count unsigned _widCountLeft; // work ids left unsigned _occCountLeft; // occurrences left CKeyBuf _curKey; // Key under the cursor WORKID _curWid; // WorkId under the cursor OCCURRENCE _curOcc; // Occurrence under the cursor }; //+--------------------------------------------------------------------------- // // Class: COneWidDecompress // // Purpose: Decompress data // // History: 28-May-92 KyleP Created // // Notes: COneWidDecompress uncompresses buffers compressed // with COneWidCompress. // //---------------------------------------------------------------------------- class COneWidDecompress : public CDecompress { public: void Init( CBlock* block ); const CKeyBuf* GetNextKey(); WORKID NextWorkId(); OCCURRENCE NextOccurrence(); }; //+--------------------------------------------------------------------------- // // Member: CCompress::SameKey, public // // Synopsis: compare argument with last key // // Arguments: [cb] - size of key // [buf] - key buffer // // History: 12-Jun-91 BartoszM Created // //---------------------------------------------------------------------------- inline BOOL CCompress::SameKey ( unsigned cb, const BYTE * buf ) const { if ( cb == _lastKey.Count() ) return ( memcmp ( buf, _lastKey.GetBuf(), cb ) == 0 ); else return FALSE; } //+--------------------------------------------------------------------------- // // Member: CCompress::KeyWillFit, private // // Synopsis: check if the key will fit into current buffer // // Arguments: [cb] -- byte size of the key suffix // // History: 07-Aug-91 BartoszM Created // 28-May-92 KyleP Adjusted for compression // //---------------------------------------------------------------------------- inline BOOL CCompress::KeyWillFit ( unsigned cb ) const { // This is an absolutely worst-case estimate. Most keys will be smaller. // Note: This also allocates room for the wid count, which is only needed // for the many-wid compressor. unsigned need = cb + // Suffix sizeof(BYTE) + // Flags field sizeof(BYTE) + // Prefix size sizeof(BYTE) + // Suffix size sizeof(PROPID) + sizeof(BYTE) + // Property ID (worst case) sizeof(BYTE); // WID Count return ( (_cur + need) < (_buf + cbInitialBlock ) ); } //+--------------------------------------------------------------------------- // // Member: CCompress::OccCountWillFit, private // // Synopsis: check if the occurrence count will fit into current buffer // // History: 06-Dec-99 dlee Created // //---------------------------------------------------------------------------- inline BOOL CCompress::OccCountWillFit () const { unsigned need = sizeof(unsigned); return ( _cur + need < _buf + cbInitialBlock ); } //+--------------------------------------------------------------------------- // // Member: CCompress::WidAndOccCountWillFit, private // // Synopsis: check if wid and occ count will fit into current buffer // // History: 07-Aug-91 BartoszM Created // 28-May-92 KyleP Adjusted for compression // //---------------------------------------------------------------------------- inline BOOL CCompress::WidAndOccCountWillFit () const { // wid + occCount // Note: this isn't called for single wid compressors. unsigned need = sizeof(BYTE) + sizeof(unsigned); return ( _cur + need < _buf + cbInitialBlock ); } //+--------------------------------------------------------------------------- // // Member: CCompress::WidWillFit, private // // Synopsis: check if wid will fit into current buffer // // History: 06-Dec-99 dlee Created // //---------------------------------------------------------------------------- inline BOOL CCompress::WidWillFit () const { // wid unsigned need = sizeof(BYTE); return ( _cur + need < _buf + cbInitialBlock ); } //+--------------------------------------------------------------------------- // // Member: CCompress::OccWillFit, private // // Synopsis: check occ will fit into current buffer // // History: 07-Aug-91 BartoszM Created // 28-May-92 KyleP Adjusted for compression // //---------------------------------------------------------------------------- inline BOOL CCompress::OccWillFit( OCCURRENCE occDelta ) const { unsigned next; if ( occDelta <= (1 << 8) - 1 ) next = 1; else if ( occDelta <= (1 << 16) - 1 ) next = 3; else next = 7; return ( _cur + next < _buf + cbInitialBlock ); } //+--------------------------------------------------------------------------- // // Member: CDeCompress::GetKey, public // // Synopsis: return current key // // History: 12-Jun-91 BartoszM Created // //---------------------------------------------------------------------------- inline const CKeyBuf * CDecompress::GetKey() const { if( KeyExists() ) return &_curKey; else return 0; } //+--------------------------------------------------------------------------- // // Member: CDeCompress::WorkId, public // // Synopsis: return current work id // // History: 12-Jun-91 BartoszM Created // //---------------------------------------------------------------------------- inline WORKID CDecompress::WorkId() const { ciAssert ( KeyExists() ); return( _curWid ); } //+--------------------------------------------------------------------------- // // Member: CDeCompress::Occurrence, public // // Synopsis: return current occurrence // // History: 12-Jun-91 BartoszM Created // //---------------------------------------------------------------------------- inline OCCURRENCE CDecompress::Occurrence() const { ciAssert ( KeyExists() ); return( _curOcc ); }