/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    compress.c

Abstract:

    This module implements the NT Rtl compression engine.

Author:

    Gary Kimura     [GaryKi]    21-Jan-1994

Revision History:

--*/

#include "ntrtlp.h"


//
//  The following arrays hold procedures that we call to do the various
//  compression functions.  Each new compression function will need to
//  be added to this array.  For one that are currently not supported
//  we will fill in a not supported routine.
//

NTSTATUS
RtlCompressWorkSpaceSizeNS (
    IN USHORT CompressionEngine,
    OUT PULONG CompressBufferWorkSpaceSize,
    OUT PULONG CompressFragmentWorkSpaceSize
    );

NTSTATUS
RtlCompressBufferNS (
    IN USHORT CompressionEngine,
    IN PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    OUT PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG UncompressedChunkSize,
    OUT PULONG FinalCompressedSize,
    IN PVOID WorkSpace
    );

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

NTSTATUS
RtlDecompressFragmentNS (
    OUT PUCHAR UncompressedFragment,
    IN ULONG UncompressedFragmentSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG FragmentOffset,
    OUT PULONG FinalUncompressedSize,
    IN PVOID WorkSpace
    );

NTSTATUS
RtlDescribeChunkNS (
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    OUT PULONG ChunkSize
    );

NTSTATUS
RtlReserveChunkNS (
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    IN ULONG ChunkSize
    );

#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma const_seg("PAGELKCONST")
#endif

//
//  Routines to query the amount of memory needed for each workspace
//

const PRTL_COMPRESS_WORKSPACE_SIZE RtlWorkSpaceProcs[8] = {
    NULL,                          // 0
    NULL,                          // 1
    RtlCompressWorkSpaceSizeLZNT1, // 2
    RtlCompressWorkSpaceSizeNS,    // 3
    RtlCompressWorkSpaceSizeNS,    // 4
    RtlCompressWorkSpaceSizeNS,    // 5
    RtlCompressWorkSpaceSizeNS,    // 6
    RtlCompressWorkSpaceSizeNS     // 7
};

//
//  Routines to compress a buffer
//

const PRTL_COMPRESS_BUFFER RtlCompressBufferProcs[8] = {
    NULL,                   // 0
    NULL,                   // 1
    RtlCompressBufferLZNT1, // 2
    RtlCompressBufferNS,    // 3
    RtlCompressBufferNS,    // 4
    RtlCompressBufferNS,    // 5
    RtlCompressBufferNS,    // 6
    RtlCompressBufferNS     // 7
};

#if defined(ALLOC_DATA_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
#pragma const_seg("PAGECONST")
#endif

//
//  Routines to decompress a buffer
//

const PRTL_DECOMPRESS_BUFFER RtlDecompressBufferProcs[8] = {
    NULL,                     // 0
    NULL,                     // 1
    RtlDecompressBufferLZNT1, // 2
    RtlDecompressBufferNS,    // 3
    RtlDecompressBufferNS,    // 4
    RtlDecompressBufferNS,    // 5
    RtlDecompressBufferNS,    // 6
    RtlDecompressBufferNS     // 7
};

//
//  Routines to decompress a fragment
//

const PRTL_DECOMPRESS_FRAGMENT RtlDecompressFragmentProcs[8] = {
    NULL,                       // 0
    NULL,                       // 1
    RtlDecompressFragmentLZNT1, // 2
    RtlDecompressFragmentNS,    // 3
    RtlDecompressFragmentNS,    // 4
    RtlDecompressFragmentNS,    // 5
    RtlDecompressFragmentNS,    // 6
    RtlDecompressFragmentNS     // 7
};

//
//  Routines to describe the current chunk
//

const PRTL_DESCRIBE_CHUNK RtlDescribeChunkProcs[8] = {
    NULL,                  // 0
    NULL,                  // 1
    RtlDescribeChunkLZNT1, // 2
    RtlDescribeChunkNS,    // 3
    RtlDescribeChunkNS,    // 4
    RtlDescribeChunkNS,    // 5
    RtlDescribeChunkNS,    // 6
    RtlDescribeChunkNS     // 7
};

