//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1991 - 2000.
//
//  File:       PComp.hxx
//
//  Contents:   Persistent index compressor, and decompressor
//
//  Classes:    CCoder, CKeyComp, CPersComp
//
//  History:    03-Jul-91       KyleP           Created
//              05-Nov-91       BartoszM        Restructured to use CCoder
//              05-Dec-93       w-PatG          Restructured to use CKeyComp
//
//  Notes:      The CKeyComp class is under development, testing phase.
//              Several revisions must be made to increase the amount of
//              inheritence, and improve the coding algorithm used by CKeyComp.
//
//----------------------------------------------------------------------------

#pragma once

#include <keycur.hxx>
#include <misc.hxx>
#include <pdir.hxx>

#include "bitstm.hxx"

#define LINK_MAX_BITS  (CI_PAGE_SHIFT + 8)              // The max. value of "link" of the persistent index
#define LINK_MAX_VALUE ( 1 << LINK_MAX_BITS )   //

const unsigned OccDeltaBits = 7;        // Bits to initially store an
                                        //  occurrence delta.
class CPageBuffer;

//+---------------------------------------------------------------------------
//
//  Class:      CCoder
//
//  Purpose:    Common class for encoding/decoding of compressed data
//
//  Interface:
//
//  History:    05-Nov-91       BartoszM           Created
//
//----------------------------------------------------------------------------

class CCoder
{
public:

    CCoder(WORKID widMax);

    CCoder(WORKID widMax, const CKeyBuf& keyInit);

    CCoder(CCoder& orig);

    virtual ~CCoder();

#if DEVL == 1
    void Dump();
#endif

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif


protected:

    UINT          AverageWidBits ( UINT cWid );

    void          SetAverageBits ( UINT cWid )
                  { _cbitAverageWid = AverageWidBits ( cWid ); }

    const WORKID _widMaximum;           // Maximum workid in current index

    CKeyBuf      _key;

    WORKID       _wid;

    OCCURRENCE   _occ;

    UINT         _cbitAverageWid;          // Log of the average WorkId delta.
};

__forceinline  UINT CCoder::AverageWidBits ( UINT cWid )
{
    ciAssert ( cWid != 0 );
    ciAssert ( _widMaximum / cWid != 0 );
    UINT x = Log2(_widMaximum / cWid);
    ciAssert ( x <= ULONG_BITS );
    ciAssert ( x > 0 );
    return x;
}

//+---------------------------------------------------------------------------
//
//  Class:      CKeyComp (kcomp)
//
//  Purpose:    Key compressor
//
//  Interface:  (Under development)
//
//  History:    12-Nov-93   w-PatG       Created.
//
//  Notes:      Derived class that lies between base class CCoder and further
//              derived class CPersCoder.
//----------------------------------------------------------------------------

const LONGLONG eSigKeyComp = 0x20504d4f4359454bi64;  // "KEYCOMP"

class CKeyComp: public CCoder
{
public:

    CKeyComp(CPhysIndex& phIndex, WORKID widMax, BOOL fUseLinks = TRUE);
    CKeyComp(CPhysIndex& phIndex,
             WORKID widMax,
             const BitOffset & bitOffRestart,
             const BitOffset & bitOffSplitKey,
             const CKeyBuf & splitKey,
             BOOL fUseLinks = TRUE);

    ~CKeyComp();

    void PutKey(const CKeyBuf * pkey, BitOffset & bitOffCurKey);

    __forceinline void GetOffset( BitOffset & bitOff )
    {
        _bitStream.GetOffset( bitOff );
    }

    __forceinline BOOL IsAtSentinel()
    {
        return _key.IsMaxKey();
    }

#if DEVL == 1
    void Dump();
#endif

    void ZeroToEndOfPage()
    {
        _bitStream.ZeroToEndOfPage();
    }

    void InitSignature()
    {
        _bitStream.InitSignature();
    }

    void WriteFirstKeyFull() { _fWriteFirstKeyFull = TRUE; }

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

