/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    mrcf.c

Abstract:

    This module implements the Mrcf compression engine.

Author:

    Gary Kimura     [GaryKi]    21-Jan-1994

Revision History:

--*/

#include "ntrtlp.h"

#include <stdio.h>


//
//  To decompress/compress a block of data the user needs to
//  provide a work space as an extra parameter to all the exported
//  procedures.  That way the routines will not need to use excessive
//  stack space and will still be multithread safe
//

//
//  Variables for reading and writing bits
//

typedef struct _MRCF_BIT_IO {

    USHORT  abitsBB;        //  16-bit buffer being read
    LONG    cbitsBB;        //  Number of bits left in abitsBB

    PUCHAR  pbBB;           //  Pointer to byte stream being read
    ULONG   cbBB;           //  Number of bytes left in pbBB
    ULONG   cbBBInitial;    //  Initial size of pbBB

} MRCF_BIT_IO;
typedef MRCF_BIT_IO *PMRCF_BIT_IO;

//
//  Maximum back-pointer value, also used to indicate end of compressed stream!
//

#define wBACKPOINTERMAX                  (4415)

//
//  MDSIGNATURE - Signature at start of each compressed block
//
//      This 4-byte signature is used as a check to ensure that we
//      are decompressing data we compressed, and also to indicate
//      which compression method was used.
//
//      NOTE: A compressed block consists of one or more "chunks", separated
//            by the bitsEND_OF_STREAM pattern.
//
//            Byte          Word
//        -----------    ---------
//         0  1  2  3      0    1      Meaning
//        -- -- -- --    ---- ----     ----------------
//        44 53 00 01    5344 0100     MaxCompression
//        44 53 00 02    5344 0200     StandardCompression
//
//      NOTE: The *WORD* values are listed to be clear about the
//            byte ordering!
//

typedef struct _MDSIGNATURE {

    //
    //  Must be MD_STAMP
    //

    USHORT sigStamp;

    //
    //  mdsSTANDARD or mdsMAX
    //

    USHORT sigType;

} MDSIGNATURE;
typedef MDSIGNATURE *PMDSIGNATURE;

#define MD_STAMP        0x5344  // Signature stamp at start of compressed blk
#define MASK_VALID_mds  0x0300  // All other bits must be zero


//
//  Local procedure declarations and macros
//

#define minimum(a,b) (a < b ? a : b)

//
//  Local procedure prototypes
//

VOID
MrcfSetBitBuffer (
    PUCHAR pb,
    ULONG cb,
    PMRCF_BIT_IO BitIo
    );

VOID
MrcfFillBitBuffer (
    PMRCF_BIT_IO BitIo
    );

USHORT
MrcfReadBit (
    PMRCF_BIT_IO BitIo
    );

USHORT
MrcfReadNBits (
    LONG cbits,
    PMRCF_BIT_IO BitIo
    );


NTSTATUS
RtlDecompressBufferMrcf (
    OUT PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    OUT PULONG FinalUncompressedSize
    )

/*++

Routine Description:

    This routine decompresses a buffer of StandardCompressed or MaxCompressed
    data.

Arguments:

    UncompressedBuffer - buffer to receive uncompressed data

    UncompressedBufferSize - length of UncompressedBuffer

          NOTE: UncompressedBufferSize must be the EXACT length of the uncompressed
                data, as Decompress uses this information to detect
                when decompression is complete.  If this value is
                incorrect, Decompress may crash!

    CompressedBuffer - buffer containing compressed data

    CompressedBufferSize - length of CompressedBuffer

    WorkSpace - pointer to a private work area for use by this operation

Return Value:

    ULONG - Returns the size of the decompressed data in bytes. Returns 0 if
        there was an error in the decompress.

--*/

