mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1278 lines
36 KiB
1278 lines
36 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
|