    void FreeStream()
    {
        _bitStream.FreeStream();
        _bitStreamLink.FreeStream();
    }

protected:

    void BitCompress(ULONG ul, unsigned cbitAverage);

    void IBitCompress(ULONG ul, unsigned cbitAverage, unsigned bitSize);

    void PutPSSize ( unsigned cPrefix, unsigned cSuffix );

    void PutPid ( PROPID pid );

    const LONGLONG  _sigKeyComp;     //
    CPhysIndex & _phIndex;          // The Physical Index for the bitstream

    CWBitStream  _bitStream;        // must be created first

    BOOL         _fUseLinks;
    CPBitStream  _bitStreamLink;

    BitOffset    _bitOffCurKey;     // These two bitOffsets are used to rewind
    BitOffset    _bitOffLastKey;    // the compressor to the previous key.

    BOOL         _fWriteFirstKeyFull;   // Write first key on each page fully
};

//+---------------------------------------------------------------------------
//
//  Class:      CKeyDeComp (kdcomp)
//
//  Purpose:    Persistent key de-compressor
//
//  Interface:
//
//  History:    09-Jul-91   KyleP       Created.
//              22-Nov-93   w-PatG      Converted from CPersDeComp
//
//  Notes:      One of the key requirements for using de-compressors is
//              that if an operation fails because the end of page was
//              reached, that *same* operation must be the first one
//              performed on the following compressor.
//
//----------------------------------------------------------------------------
const LONGLONG eSigKeyDeComp = 0x504d4f434459454bi64;   // "KEYDCOMP"

class CKeyDeComp: public CCoder, public CKeyCursor
{
public:

    CKeyDeComp( PDirectory& pDir,
                INDEXID iid,
                CPhysIndex& phIndex,
                WORKID widMax,
                BOOL fUseLinks  = TRUE,
                BOOL fUseDir = TRUE );

    CKeyDeComp( PDirectory& pDir,
                INDEXID iid,
                CPhysIndex& phIndex,
                BitOffset& posKey,
                const CKeyBuf& keyInit,
                const CKey* pKey,
                WORKID widMax,
                BOOL fUseLinks = TRUE,
                BOOL fUseDir = TRUE );

    CKeyDeComp( CKeyDeComp & decomp );

    ~CKeyDeComp();

    const CKeyBuf * GetKey();

    const CKeyBuf * GetNextKey();

    virtual const CKeyBuf * GetNextKey( BitOffset * pBitOff );

    virtual const CKeyBuf * GetKey( BitOffset * pBitOff );

    PROPID      Pid();

    void        GetOffset( BitOffset & bitOff );

    void         FreeStream() { _bitStream.FreeStream(); }

    void         RefillStream() { _bitStream.RefillStream(); }

    //
    // Unused.  Required for inheritance.
    //

    OCCURRENCE  Occurrence();

    OCCURRENCE  NextOccurrence();

    ULONG       OccurrenceCount();

    OCCURRENCE  MaxOccurrence();

    WORKID      WorkId();

    WORKID      NextWorkId();

    ULONG       HitCount();

#if DEVL == 1
    void Dump();
#endif

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

protected:

    const CKeyBuf* SeekKey(const CKey* pKey);

    ULONG       BitUnCompress(unsigned cbitAverage);

    ULONG       IBitUnCompress(unsigned cbitAverage, ULONG ulPartial);

    void        LoadKey();

    void        LoadPSSize ( unsigned& cPrefix, unsigned& cSuffix );

    void        LoadPid ();

    BOOL        IsAtSentinel() const { return _fAtSentinel; }

    const LONGLONG  _sigKeyDeComp;

    CRBitStream  _bitStream;

    BOOL         _fUseLinks;

    BitOffset    _bitOffNextKey;

    PDirectory&  _pDir;

    BOOL         _fUseDir;
    BOOL         _fAtSentinel;      // TRUE if at sentinel (end) key.

    CPhysIndex & _physIndex;

#if (CIDBG == 1)
    BOOL         _fLastKeyFromDir;  // TRUE if last key looked up in directory
#else
    BOOL         _fDummy;           // to keep chk/free size the same
#endif
};