//
//  Routines to reserve for a chunk
//

const PRTL_RESERVE_CHUNK RtlReserveChunkProcs[8] = {
    NULL,                 // 0
    NULL,                 // 1
    RtlReserveChunkLZNT1, // 2
    RtlReserveChunkNS,    // 3
    RtlReserveChunkNS,    // 4
    RtlReserveChunkNS,    // 5
    RtlReserveChunkNS,    // 6
    RtlReserveChunkNS     // 7
};

#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)

//
// N.B. The two functions below are placed in the PAGELK section
//      because they need to be locked down in memory during Hibernation,
//      since they are used to enable compression of the Hiberfile.
//

#pragma alloc_text(PAGELK, RtlGetCompressionWorkSpaceSize)
#pragma alloc_text(PAGELK, RtlCompressBuffer)

#pragma alloc_text(PAGE, RtlDecompressChunks)
#pragma alloc_text(PAGE, RtlCompressChunks)
#pragma alloc_text(PAGE, RtlDecompressBuffer)
#pragma alloc_text(PAGE, RtlDecompressFragment)
#pragma alloc_text(PAGE, RtlDescribeChunk)
#pragma alloc_text(PAGE, RtlReserveChunk)
#pragma alloc_text(PAGE, RtlCompressWorkSpaceSizeNS)
#pragma alloc_text(PAGE, RtlCompressBufferNS)
#pragma alloc_text(PAGE, RtlDecompressBufferNS)
#pragma alloc_text(PAGE, RtlDecompressFragmentNS)
#pragma alloc_text(PAGE, RtlDescribeChunkNS)
#pragma alloc_text(PAGE, RtlReserveChunkNS)
#endif


NTSTATUS
RtlGetCompressionWorkSpaceSize (
    IN USHORT CompressionFormatAndEngine,
    OUT PULONG CompressBufferWorkSpaceSize,
    OUT PULONG CompressFragmentWorkSpaceSize
    )


