Windows2000/private/inet/urlmon/compress/gzip/inflate.c
2020-09-30 17:12:32 +02:00

278 lines
7.3 KiB
C

// inflate.c
// Decompressor
#include <crtdbg.h>
#include <stdio.h>
#include "inflate.h"
#include "infmacro.h"
#include "infgzip.h"
#include "maketbl.h"
// Local function prototypes
static BOOL decodeBlock(t_decoder_context *context);
static BOOL makeTables(t_decoder_context *context);
HRESULT WINAPI Decompress(
PVOID void_context,
CONST BYTE * input,
LONG input_size,
BYTE * output,
LONG output_size,
PLONG input_used,
PLONG output_used
)
{
t_decoder_context *context = (t_decoder_context *) void_context;
context->input_curpos = input;
context->end_input_buffer = input + input_size;
context->output_curpos = output;
context->end_output_buffer = output + output_size;
context->output_buffer = output;
// Keep decoding blocks until the output fills up, we read all the input, or we enter
// the "done" state
// Note that INPUT_EOF() is not a sufficient check for determining that all the input
// has been used; there could be an additional block stored entirely in the bit buffer.
// For this reason, if we're in the READING_BFINAL state (start of new block) after
// calling decodeBlock(), don't quit the loop unless there is truly no input left in
// the bit buffer.
while ( (context->output_curpos < context->end_output_buffer) &&
(!INPUT_EOF()) &&
(context->state != STATE_DONE && context->state != STATE_VERIFYING_GZIP_FOOTER)
)
{
retry:
if (decodeBlock(context) == FALSE)
{
*input_used = 0;
*output_used = 0;
return E_FAIL;
}
// No more input bytes, but am starting a new block and there's at least one bit
// in the bit buffer
if (context->state == STATE_READING_BFINAL && INPUT_EOF() && context->bitcount > -16)
goto retry;
}
*input_used = (long) (context->input_curpos - input);
*output_used = (long) (context->output_curpos - output);
if (context->using_gzip)
{
// Calculate the crc32 of everything we just decompressed, and then, if our state
// is STATE_DONE, verify the crc
if (*output_used > 0)
{
context->gzip_crc32 = GzipCRC32(context->gzip_crc32, output, *output_used);
context->gzip_output_stream_size += (*output_used);
}
if (context->state == STATE_VERIFYING_GZIP_FOOTER)
{
context->state = STATE_DONE;
// Now do our crc/input size check
if (context->gzip_crc32 != context->gzip_footer_crc32 ||
context->gzip_output_stream_size != context->gzip_footer_output_stream_size)
{
*input_used = 0;
*output_used = 0;
return E_FAIL;
}
}
}
if (*input_used == 0 && *output_used == 0)
{
if (context->state == STATE_DONE)
return S_FALSE; // End of compressed data
else
return E_FAIL; // Avoid infinite loops
}
else
{
return S_OK;
}
}
// Returns TRUE for success, FALSE for an error of some kind (invalid data)
static BOOL decodeBlock(t_decoder_context *context)
{
BOOL eob, result;
if (context->state == STATE_DONE || context->state == STATE_VERIFYING_GZIP_FOOTER)
return TRUE;
if (context->using_gzip)
{
if (context->state == STATE_READING_GZIP_HEADER)
{
if (ReadGzipHeader(context) == FALSE)
return FALSE;
// If we're still reading the GZIP header it means we ran out of input
if (context->state == STATE_READING_GZIP_HEADER)
return TRUE;
}
if (context->state == STATE_START_READING_GZIP_FOOTER || context->state == STATE_READING_GZIP_FOOTER)
{
if (ReadGzipFooter(context) == FALSE)
return FALSE;
// Whether we ran out of input or not, return
return TRUE;
}
}
// Do we need to fill our bit buffer?
// This will happen the very first time we call Decompress(), as well as after decoding
// an uncompressed block
if (context->state == STATE_READING_BFINAL_NEED_TO_INIT_BITBUF)
{
// If we didn't have enough bits to init, return
if (initBitBuffer(context) == FALSE)
return TRUE;
}
// Need to read bfinal bit
if (context->state == STATE_READING_BFINAL)
{
// Need 1 bit
if (ensureBitsContext(context, 1) == FALSE)
return TRUE;
context->bfinal = getBits(context, 1);
context->state = STATE_READING_BTYPE;
}
if (context->state == STATE_READING_BTYPE)
{
// Need 2 bits
if (ensureBitsContext(context, 2) == FALSE)
return TRUE;
context->btype = getBits(context, 2);
if (context->btype == BLOCKTYPE_DYNAMIC)
{
context->state = STATE_READING_NUM_LIT_CODES;
}
else if (context->btype == BLOCKTYPE_FIXED)
{
context->state = STATE_DECODE_TOP;
}
else if (context->btype == BLOCKTYPE_UNCOMPRESSED)
{
context->state = STATE_UNCOMPRESSED_ALIGNING;
}
else
{
// unsupported compression mode
return FALSE;
}
}
if (context->btype == BLOCKTYPE_DYNAMIC)
{
if (context->state < STATE_DECODE_TOP)
{
if (readDynamicBlockHeader(context) == FALSE)
return FALSE;
if (context->state == STATE_DECODE_TOP)
{
if (makeTables(context) == FALSE)
return FALSE; // bad tables
}
else
{
return TRUE; // not enough input
}
}
result = DecodeDynamicBlock(context, &eob);
if (eob)
context->state = STATE_READING_BFINAL;
}
else if (context->btype == BLOCKTYPE_FIXED)
{
result = DecodeStaticBlock(context, &eob);
if (eob)
context->state = STATE_READING_BFINAL;
}
else if (context->btype == BLOCKTYPE_UNCOMPRESSED)
{
result = decodeUncompressedBlock(context, &eob);
}
else
{
// Invalid block type
return FALSE;
}
// If we reached the end of the block and the block we were decoding had
// bfinal=1 (final block)
if (eob && context->bfinal)
{
if (context->using_gzip)
context->state = STATE_START_READING_GZIP_FOOTER;
else
context->state = STATE_DONE;
}
return result;
}
// Will throw an exception if a corrupt table is detected
static BOOL makeTables(t_decoder_context *context)
{
if (makeTable(
MAX_LITERAL_TREE_ELEMENTS,
LITERAL_TABLE_BITS,
context->literal_tree_code_length,
context->literal_table,
context->literal_left,
context->literal_right) == FALSE)
return FALSE;
return makeTable(
MAX_DIST_TREE_ELEMENTS,
DISTANCE_TABLE_BITS,
context->distance_tree_code_length,
context->distance_table,
context->distance_left,
context->distance_right
);
}