//+---------------------------------------------------------------------------
//
//  Class:      CPersComp (pcomp)
//
//  Purpose:    Persistent index compressor
//
//  Interface:
//
//  History:    03-Jul-91   KyleP       Created.
//              02-Dec-93   w-PatG      Altered to use CKeyComp.
//
//----------------------------------------------------------------------------

const LONGLONG eSigPersComp = 0x504d4f4353524550i64;    // "PERSCOMP"

class CPersComp: public CKeyComp
{
public:
    CPersComp(CPhysIndex& phIndex, WORKID widMax);
    CPersComp(CPhysIndex& phIndex, WORKID widMax,
              const BitOffset & bitOffRestart,
              const BitOffset & bitoffSplitKey,
              const CKeyBuf & splitKey );

    ~CPersComp();

    void PutKey(const CKeyBuf * pkey,
                ULONG cWorkId,
                BitOffset & bitOffCurKey);

    void PutWorkId(WORKID wid, OCCURRENCE maxOcc, ULONG cOccurrence);

    inline void PutOccurrence(OCCURRENCE occ);

#if DEVL == 1
    void Dump();
#endif

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

    void FreeStream()
    {
        _bitStreamPatch.FreeStream();
        CKeyComp::FreeStream();
    }

protected:

    inline void BackSpace();

    void SetCWIDAccuracy();

    void PatchWidCount ();

    void PutWidCount ( ULONG cWorkId );

    void SkipWidCount ();

    void PutMaxOccurrence( OCCURRENCE maxOcc );

    const LONGLONG _sigPersComp;

    unsigned     _cWidActual;       // Used to decide if the originally

    unsigned     _cWidProposed;     // Original WorkId count. Used to
                                    //   allocate space for the count.

    CPBitStream  _bitStreamPatch;   // Used to back patch wid count
                                    // created after main _bitStream

#if DEVL == 1
    unsigned     _cOccLeft;         // ciVerify the specified number of
#endif

};

//+---------------------------------------------------------------------------
//----------------------------------------------------------------------------
inline void CPersComp::BackSpace()
{
   _bitStream.Seek(_bitOffCurKey);
   if ( _fUseLinks ) _bitStreamLink.Seek(_bitOffLastKey);

   ZeroToEndOfPage();

   _phIndex.SetUsedPagesCount( _bitOffCurKey.Page() + 1 );
   _cWidProposed = _cWidActual;
   _key.SetCount( 0 );
}

//+---------------------------------------------------------------------------
//
//  Class:      CPersDeComp (pdcomp)
//
//  Purpose:    Persistent index de-compressor
//
//  Interface:
//
//  History:    09-Jul-91   KyleP       Created.
//              02-Dec-93   w-PatG      Altered to use CKeyComp.
//
//  Notes:      One of the key requirements for using de-compressors is
//              that if an operation fails because the end of page was
//              reached, that *same* operation must be the first one
//              performed on the following compressor.
//
//
//----------------------------------------------------------------------------

const LONGLONG eSigPersDeComp = 0x504d434453524550i64; // "PERSDCMP"

class CPersDeComp: public CKeyDeComp
{
public:

    CPersDeComp(
        PDirectory& pDir,
        INDEXID iid,
        CPhysIndex& phIndex,
        WORKID widMax,
        BOOL fUseLinks = TRUE,
        BOOL fUseDir = TRUE );

    CPersDeComp(
        PDirectory& pDir,
        INDEXID iid,
        CPhysIndex& phIndex,
        BitOffset& posKey,
        const CKeyBuf& keyInit,
        const CKey* pKey,
        WORKID widMax,
        BOOL fUseLinks = TRUE,
        BOOL fUseDir = TRUE);

    CPersDeComp(CPersDeComp& orig);

    ~CPersDeComp();

    const CKeyBuf * GetNextKey();

    const CKeyBuf * GetNextKey( BitOffset * pbitOff );

    WORKID       WorkId();

    WORKID       NextWorkId();