/*++

Routine Description:

    This routine returns to the caller the size in bytes of the
    different work space buffers need to perform the compression

Arguments:

    CompressionFormatAndEngine - Supplies the format and engine
        specification for the compressed data.

    CompressBufferWorkSpaceSize - Receives the size in bytes needed
        to compress a buffer.

    CompressBufferWorkSpaceSize - Receives the size in bytes needed
        to decompress a fragment.

Return Value:

    STATUS_SUCCESS - the operation worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    //
    //  Declare two variables to hold the format and engine specification
    //

    USHORT Format = CompressionFormatAndEngine & 0x00ff;
    USHORT Engine = CompressionFormatAndEngine & 0xff00;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the routine to return the workspace sizes.
    //

    return RtlWorkSpaceProcs[ Format ]( Engine,
                                        CompressBufferWorkSpaceSize,
                                        CompressFragmentWorkSpaceSize );
}


NTSTATUS
RtlCompressBuffer (
    IN USHORT CompressionFormatAndEngine,
    IN PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    OUT PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG UncompressedChunkSize,
    OUT PULONG FinalCompressedSize,
    IN PVOID WorkSpace
    )

/*++

Routine Description:

    This routine takes as input an uncompressed buffer and produces
    its compressed equivalent provided the compressed data fits within
    the specified destination buffer.

    An output variable indicates the number of bytes used to store
    the compressed buffer.

Arguments:

    CompressionFormatAndEngine - Supplies the format and engine
        specification for the compressed data.

    UncompressedBuffer - Supplies a pointer to the uncompressed data.

    UncompressedBufferSize - Supplies the size, in bytes, of the
        uncompressed buffer.

    CompressedBuffer - Supplies a pointer to where the compressed data
        is to be stored.

    CompressedBufferSize - Supplies the size, in bytes, of the
        compressed buffer.

    UncompressedChunkSize - Supplies the chunk size to use when
        compressing the input buffer.  The only valid values are
        512, 1024, 2048, and 4096.

    FinalCompressedSize - Receives the number of bytes needed in
        the compressed buffer to store the compressed data.

    WorkSpace - Mind your own business, just give it to me.

Return Value:

    STATUS_SUCCESS - the compression worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BUFFER_ALL_ZEROS - the compression worked without a hitch and in
        addition the input buffer was all zeros.

    STATUS_BUFFER_TOO_SMALL - the compressed buffer is too small to hold the
        compressed data.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    //
    //  Declare two variables to hold the format and engine specification
    //

    USHORT Format = CompressionFormatAndEngine & 0x00ff;
    USHORT Engine = CompressionFormatAndEngine & 0xff00;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the compression routine for the individual format
    //

    return RtlCompressBufferProcs[ Format ]( Engine,
                                             UncompressedBuffer,
                                             UncompressedBufferSize,
                                             CompressedBuffer,
                                             CompressedBufferSize,
                                             UncompressedChunkSize,
                                             FinalCompressedSize,
                                             WorkSpace );
}


NTSTATUS
RtlDecompressBuffer (
    IN USHORT CompressionFormat,
    OUT PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    OUT PULONG FinalUncompressedSize
    )

/*++

Routine Description:

    This routine takes as input a compressed buffer and produces
    its uncompressed equivalent provided the uncompressed data fits
    within the specified destination buffer.

    An output variable indicates the number of bytes used to store the
    uncompressed data.

Arguments:

    CompressionFormat - Supplies the format of the compressed data.

    UncompressedBuffer - Supplies a pointer to where the uncompressed
        data is to be stored.

    UncompressedBufferSize - Supplies the size, in bytes, of the
        uncompressed buffer.

    CompressedBuffer - Supplies a pointer to the compressed data.

    CompressedBufferSize - Supplies the size, in bytes, of the
        compressed buffer.

    FinalUncompressedSize - Receives the number of bytes needed in
        the uncompressed buffer to store the uncompressed data.

Return Value:

    STATUS_SUCCESS - the decompression worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
        ill-formed.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    //
    //  Declare two variables to hold the format specification
    //

    USHORT Format = CompressionFormat & 0x00ff;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the compression routine for the individual format
    //

    return RtlDecompressBufferProcs[ Format ]( UncompressedBuffer,
                                               UncompressedBufferSize,
                                               CompressedBuffer,
                                               CompressedBufferSize,
                                               FinalUncompressedSize );
}


NTSTATUS
RtlDecompressFragment (
    IN USHORT CompressionFormat,
    OUT PUCHAR UncompressedFragment,
    IN ULONG UncompressedFragmentSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG FragmentOffset,
    OUT PULONG FinalUncompressedSize,
    IN PVOID WorkSpace
    )

/*++

Routine Description:

    This routine takes as input a compressed buffer and extract an
    uncompressed fragment.

    Output bytes are copied to the fragment buffer until either the
    fragment buffer is full or the end of the uncompressed buffer is
    reached.

    An output variable indicates the number of bytes used to store the
    uncompressed fragment.

Arguments:

    CompressionFormat - Supplies the format of the compressed data.

    UncompressedFragment - Supplies a pointer to where the uncompressed
        fragment is to be stored.

    UncompressedFragmentSize - Supplies the size, in bytes, of the
        uncompressed fragment buffer.

    CompressedBuffer - Supplies a pointer to the compressed data buffer.

    CompressedBufferSize - Supplies the size, in bytes, of the
        compressed buffer.

    FragmentOffset - Supplies the offset (zero based) where the uncompressed
        fragment is being extract from.  The offset is the position within
        the original uncompressed buffer.

    FinalUncompressedSize - Receives the number of bytes needed in
        the Uncompressed fragment buffer to store the data.

Return Value:

    STATUS_SUCCESS - the operation worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
        ill-formed.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    //
    //  Declare two variables to hold the format specification
    //

    USHORT Format = CompressionFormat & 0x00ff;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the compression routine for the individual format
    //

    return RtlDecompressFragmentProcs[ Format ]( UncompressedFragment,
                                                 UncompressedFragmentSize,
                                                 CompressedBuffer,
                                                 CompressedBufferSize,
                                                 FragmentOffset,
                                                 FinalUncompressedSize,
                                                 WorkSpace );
}


NTSYSAPI
NTSTATUS
NTAPI
RtlDescribeChunk (
    IN USHORT CompressionFormat,
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    OUT PULONG ChunkSize
    )

/*++

Routine Description:

    This routine takes as input a compressed buffer, and returns
    a description of the current chunk in that buffer, updating
    the CompressedBuffer pointer to point to the next chunk (if
    there is one).

Arguments:

    CompressionFormat - Supplies the format of the compressed data.

    CompressedBuffer - Supplies a pointer to the current chunk in
        the compressed data, and returns pointing to the next chunk

    EndOfCompressedBufferPlus1 - Points at first byte beyond
        compressed buffer

    ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
        is nonzero, else undefined

    ChunkSize - Receives the size of the current chunk pointed
        to by CompressedBuffer.  Returns 0 if STATUS_NO_MORE_ENTRIES.

Return Value:

    STATUS_SUCCESS - the decompression worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
        ill-formed.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

    STATUS_NO_MORE_ENTRIES - There is no chunk at the current pointer.

--*/