{
    MRCF_BIT_IO WorkSpace;

    ULONG  cbMatch; //  Length of match string
    ULONG  i;       //  Index in UncompressedBuffer to receive decoded data
    ULONG  iMatch;  //  Index in UncompressedBuffer of matched string
    ULONG  k;       //  Number of bits in length string
    ULONG  off;     //  Offset from i in UncompressedBuffer of match string
    USHORT x;       //  Current bit being examined
    ULONG  y;

    //
    //  verify that compressed data starts with proper signature
    //

    if (CompressedBufferSize < sizeof(MDSIGNATURE) ||                            // Must have signature
        ((PMDSIGNATURE)CompressedBuffer)->sigStamp != MD_STAMP ||            // Stamp must be OK
        ((PMDSIGNATURE)CompressedBuffer)->sigType & (~MASK_VALID_mds)) {     // Type must be OK

        *FinalUncompressedSize = 0;
        return STATUS_BAD_COMPRESSION_BUFFER;
    }

    //
    //  Skip over the valid signature
    //

    CompressedBufferSize -= sizeof(MDSIGNATURE);
    CompressedBuffer += sizeof(MDSIGNATURE);

    //
    //  Set up for decompress, start filling UncompressedBuffer at front
    //

    i = 0;

    //
    //  Set statics to save parm passing
    //

    MrcfSetBitBuffer(CompressedBuffer,CompressedBufferSize,&WorkSpace);

    while (TRUE) {

        y = MrcfReadNBits(2,&WorkSpace);

        //
        //  Check if next 7 bits are a byte
        //  1 if 128..255 (0x80..0xff), 2 if 0..127 (0x00..0x7f)
        //

        if (y == 1 || y == 2) {

            ASSERTMSG("Don't exceed expected length ", i<UncompressedBufferSize);

            UncompressedBuffer[i] = (UCHAR)((y == 1 ? 0x80 : 0) | MrcfReadNBits(7,&WorkSpace));

            i++;

        } else {

            //
            //  Have match sequence
            //

            //
            // Get the offset
            //

            if (y == 0) {

                //
                //  next 6 bits are offset
                //

                off = MrcfReadNBits(6,&WorkSpace);

                ASSERTMSG("offset 0 is invalid ", off != 0);

            } else {

                x = MrcfReadBit(&WorkSpace);

                if (x == 0) {

                    //
                    //  next 8 bits are offset-64 (0x40)
                    //

                    off = MrcfReadNBits(8, &WorkSpace) + 64;

                } else {

                    //
                    //  next 12 bits are offset-320 (0x140)
                    //

                    off = MrcfReadNBits(12, &WorkSpace) + 320;

                    if (off == wBACKPOINTERMAX) {

                        //
                        //  EOS marker
                        //

                        if (i >= UncompressedBufferSize) {

                            //
                            // Done with entire buffer
                            //

                            *FinalUncompressedSize = i;
                            return STATUS_SUCCESS;

                        } else {

                            //
                            //  More to do
                            //  Done with a 512-byte chunk
                            //

                            continue;
                        }
                    }
                }
            }

            ASSERTMSG("Don't exceed expected length ", i<UncompressedBufferSize);
            ASSERTMSG("Cannot match before start of uncoded buffer! ", off <= i);

            //
            //  Get the length  - logarithmically encoded
            //

            for (k=0; (x=MrcfReadBit(&WorkSpace)) == 0; k++) { NOTHING; }

            ASSERT(k <= 8);

            if (k == 0) {

                //
                //  All matches at least 2 chars long
                //

                cbMatch = 2;

            } else {

                cbMatch = (1 << k) + 1 + MrcfReadNBits(k, &WorkSpace);
            }

            ASSERTMSG("Don't exceed buffer size ", (i - off + cbMatch - 1) <= UncompressedBufferSize);

            //
            //  Copy the matched string
            //

            iMatch = i - off;

            while ( (cbMatch > 0) && (i<UncompressedBufferSize) ) {

                UncompressedBuffer[i++] = UncompressedBuffer[iMatch++];
                cbMatch--;
            }

            ASSERTMSG("Should have copied it all ", cbMatch == 0);
        }
    }
}


//
//  Internal Support Routine
//

VOID
MrcfSetBitBuffer (
    PUCHAR pb,
    ULONG cb,
    PMRCF_BIT_IO BitIo
    )