    ULONG        WorkIdCount();

    OCCURRENCE   Occurrence();

    OCCURRENCE   NextOccurrence();

    OCCURRENCE   MaxOccurrence()         { return _maxOcc; }

    ULONG        OccurrenceCount();

    ULONG        HitCount() {
        return OccurrenceCount();
    }

    void        RatioFinished ( ULONG& denom, ULONG& num );

#if DEVL == 1
    void Dump();
#endif

#ifdef CIEXTMODE
    void        CiExtDump(void *ciExtSelf);
#endif

private:

    void  LoadKey();

    void        FinishKeyLoad();

    void        LoadWorkId();

    void        LoadOccurrence();

    void        LoadFirstOccurrence();

    void        LoadWidCount ();

    void        LoadMaxOccurrence();

    const LONGLONG  _sigPersDeComp;

    //
    // 'Current' state of the decompressor. These values are always
    // valid and are returned by the various Get functions.
    //

    ULONG           _cWid;
    ULONG           _cOcc;
    OCCURRENCE      _maxOcc;

    //
    // 'Transient' state. Helps the decompressor figure out what/where
    // its decompressing.
    //

    BOOL            _fcwidAccurate;
    ULONG           _cWidLeft;
    ULONG           _cOccLeft;
};

//+---------------------------------------------------------------------------
//
//  Member:     CPersComp::PutOccurrence, public
//
//  Synopsis:   Store an occurrence for the current WorkId.
//
//  Arguments:  [occ] -- The occurrence to store.
//
//  Modifies:   [occ] is added to the persistent index.
//
//  History:    08-Jul-91   KyleP       Created.
//
//----------------------------------------------------------------------------

__forceinline void CPersComp::PutOccurrence(OCCURRENCE occ)
{
    ciAssert(occ != OCC_INVALID);
    ciAssert(occ > _occ);
    ciAssert(_cOccLeft > 0);

    ciDebugOut (( DEB_PCOMP | DEB_NOCOMPNAME,"%d ", occ ));

    BitCompress(occ - _occ, OccDeltaBits);

    _occ = occ;

#if CIDBG == 1
    _cOccLeft--;
#endif

}

//+---------------------------------------------------------------------------
//
//  Member:     CKeyComp::BitCompress, private
//
//  Synopsis:   Compress and store a number.
//
//  Arguments:  [ul] -- Number to store.
//
//              [cbitAverage] -- Minimum number of bits to store.
//
//  Algorithm:  First, store the bottom cbitAverage bits.
//              while there are more bits to store
//                  store a 1 bit indicating more to follow
//                  store the next n bits, where n = 2, 3, 4, ...
//              store a 0 bit indicating end of sequence
//
//  History:    08-Jul-91   KyleP       Created.
//              06-Dec-93   w-PatG      Moved from CPersComp.
//
//----------------------------------------------------------------------------

__forceinline void CKeyComp::BitCompress(ULONG ul, unsigned cbitAverage)
{
    ciAssert(cbitAverage > 0);
    ciAssert(cbitAverage <= ULONG_BITS );

    unsigned  bitSize = Log2(ul);

    //
    // If bitSize < cbitAverage then rightshift
    // to make it cbitAverage and store cbitAverage bits
    // plus the signal bit (0 = done, 1 = more)
    //

    if (bitSize <= cbitAverage)
    {
        //
        // Store cbitAverage bits of ul
        // plus a trailing 0 bit for end-of-sequence
        // cbitAverage == ULONG_BITS (maximum!) is a special case,
        // no trailing bit is used.

        if (cbitAverage < ULONG_BITS)
            _bitStream.PutBits(ul << 1, cbitAverage + 1);
        else
        {
            ciAssert(cbitAverage == ULONG_BITS);
            _bitStream.PutBits(ul, ULONG_BITS);
        }
    }
    else
        IBitCompress(ul, cbitAverage, bitSize);
}

__forceinline PROPID CKeyDeComp::Pid()
{
    return GetKey()?
        _key.Pid():
        pidInvalid;
}