{
    //
    //  Declare two variables to hold the format specification
    //

    USHORT Format = CompressionFormat & 0x00ff;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the compression routine for the individual format
    //

    return RtlDescribeChunkProcs[ Format ]( CompressedBuffer,
                                            EndOfCompressedBufferPlus1,
                                            ChunkBuffer,
                                            ChunkSize );
}


NTSYSAPI
NTSTATUS
NTAPI
RtlReserveChunk (
    IN USHORT CompressionFormat,
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    IN ULONG ChunkSize
    )

/*++

Routine Description:

    This routine takes as input a compressed buffer, and reserves
    space for a chunk of the specified size - filling in any pattern
    as is necessary for a chunk of that size.  On return it has
    updated the CompressedBuffer pointer to point to the next chunk (if
    there is one).

Arguments:

    CompressionFormat - Supplies the format of the compressed data.

    CompressedBuffer - Supplies a pointer to the current chunk in
        the compressed data, and returns pointing to the next chunk

    EndOfCompressedBufferPlus1 - Points at first byte beyond
        compressed buffer

    ChunkBuffer - Receives a pointer to the chunk, if ChunkSize
        is nonzero, else undefined

    ChunkSize - Supplies the compressed size of the chunk to be received.
                Two special values are 0, and whatever the maximum
                uncompressed chunk size is for the routine.  0 means
                the chunk should be filled with a pattern that equates
                to all 0's.  The maximum chunk size implies that the
                compression routine should prepare to receive all of the
                data in uncompressed form.

Return Value:

    STATUS_SUCCESS - the decompression worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
        ill-formed.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    //
    //  Declare two variables to hold the format specification
    //

    USHORT Format = CompressionFormat & 0x00ff;

    //
    //  make sure the format is sort of supported
    //

    if ((Format == COMPRESSION_FORMAT_NONE) || (Format == COMPRESSION_FORMAT_DEFAULT)) {

        return STATUS_INVALID_PARAMETER;
    }

    if (Format & 0x00f0) {

        return STATUS_UNSUPPORTED_COMPRESSION;
    }

    //
    //  Call the compression routine for the individual format
    //

    return RtlReserveChunkProcs[ Format ]( CompressedBuffer,
                                           EndOfCompressedBufferPlus1,
                                           ChunkBuffer,
                                           ChunkSize );
}


NTSTATUS
RtlDecompressChunks (
    OUT PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN PUCHAR CompressedTail,
    IN ULONG CompressedTailSize,
    IN PCOMPRESSED_DATA_INFO CompressedDataInfo
    )

/*++

Routine Description:

    This routine takes as input a compressed buffer which is a stream
    of chunks and decompresses it into the specified destination buffer.
    The compressed data may be in two pieces, such that the "tail" of
    the buffer is top aligned in the buffer at CompressedTail, and the
    rest of the data is top aligned in the CompressedBuffer.  The
    CompressedBuffer can overlap and be top-aligned in the UncompressedBuffer,
    to allow something close to in-place decompression.  The CompressedTail
    must be large enough to completely contain the final chunk and it
    chunk header.

Arguments:

    UncompressedBuffer - Supplies a pointer to where the uncompressed
        data is to be stored.

    UncompressedBufferSize - Supplies the size, in bytes, of the
        uncompressed buffer.

    CompressedBuffer - Supplies a pointer to the compressed data, part 1.

    CompressedBufferSize - Supplies the size, in bytes, of the
        compressed buffer.

    CompressedTail - Supplies a pointer to the compressed data, part 2,
        which must be the bytes immediately following the CompressedBuffer.

    CompressedTailSize - Supplies the size of the CompressedTail.

    CompressedDataInfo - Supplies a complete description of the
        compressed data with all the chunk sizes and compression
        parameters.

Return Value:

    STATUS_SUCCESS - the decompression worked without a hitch.

    STATUS_INVALID_PARAMETER - The specified format is illegal

    STATUS_BAD_COMPRESSION_BUFFER - the input compressed buffer is
        ill-formed.

    STATUS_UNSUPPORTED_COMPRESSION - the specified compression format and/or engine
        is not support.

--*/