/*++

Routine Description:

    Set statics with coded buffer pointer and length

Arguments:

    pb - pointer to compressed data buffer

    cb - length of compressed data buffer

    BitIo - Supplies a pointer to the bit buffer statics

Return Value:

    None.

--*/

{
    BitIo->pbBB        = pb;
    BitIo->cbBB        = cb;
    BitIo->cbBBInitial = cb;
    BitIo->cbitsBB     = 0;
    BitIo->abitsBB     = 0;
}


//
//  Internal Support Routine
//

USHORT
MrcfReadBit (
    PMRCF_BIT_IO BitIo
    )

/*++

Routine Description:

    Get next bit from bit buffer

Arguments:

    BitIo - Supplies a pointer to the bit buffer statics

Return Value:

    USHORT - Returns next bit (0 or 1)

--*/

{
    USHORT bit;

    //
    //  Check if no bits available
    //

    if ((BitIo->cbitsBB) == 0) {

        MrcfFillBitBuffer(BitIo);
    }

    //
    //  Decrement the bit count
    //  get the bit, remove it, and return the bit
    //

    (BitIo->cbitsBB)--;
    bit = (BitIo->abitsBB) & 1;
    (BitIo->abitsBB) >>= 1;

    return bit;
}


//
//  Internal Support Routine
//

USHORT
MrcfReadNBits (
    LONG cbits,
    PMRCF_BIT_IO BitIo
    )

/*++

Routine Description:

    Get next N bits from bit buffer

Arguments:

    cbits - count of bits to get

    BitIo - Supplies a pointer to the bit buffer statics

Return Value:

    USHORT - Returns next cbits bits.

--*/

{
    ULONG abits;        // Bits to return
    LONG cbitsPart;    // Partial count of bits
    ULONG cshift;       // Shift count
    ULONG mask;         // Mask

    //
    //  Largest number of bits we should read at one time is 12 bits for
    //  a 12-bit offset.  The largest length field component that we
    //  read is 8 bits.  If this routine were used for some other purpose,
    //  it can support up to 15 (NOT 16) bit reads, due to how the masking
    //  code works.
    //

    ASSERT(cbits <= 12);

    //
    //  No shift and no bits yet
    //

    cshift = 0;
    abits = 0;

    while (cbits > 0) {

        //
        //  If not bits available get some bits
        //

        if ((BitIo->cbitsBB) == 0) {

            MrcfFillBitBuffer(BitIo);
        }

        //
        //  Number of bits we can read
        //

        cbitsPart = minimum((BitIo->cbitsBB), cbits);

        //
        //  Mask for bits we want, extract and store them
        //

        mask = (1 << cbitsPart) - 1;
        abits |= ((BitIo->abitsBB) & mask) << cshift;

        //
        //  Remember the next chunk of bits
        //

        cshift = cbitsPart;

        //
        //  Update bit buffer, move remaining bits down and
        //  update count of bits left
        //

        (BitIo->abitsBB) >>= cbitsPart;
        (BitIo->cbitsBB) -= cbitsPart;

        //
        //  Update count of bits left to read
        //

        cbits -= cbitsPart;
    }

    //
    //  Return requested bits
    //

    return (USHORT)abits;
}


//
//  Internal Support Routine
//

VOID
MrcfFillBitBuffer (
    PMRCF_BIT_IO BitIo
    )

/*++

Routine Description:

    Fill abitsBB from static bit buffer

Arguments:

    BitIo - Supplies a pointer to the bit buffer statics

Return Value:

    None.

--*/

{
    ASSERT((BitIo->cbitsBB) == 0);

    switch (BitIo->cbBB) {

    case 0:

        ASSERTMSG("no bits left in coded buffer!", FALSE);

        break;

    case 1:

        //
        //  Get last byte and adjust count
        //

        BitIo->cbitsBB = 8;
        BitIo->abitsBB = *(BitIo->pbBB)++;
        BitIo->cbBB--;

        break;

    default:

        //
        //  Get word and adjust count
        //

        BitIo->cbitsBB = 16;
        BitIo->abitsBB = *((USHORT *)(BitIo->pbBB))++;
        BitIo->cbBB -= 2;

        break;
    }
}