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

352 lines
9.6 KiB
C

/*
* output.c
* General outputting routines
*/
#include "deflate.h"
#include <string.h>
#include <stdio.h>
#include <crtdbg.h>
// Output an element from the pre-tree
#define OUTPUT_PRETREE_ELEMENT(element) \
_ASSERT(pretree_len[element] != 0); \
outputBits(context, pretree_len[element], pretree_code[element]);
// Output the tree structure for a dynamic block
void outputTreeStructure(t_encoder_context *context, const BYTE *literal_tree_len, const BYTE *dist_tree_len)
{
int hdist, hlit, combined_tree_elements, i, pass;
USHORT pretree_freq[NUM_PRETREE_ELEMENTS*2];
USHORT pretree_code[NUM_PRETREE_ELEMENTS];
byte pretree_len[NUM_PRETREE_ELEMENTS];
// combined literal + distance length code array for outputting the trees
// in compressed form
// +3 is so we can overflow the array when performing run length encoding
// (dummy values are inserted at the end so that run length encoding fails
// before falling off the end of the array)
BYTE lens[MAX_LITERAL_TREE_ELEMENTS + MAX_DIST_TREE_ELEMENTS + 3];
// Calculate HDIST
for (hdist = MAX_DIST_TREE_ELEMENTS - 1; hdist >= 1; hdist--)
{
if (dist_tree_len[hdist] != 0)
break;
}
hdist++;
// Calculate HLIT
for (hlit = MAX_LITERAL_TREE_ELEMENTS - 1; hlit >= 257; hlit--)
{
if (literal_tree_len[hlit] != 0)
break;
}
hlit++;
// Now initialise the array to have all of the hlit and hdist codes
// in it
combined_tree_elements = hdist + hlit;
memcpy(lens, literal_tree_len, hlit);
memcpy(&lens[hlit], dist_tree_len, hdist);
// Stick in some dummy values at the end so that we don't overflow the
// array when comparing
for (i = combined_tree_elements; i < sizeof(lens); i++)
lens[i] = -1;
for (i = 0; i < NUM_PRETREE_ELEMENTS; i++)
pretree_freq[i] = 0;
// Output the bitlengths in compressed (run length encoded) form.
// Make two passes; on the first pass count the various codes, create
// the tree and output it, on the second pass output the codes using
// the tree.
for (pass = 0; pass < 2; pass++)
{
int cur_element;
// are we outputting during this pass?
BOOL outputting = (pass == 1);
cur_element = 0;
while (cur_element < combined_tree_elements)
{
int curlen = lens[cur_element];
int run_length;
// See how many consecutive elements have the same value
// This won't run off the end of the array; it will hit the -1's
// we stored there
for (run_length = cur_element+1; lens[run_length] == curlen; run_length++)
;
run_length -= cur_element;
// For non-zero codes need 4 identical in a row (original code
// plus 3 repeats). We decrement the run_length by one if the
// code is not zero, since we don't count the first (original)
// code in this case.
// For zero codes, need 3 zeroes in a row.
if (curlen != 0)
run_length--;
if (run_length < 3)
{
if (outputting)
{
OUTPUT_PRETREE_ELEMENT(curlen);
}
else
pretree_freq[curlen]++;
cur_element++;
}
else
{
// Elements with zero values are encoded specially
if (curlen == 0)
{
// Do we use code 17 (3-10 repeated zeroes) or
// code 18 (11-138 repeated zeroes)?
if (run_length <= 10)
{
// code 17
if (outputting)
{
OUTPUT_PRETREE_ELEMENT(17);
outputBits(context, 3, run_length - 3);
}
else
{
pretree_freq[17]++;
}
}
else
{
// code 18
if (run_length > 138)
run_length = 138;
if (outputting)
{
OUTPUT_PRETREE_ELEMENT(18);
outputBits(context, 7, run_length - 11);
}
else
{
pretree_freq[18]++;
}
}
cur_element += run_length;
}
else
{
// Number of lengths actually encoded. This may end up
// being less than run_length if we have a run length of
// 7 (6 + 1 [which cannot be encoded with a code 16])
int run_length_encoded = 0;
// curlen != 0
// can output 3...6 repeats of a non-zero code, so split
// longer runs into short ones (if possible)
// remember to output the code itself first!
if (outputting)
{
OUTPUT_PRETREE_ELEMENT(curlen);
while (run_length >= 3)
{
int this_run = (run_length <= 6) ? run_length : 6;
OUTPUT_PRETREE_ELEMENT(16);
outputBits(context, 2, this_run - 3);
run_length_encoded += this_run;
run_length -= this_run;
}
}
else
{
pretree_freq[curlen]++;
while (run_length >= 3)
{
int this_run = (run_length <= 6) ? run_length : 6;
pretree_freq[16]++;
run_length_encoded += this_run;
run_length -= this_run;
}
}
// +1 for the original code itself
cur_element += (run_length_encoded+1);
}
}
}
// If this is the first pass, create the pretree from the
// frequency data and output it, as well as the values of
// HLIT, HDIST, HDCLEN (# pretree codes used)
if (pass == 0)
{
int hclen, i;
makeTree(
NUM_PRETREE_ELEMENTS,
7,
pretree_freq,
pretree_code,
pretree_len
);
// Calculate HCLEN
for (hclen = NUM_PRETREE_ELEMENTS-1; hclen >= 4; hclen--)
{
if (pretree_len[ g_CodeOrder[hclen] ] != 0)
break;
}
hclen++;
// Dynamic block header
outputBits(context, 5, hlit - 257);
outputBits(context, 5, hdist - 1);
outputBits(context, 4, hclen - 4);
for (i = 0; i < hclen; i++)
{
outputBits(context, 3, pretree_len[g_CodeOrder[i]]);
}
}
}
}
// bitwise i/o
void flushOutputBitBuffer(t_encoder_context *context)
{
if (context->bitcount > 0)
{
int prev_bitcount = context->bitcount;
outputBits(context, 16 - context->bitcount, 0);
// backtrack if we have to; ZIP is byte aligned, not 16-bit word aligned
if (prev_bitcount <= 8)
context->output_curpos--;
}
}
// Does not check for output overflow, so make sure to call checkOutputOverflow()
// often enough!
void outputBits(t_encoder_context *context, int n, int x)
{
_ASSERT(context->output_curpos < context->output_endpos-1);
_ASSERT(n > 0 && n <= 16);
context->bitbuf |= (x << context->bitcount);
context->bitcount += n;
if (context->bitcount >= 16)
{
*context->output_curpos++ = (BYTE) context->bitbuf;
*context->output_curpos++ = (BYTE) (context->bitbuf >> 8);
context->bitbuf >>= 16;
context->bitcount -= 16;
}
}
// initialise the bit buffer
void InitBitBuffer(t_encoder_context *context)
{
context->bitbuf = 0;
context->bitcount = 0;
}
void OutputBlock(t_encoder_context *context)
{
_ASSERT(context->std_encoder != NULL || context->optimal_encoder != NULL);
// we never call OutputBlock() with the fast encoder
_ASSERT(context->fast_encoder == NULL);
if (context->std_encoder != NULL)
StdEncoderOutputBlock(context);
else if (context->optimal_encoder != NULL)
OptimalEncoderOutputBlock(context);
}
void FlushRecordingBuffer(t_encoder_context *context)
{
_ASSERT(context->std_encoder != NULL || context->optimal_encoder != NULL);
_ASSERT(context->fast_encoder == NULL); // fast encoder does not record
if (context->std_encoder != NULL)
{
*context->std_encoder->recording_bufptr++ = (BYTE) context->std_encoder->recording_bitbuf;
*context->std_encoder->recording_bufptr++ = (BYTE) (context->std_encoder->recording_bitbuf >> 8);
}
else if (context->optimal_encoder != NULL)
{
*context->optimal_encoder->recording_bufptr++ = (BYTE) context->optimal_encoder->recording_bitbuf;
*context->optimal_encoder->recording_bufptr++ = (BYTE) (context->optimal_encoder->recording_bitbuf >> 8);
}
}