{
    NTSTATUS Status;
    PULONG CurrentCompressedChunkSize;
    ULONG SizeToDecompress, FinalUncompressedSize;
    ULONG ChunksToGo = CompressedDataInfo->NumberOfChunks;
    ULONG UncompressedChunkSize = 1 << CompressedDataInfo->ChunkShift;

    CurrentCompressedChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];

    //
    //  Loop to decompress chunks.
    //

    do {

        //
        //  Calculate uncompressed size of next chunk to decompress.
        //

        SizeToDecompress = UncompressedBufferSize;
        if (SizeToDecompress >= UncompressedChunkSize) {
            SizeToDecompress = UncompressedChunkSize;
        }

        //
        //  If the next chunk is all zeros, then zero it.
        //

        if ((ChunksToGo == 0) || (*CurrentCompressedChunkSize == 0)) {

            RtlZeroMemory( UncompressedBuffer, SizeToDecompress );

            //
            //  Test for out of chunks here and set to 1, so we can
            //  unconditionally decrement below.  Also back up the 
            //  CompressedChunkSize pointer because we dereference
            //  it as well.
            //

            if (ChunksToGo == 0) {
                ChunksToGo = 1;
                CurrentCompressedChunkSize -= 1;
            }

        //
        //  If the next chunk is not compressed, just copy it.
        //

        } else if (*CurrentCompressedChunkSize == UncompressedChunkSize) {

            //
            //  Does this chunk extend beyond the end of the current
            //  buffer?  If so, that probably means we can move the
            //  first part of the chunk, and then switch to the Compressed
            //  tail to get the rest.
            //

            if (SizeToDecompress >= CompressedBufferSize) {

                //
                //  If we have already switched to the tail, then this must
                //  be badly formatted compressed data.
                //

                if ((CompressedTailSize == 0) && (SizeToDecompress > CompressedBufferSize)) {
                    return STATUS_BAD_COMPRESSION_BUFFER;
                }

                //
                //  Copy the first part, and then the second part from the tail.
                //  Then switch to make the tail the current buffer.
                //

                RtlCopyMemory( UncompressedBuffer, CompressedBuffer, CompressedBufferSize );
                RtlCopyMemory( UncompressedBuffer + CompressedBufferSize,
                               CompressedTail,
                               SizeToDecompress - CompressedBufferSize );

                //
                //  If we exhausted the first buffer, move into the tail, knowing
                //  that we adjust these pointers by *CurrentCompressedChunkSize
                //  below.
                //

                CompressedBuffer = CompressedTail - CompressedBufferSize;
                CompressedBufferSize = CompressedTailSize + CompressedBufferSize;
                CompressedTailSize = 0;

            //
            //  Otherwise we can just copy the whole chunk.
            //

            } else {
                RtlCopyMemory( UncompressedBuffer, CompressedBuffer, SizeToDecompress );
            }

        //
        //  Otherwise it is a normal chunk to decompress.
        //

        } else {

            //
            //  Does this chunk extend beyond the end of the current
            //  buffer?  If so, that probably means we can move the
            //  first part of the chunk, and then switch to the Compressed
            //  tail to get the rest.  Since the tail must be at least
            //  ChunkSize, the last chunk cannot be the one that is
            //  overlapping into the tail.  Therefore, it is safe for
            //  us to copy the chunk to decompress into the last chunk
            //  of the uncompressed buffer, and decompress it from there.
            //

            if (*CurrentCompressedChunkSize > CompressedBufferSize) {

                //
                //  If we have already switched to the tail, then this must
                //  be badly formatted compressed data.
                //

                if (CompressedTailSize == 0) {
                    return STATUS_BAD_COMPRESSION_BUFFER;
                }

                //
                //  Move the beginning of the chunk to the beginning of the last
                //  chunk in the uncompressed buffer.  This move could overlap.
                //

                RtlMoveMemory( UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize,
                               CompressedBuffer,
                               CompressedBufferSize );

                //
                //  Move the rest of the chunk from the tail.
                //

                RtlCopyMemory( UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize + CompressedBufferSize,
                               CompressedTail,
                               *CurrentCompressedChunkSize - CompressedBufferSize );

                //
                //  We temporarily set CompressedBuffer to describe where we
                //  copied the chunk to make the call in common code, then we
                //  switch it into the tail below.
                //

                CompressedBuffer = UncompressedBuffer + UncompressedBufferSize - UncompressedChunkSize;
            }

            //
            //  Attempt the decompress.
            //

            Status =
            RtlDecompressBuffer( CompressedDataInfo->CompressionFormatAndEngine,
                                 UncompressedBuffer,
                                 SizeToDecompress,
                                 CompressedBuffer,
                                 *CurrentCompressedChunkSize,
                                 &FinalUncompressedSize );

            if (!NT_SUCCESS(Status)) {
                return Status;
            }

            //
            //  If we did not get a full chunk, zero the rest.
            //

            if (SizeToDecompress > FinalUncompressedSize) {
                RtlZeroMemory( UncompressedBuffer + FinalUncompressedSize,
                               SizeToDecompress - FinalUncompressedSize );
            }

            //
            //  If we exhausted the first buffer, move into the tail, knowing
            //  that we adjust these pointers by *CurrentCompressedChunkSize
            //  below.
            //

            if (*CurrentCompressedChunkSize >= CompressedBufferSize) {
                CompressedBuffer = CompressedTail - CompressedBufferSize;
                CompressedBufferSize = CompressedTailSize + CompressedBufferSize;
                CompressedTailSize = 0;
            }
        }

        //
        //  Update for next possible pass through the loop.
        //

        UncompressedBuffer += SizeToDecompress;
        UncompressedBufferSize -= SizeToDecompress;
        CompressedBuffer += *CurrentCompressedChunkSize;
        CompressedBufferSize -= *CurrentCompressedChunkSize;
        CurrentCompressedChunkSize += 1;
        ChunksToGo -= 1;

    } while (UncompressedBufferSize != 0);

    return STATUS_SUCCESS;
}


NTSTATUS
RtlCompressChunks(
    IN PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    OUT PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN OUT PCOMPRESSED_DATA_INFO CompressedDataInfo,
    IN ULONG CompressedDataInfoLength,
    IN PVOID WorkSpace
    )

/*++

Routine Description:

    This routine takes as input an uncompressed buffer and produces
    its compressed equivalent provided the compressed data fits within
    the specified destination buffer.

    The desired compression parameters must be supplied via the
    CompressedDataInfo structure, and this structure then returns all
    of the compressed chunk sizes.

    Note that since any given chunk (or all chunks) can simply be
    transmitted uncompressed, all error possibilities are actually
    stopped in this routine, except for STATUS_BUFFER_TOO_SMALL.
    This code will be returned when the data is not compressing
    sufficiently to warrant sending the data compressed.  The caller
    must field this error, and send the data uncompressed.

Arguments:

    UncompressedBuffer - Supplies a pointer to the uncompressed data.

    UncompressedBufferSize - Supplies the size, in bytes, of the
        uncompressed buffer.

    CompressedBuffer - Supplies a pointer to where the compressed data
        is to be stored.

    CompressedBufferSize - Supplies the size, in bytes, of the
        compressed buffer.

    CompressedDataInfo - Supplies the compression parameters, such as
        CompressionFormat, CompressionUnitSize, ChunkSize and ClusterSize,
        returns all of the compressed chunk sizes.

    CompressedDataInfoLength - Size of the supplied CompressedDataInfo
        in bytes.

    WorkSpace - A workspace area of the correct size as returned from
        RtlGetCompressionWorkSpaceSize.

Return Value:

    STATUS_SUCCESS - the compression worked without a hitch.

    STATUS_BUFFER_TOO_SMALL - the data is not compressing sufficiently to
        warrant sending the data compressed.

--*/

{
    NTSTATUS Status;
    PULONG CurrentCompressedChunkSize;
    ULONG SizeToCompress, FinalCompressedSize;
    ULONG UncompressedChunkSize = 1 << CompressedDataInfo->ChunkShift;

    //
    //  Make sure CompressedDataInfo is long enough.
    //

    ASSERT(CompressedDataInfoLength >=
           (sizeof(COMPRESSED_DATA_INFO) +
            ((UncompressedBufferSize - 1) >> (CompressedDataInfo->ChunkShift - 2))));

    //
    //  For the worst case, the compressed buffer actually has to be
    //  the same size as the uncompressed buffer, minus 1/16th.  We then
    //  will actually use that size.  If the data is not compressing very
    //  well, it is cheaper for us to actually send the data to the
    //  server uncompressed, than poorly compressed, because if the
    //  data is poorly compressed, the server will end up doing an
    //  extra copy before trying to compress the data again anyway.
    //

    ASSERT(CompressedBufferSize >= (UncompressedBufferSize - (UncompressedBufferSize / 16)));
    CompressedBufferSize = (UncompressedBufferSize - (UncompressedBufferSize / 16));

    //
    //  Initialize NumberOfChunks returned and the pointer to the first chunk size.
    //

    CompressedDataInfo->NumberOfChunks = 0;
    CurrentCompressedChunkSize = &CompressedDataInfo->CompressedChunkSizes[0];

    //
    //  Loop to decompress chunks.
    //

    do {

        //
        //  Calculate uncompressed size of next chunk to decompress.
        //

        SizeToCompress = UncompressedBufferSize;
        if (SizeToCompress >= UncompressedChunkSize) {
            SizeToCompress = UncompressedChunkSize;
        }

        //
        //  Now compress the next chunk.
        //

        Status = RtlCompressBuffer( CompressedDataInfo->CompressionFormatAndEngine,
                                    UncompressedBuffer,
                                    SizeToCompress,
                                    CompressedBuffer,
                                    CompressedBufferSize,
                                    UncompressedChunkSize,
                                    &FinalCompressedSize,
                                    WorkSpace );

        //
        //  If the Buffer was all zeros, then we will not send anything.
        //

        if (Status == STATUS_BUFFER_ALL_ZEROS) {

            FinalCompressedSize = 0;

        //
        //  Otherwise, if there was any kind of error (we only expect the
        //  case where the data did not compress), then just copy the
        //  data and return UncompressedChunkSize for this one.
        //

        } else if (!NT_SUCCESS(Status)) {

            //
            //  The most likely error is STATUS_BUFFER_TOO_SMALL.
            //  But in any case, our only recourse would be to send
            //  the data uncompressed.  To be completely safe, we
            //  see if there is enough space for an uncompressed chunk
            //  in the CompressedBuffer, and if not we return
            //  buffer too small (which is probably what we had anyway!).
            //

            if (CompressedBufferSize < UncompressedChunkSize) {
                Status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            //
            //  Copy the uncompressed chunk.
            //

            RtlCopyMemory( CompressedBuffer, UncompressedBuffer, SizeToCompress );
            if (UncompressedChunkSize > SizeToCompress) {
                RtlZeroMemory( (PCHAR)CompressedBuffer + SizeToCompress,
                               UncompressedChunkSize - SizeToCompress );
            }

            FinalCompressedSize = UncompressedChunkSize;
        }

        ASSERT(FinalCompressedSize <= CompressedBufferSize);

        //
        //  At this point, we have handled any error status.
        //

        Status = STATUS_SUCCESS;

        //
        //  Store the final chunk size.
        //

        *CurrentCompressedChunkSize = FinalCompressedSize;
        CurrentCompressedChunkSize += 1;
        CompressedDataInfo->NumberOfChunks += 1;

        //
        //  Prepare for the next trip through the loop.
        //

        UncompressedBuffer += SizeToCompress;
        UncompressedBufferSize -= SizeToCompress;
        CompressedBuffer += FinalCompressedSize;
        CompressedBufferSize -= FinalCompressedSize;

    } while (UncompressedBufferSize != 0);

    return Status;
}


NTSTATUS
RtlCompressWorkSpaceSizeNS (
    IN USHORT CompressionEngine,
    OUT PULONG CompressBufferWorkSpaceSize,
    OUT PULONG CompressFragmentWorkSpaceSize
    )
{
    return STATUS_UNSUPPORTED_COMPRESSION;
}

NTSTATUS
RtlCompressBufferNS (
    IN USHORT CompressionEngine,
    IN PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    OUT PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG UncompressedChunkSize,
    OUT PULONG FinalCompressedSize,
    IN PVOID WorkSpace
    )
{
    return STATUS_UNSUPPORTED_COMPRESSION;
}

NTSTATUS
RtlDecompressBufferNS (
    OUT PUCHAR UncompressedBuffer,
    IN ULONG UncompressedBufferSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    OUT PULONG FinalUncompressedSize
    )
{
    return STATUS_UNSUPPORTED_COMPRESSION;
}

NTSTATUS
RtlDecompressFragmentNS (
    OUT PUCHAR UncompressedFragment,
    IN ULONG UncompressedFragmentSize,
    IN PUCHAR CompressedBuffer,
    IN ULONG CompressedBufferSize,
    IN ULONG FragmentOffset,
    OUT PULONG FinalUncompressedSize,
    IN PVOID WorkSpace
    )
{
    return STATUS_UNSUPPORTED_COMPRESSION;
}

NTSYSAPI
NTSTATUS
NTAPI
RtlDescribeChunkNS (
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    OUT PULONG ChunkSize
    )

{
    return STATUS_UNSUPPORTED_COMPRESSION;
}

NTSYSAPI
NTSTATUS
NTAPI
RtlReserveChunkNS (
    IN OUT PUCHAR *CompressedBuffer,
    IN PUCHAR EndOfCompressedBufferPlus1,
    OUT PUCHAR *ChunkBuffer,
    IN ULONG ChunkSize
    )

{
    return STATUS_UNSUPPORTED_COMPRESSION;
}