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.
889 lines
25 KiB
889 lines
25 KiB
/*
|
|
* optenc.c
|
|
*
|
|
* Optimal encoder
|
|
*
|
|
* BUGBUG Can improve compression by using the "redo" method of LZX; after the first 32K bytes,
|
|
* reset the compressor but keep the tables, and start over.
|
|
*/
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <crtdbg.h>
|
|
#include "deflate.h"
|
|
|
|
|
|
//
|
|
// If we get a match this good, take it automatically
|
|
//
|
|
// Note: FAST_DECISION_THRESHOLD can be set to anything; it's been set to BREAK_LENGTH
|
|
// arbitrarily
|
|
//
|
|
#define FAST_DECISION_THRESHOLD BREAK_LENGTH
|
|
|
|
|
|
//
|
|
// After we have this many literals, create a tree to get updated statistical estimates
|
|
//
|
|
#define FIRST_TREE_UPDATE 1024
|
|
|
|
|
|
//
|
|
// Verifies that all of the hash pointers in the hash table are correct, and that
|
|
// the tree structure is valid.
|
|
//
|
|
#define DISABLE_VERIFY_HASHES
|
|
|
|
#ifdef _DEBUG
|
|
#ifndef DISABLE_VERIFY_HASHES
|
|
#define VERIFY_HASHES(bufpos) verifyHashes(context, bufpos)
|
|
#else
|
|
#define VERIFY_HASHES(bufpos) ;
|
|
#endif
|
|
#else
|
|
#define VERIFY_HASHES(bufpos) ;
|
|
#endif
|
|
|
|
|
|
#define CHECK_FLUSH_RECORDING_BUFFER() \
|
|
if (recording_bitcount >= 16) \
|
|
{ \
|
|
*recording_bufptr++ = (BYTE) recording_bitbuf; \
|
|
*recording_bufptr++ = (BYTE) (recording_bitbuf >> 8); \
|
|
recording_bitbuf >>= 16; \
|
|
recording_bitcount -= 16; \
|
|
}
|
|
|
|
|
|
#define OUTPUT_RECORDING_DATA(count,data) \
|
|
recording_bitbuf |= ((data) << recording_bitcount); \
|
|
recording_bitcount += (count);
|
|
|
|
|
|
//
|
|
// Record unmatched symbol c
|
|
//
|
|
#define RECORD_CHAR(c) \
|
|
context->outputting_block_num_literals++; \
|
|
encoder->literal_tree_freq[c]++; \
|
|
_ASSERT(encoder->recording_literal_tree_len[c] != 0); \
|
|
OUTPUT_RECORDING_DATA(encoder->recording_literal_tree_len[c], encoder->recording_literal_tree_code[c]); \
|
|
CHECK_FLUSH_RECORDING_BUFFER();
|
|
|
|
|
|
//
|
|
// Record a match with length match_len (>= MIN_MATCH) and displacement match_pos
|
|
//
|
|
#define RECORD_MATCH(match_len, match_pos) \
|
|
{ \
|
|
int pos_slot = POS_SLOT(match_pos); \
|
|
int len_slot = g_LengthLookup[match_len - MIN_MATCH]; \
|
|
int item = (NUM_CHARS+1) + len_slot; \
|
|
int extra_dist_bits = g_ExtraDistanceBits[pos_slot]; \
|
|
int extra_len_bits = g_ExtraLengthBits[len_slot]; \
|
|
_ASSERT(match_len >= MIN_MATCH && match_len <= MAX_MATCH); \
|
|
_ASSERT(context->outputting_block_num_literals >= 0 && context->outputting_block_num_literals < OPT_ENCODER_MAX_ITEMS); \
|
|
_ASSERT(encoder->recording_literal_tree_len[item] != 0); \
|
|
_ASSERT(encoder->recording_dist_tree_len[pos_slot] != 0); \
|
|
context->outputting_block_num_literals++; \
|
|
encoder->literal_tree_freq[(NUM_CHARS + 1) + len_slot]++; \
|
|
encoder->dist_tree_freq[pos_slot]++; \
|
|
OUTPUT_RECORDING_DATA(encoder->recording_literal_tree_len[item], encoder->recording_literal_tree_code[item]); \
|
|
CHECK_FLUSH_RECORDING_BUFFER(); \
|
|
if (extra_len_bits > 0) \
|
|
{ \
|
|
OUTPUT_RECORDING_DATA(extra_len_bits, (match_len-MIN_MATCH) & ((1 << extra_len_bits)-1)); \
|
|
CHECK_FLUSH_RECORDING_BUFFER(); \
|
|
} \
|
|
OUTPUT_RECORDING_DATA(encoder->recording_dist_tree_len[pos_slot], encoder->recording_dist_tree_code[pos_slot]); \
|
|
CHECK_FLUSH_RECORDING_BUFFER(); \
|
|
if (extra_dist_bits > 0) \
|
|
{ \
|
|
OUTPUT_RECORDING_DATA(extra_dist_bits, match_pos & ((1 << extra_dist_bits)-1)); \
|
|
CHECK_FLUSH_RECORDING_BUFFER(); \
|
|
} \
|
|
}
|
|
|
|
|
|
#define FLUSH_RECORDING_BITBUF() \
|
|
*recording_bufptr++ = (BYTE) recording_bitbuf; \
|
|
*recording_bufptr++ = (BYTE) (recording_bitbuf >> 8);
|
|
|
|
|
|
static void calculateUpdatedEstimates(t_encoder_context *context);
|
|
static void OptimalEncoderMoveWindows(t_encoder_context *context);
|
|
|
|
|
|
static int match_est(t_optimal_encoder *encoder, int match_length, unsigned int match_pos)
|
|
{
|
|
int dist_slot;
|
|
int len_slot;
|
|
|
|
// output match position
|
|
len_slot = g_LengthLookup[match_length-MIN_MATCH];
|
|
dist_slot = POS_SLOT(match_pos);
|
|
|
|
return encoder->literal_tree_len[NUM_CHARS + 1 + len_slot] +
|
|
g_ExtraLengthBits[len_slot] +
|
|
encoder->dist_tree_len[dist_slot] +
|
|
g_ExtraDistanceBits[dist_slot];
|
|
}
|
|
|
|
|
|
//
|
|
// Create initial estimations to output each element
|
|
//
|
|
static void initOptimalEstimates(t_encoder_context *context)
|
|
{
|
|
int i, p;
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
|
|
for (i = 0; i < NUM_CHARS; i++)
|
|
encoder->literal_tree_len[i] = 8;
|
|
|
|
p = NUM_CHARS+1;
|
|
encoder->literal_tree_len[p] = 3;
|
|
encoder->literal_tree_len[p+1] = 4;
|
|
encoder->literal_tree_len[p+2] = 5;
|
|
|
|
for (; p < MAX_LITERAL_TREE_ELEMENTS; p++)
|
|
encoder->literal_tree_len[p] = 6;
|
|
|
|
for (i = 0; i < MAX_DIST_TREE_ELEMENTS; i++)
|
|
encoder->dist_tree_len[i] = (i/2)+1;
|
|
}
|
|
|
|
|
|
//
|
|
// Fix optimal estimates; if bitlen == 0 it doesn't mean that the element takes 0
|
|
// bits to output, it means that the element didn't occur, so come up with some estimate.
|
|
//
|
|
static void fixOptimalEstimates(t_encoder_context *context)
|
|
{
|
|
int i;
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
|
|
for (i = 0; i < NUM_CHARS; i++)
|
|
{
|
|
if (encoder->literal_tree_len[i] == 0)
|
|
encoder->literal_tree_len[i] = 13;
|
|
}
|
|
|
|
for (i = NUM_CHARS+1; i < MAX_LITERAL_TREE_ELEMENTS; i++)
|
|
{
|
|
if (encoder->literal_tree_len[i] == 0)
|
|
encoder->literal_tree_len[i] = 12;
|
|
}
|
|
|
|
for (i = 0; i < MAX_DIST_TREE_ELEMENTS; i++)
|
|
{
|
|
if (encoder->dist_tree_len[i] == 0)
|
|
encoder->dist_tree_len[i] = 10;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns an estimation of how many bits it would take to output
|
|
* a given character
|
|
*/
|
|
#define CHAR_EST(c) (numbits_t) (encoder->literal_tree_len[(c)])
|
|
|
|
|
|
/*
|
|
* Returns an estimation of how many bits it would take to output
|
|
* a given match.
|
|
*/
|
|
#define MATCH_EST(ml,mp,result) result = match_est(encoder, ml,mp);
|
|
|
|
|
|
//
|
|
// Returns whether the literal buffers are just about full
|
|
//
|
|
// Since we could output a large number of matches/chars in between these checks, we
|
|
// have to be careful.
|
|
//
|
|
// BUGBUG should check after each item output, so we don't have to be so careful; this
|
|
// means we will utilise more of the recording buffer
|
|
//
|
|
#define LITERAL_BUFFERS_FULL() \
|
|
(context->outputting_block_num_literals >= OPT_ENCODER_MAX_ITEMS-4-LOOK-MAX_MATCH || \
|
|
recording_bufptr + 3*(MAX_MATCH + LOOK) >= end_recording_bufptr)
|
|
|
|
|
|
void OptimalEncoderDeflate(t_encoder_context *context)
|
|
{
|
|
unsigned long bufpos_end;
|
|
unsigned long MatchPos;
|
|
unsigned long i;
|
|
int EncMatchLength; /* must be a signed number */
|
|
unsigned long bufpos;
|
|
unsigned long recording_bitbuf;
|
|
int recording_bitcount;
|
|
byte * recording_bufptr;
|
|
byte * end_recording_bufptr;
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
|
|
_ASSERT(encoder != NULL);
|
|
_ASSERT(context->state == STATE_NORMAL);
|
|
|
|
// reinsert the up to BREAK_LENGTH nodes we removed the last time we exit this function
|
|
VERIFY_HASHES(context->bufpos);
|
|
reinsertRemovedNodes(context);
|
|
VERIFY_HASHES(context->bufpos);
|
|
|
|
// restore literal/match bitmap variables
|
|
end_recording_bufptr = &encoder->lit_dist_buffer[OPT_ENCODER_LIT_DIST_BUFFER_SIZE-8];
|
|
recording_bufptr = encoder->recording_bufptr;
|
|
recording_bitbuf = encoder->recording_bitbuf;
|
|
recording_bitcount = encoder->recording_bitcount;
|
|
|
|
bufpos = context->bufpos;
|
|
bufpos_end = context->bufpos_end;
|
|
|
|
/*
|
|
* While we haven't reached the end of the data
|
|
*/
|
|
after_output_block:
|
|
|
|
while (bufpos < bufpos_end)
|
|
{
|
|
// time to update our stats?
|
|
if (context->outputting_block_num_literals >= encoder->next_tree_update)
|
|
{
|
|
encoder->next_tree_update += 1024;
|
|
|
|
calculateUpdatedEstimates(context);
|
|
fixOptimalEstimates(context);
|
|
}
|
|
|
|
// literal buffer or distance buffer filled up (or close to filling up)?
|
|
if (LITERAL_BUFFERS_FULL())
|
|
break;
|
|
|
|
/*
|
|
* Search for matches of all different possible lengths, at bufpos
|
|
*/
|
|
EncMatchLength = optimal_find_match(context, bufpos);
|
|
|
|
if (EncMatchLength < MIN_MATCH)
|
|
{
|
|
|
|
output_literal:
|
|
/*
|
|
* No match longer than 1 character exists in the history
|
|
* window, so output the character at bufpos as a symbol.
|
|
*/
|
|
RECORD_CHAR(encoder->window[bufpos]);
|
|
bufpos++;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Found a match.
|
|
*
|
|
* Make sure it cannot exceed the end of the buffer.
|
|
*/
|
|
if ((unsigned long) EncMatchLength + bufpos > bufpos_end)
|
|
{
|
|
EncMatchLength = bufpos_end - bufpos;
|
|
|
|
/*
|
|
* Oops, not enough for even a small match, so we
|
|
* have to output a literal
|
|
*/
|
|
if (EncMatchLength < MIN_MATCH)
|
|
goto output_literal;
|
|
}
|
|
|
|
if (EncMatchLength < FAST_DECISION_THRESHOLD)
|
|
{
|
|
/*
|
|
* A match has been found that is between MIN_MATCH and
|
|
* FAST_DECISION_THRESHOLD bytes in length. The following
|
|
* algorithm is the optimal encoder that will determine the
|
|
* most efficient order of matches and unmatched characters
|
|
* over a span area defined by LOOK.
|
|
*
|
|
* The code is essentially a shortest path determination
|
|
* algorithm. A stream of data can be encoded in a vast number
|
|
* of different ways depending on the match lengths and offsets
|
|
* chosen. The key to good compression ratios is to chose the
|
|
* least expensive path.
|
|
*/
|
|
unsigned long span;
|
|
unsigned long epos, bpos, NextPrevPos, MatchPos;
|
|
t_decision_node *decision_node_ptr;
|
|
t_decision_node *context_decision_node = encoder->decision_node;
|
|
t_match_pos *matchpos_table = encoder->matchpos_table;
|
|
long iterations;
|
|
|
|
/*
|
|
* Points to the end of the area covered by this match; the span
|
|
* will continually be extended whenever we find more matches
|
|
* later on. It will stop being extended when we reach a spot
|
|
* where there are no matches, which is when we decide which
|
|
* path to take to output the matches.
|
|
*/
|
|
span = bufpos + EncMatchLength;
|
|
|
|
/*
|
|
* The furthest position into which we will do our lookahead parsing
|
|
*/
|
|
epos = bufpos + LOOK;
|
|
|
|
/*
|
|
* Temporary bufpos variable
|
|
*/
|
|
bpos = bufpos;
|
|
|
|
/*
|
|
* Calculate the path to the next character if we output
|
|
* an unmatched symbol.
|
|
*/
|
|
|
|
/* bits required to get here */
|
|
context_decision_node[1].numbits = CHAR_EST(encoder->window[bufpos]);
|
|
|
|
/* where we came from */
|
|
context_decision_node[1].path = bufpos;
|
|
|
|
/* bits required to get here */
|
|
context_decision_node[2].numbits = CHAR_EST(encoder->window[bufpos+1]) + context_decision_node[1].numbits;
|
|
|
|
/* where we came from */
|
|
context_decision_node[2].path = bufpos+1;
|
|
|
|
/*
|
|
* For the match found, estimate the cost of encoding the match
|
|
* for each possible match length, shortest offset combination.
|
|
*
|
|
* The cost, path and offset is stored at bufpos + Length.
|
|
*/
|
|
for (i = MIN_MATCH; i <= (unsigned long) EncMatchLength; i++)
|
|
{
|
|
/*
|
|
* Get estimation of match cost given match length = i,
|
|
* match position = matchpos_table[i], and store
|
|
* the result in numbits[i]
|
|
*/
|
|
MATCH_EST(i, matchpos_table[i], context_decision_node[i].numbits);
|
|
|
|
/*
|
|
* Where we came from
|
|
*/
|
|
context_decision_node[i].path = bufpos;
|
|
|
|
/*
|
|
* Associated match position with this path
|
|
*/
|
|
context_decision_node[i].link = matchpos_table[i];
|
|
}
|
|
|
|
/*
|
|
* Set bit counter to zero at the start
|
|
*/
|
|
context_decision_node[0].numbits = 0;
|
|
|
|
decision_node_ptr = &context_decision_node[-(long) bpos];
|
|
|
|
while (1)
|
|
{
|
|
numbits_t est, cum_numbits;
|
|
|
|
bufpos++;
|
|
|
|
/*
|
|
* Set the proper repeated offset locations depending on the
|
|
* shortest path to the location prior to searching for a
|
|
* match.
|
|
*/
|
|
|
|
/*
|
|
* The following is one of the two possible break points from
|
|
* the inner encoding loop. This break will exit the loop if
|
|
* a point is reached that no match can incorporate; i.e. a
|
|
* character that does not match back to anything is a point
|
|
* where all possible paths will converge and the longest one
|
|
* can be chosen.
|
|
*/
|
|
if (span == bufpos)
|
|
break;
|
|
|
|
/*
|
|
* Search for matches at bufpos
|
|
*/
|
|
EncMatchLength = optimal_find_match(context, bufpos);
|
|
|
|
/*
|
|
* Make sure that the match does not exceed the stop point
|
|
*/
|
|
if ((unsigned long) EncMatchLength + bufpos > bufpos_end)
|
|
{
|
|
EncMatchLength = bufpos_end - bufpos;
|
|
|
|
if (EncMatchLength < MIN_MATCH)
|
|
EncMatchLength = 0;
|
|
}
|
|
|
|
/*
|
|
* If the match is very long or it exceeds epos (either
|
|
* surpassing the LOOK area, or exceeding past the end of the
|
|
* input buffer), then break the loop and output the path.
|
|
*/
|
|
if (EncMatchLength > FAST_DECISION_THRESHOLD ||
|
|
bufpos + (unsigned long) EncMatchLength >= epos)
|
|
{
|
|
MatchPos = matchpos_table[EncMatchLength];
|
|
|
|
decision_node_ptr[bufpos+EncMatchLength].link = MatchPos;
|
|
decision_node_ptr[bufpos+EncMatchLength].path = bufpos;
|
|
|
|
/*
|
|
* Quickly insert data into the search tree without
|
|
* returning match positions/lengths
|
|
*/
|
|
#ifndef INSERT_NEAR_LONG_MATCHES
|
|
if (MatchPos == 3 && EncMatchLength > 16)
|
|
{
|
|
/*
|
|
* If we found a match 1 character away and it's
|
|
* length 16 or more, it's probably a string of
|
|
* zeroes, so don't insert that into the search
|
|
* engine, since doing so can slow things down
|
|
* significantly!
|
|
*/
|
|
optimal_insert(
|
|
context,
|
|
bufpos + 1,
|
|
bufpos - WINDOW_SIZE + 2
|
|
);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
for (i = 1; i < (unsigned long) EncMatchLength; i++)
|
|
optimal_insert(
|
|
context,
|
|
bufpos + i,
|
|
bufpos + i - WINDOW_SIZE + 4
|
|
);
|
|
}
|
|
|
|
bufpos += EncMatchLength;
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* The following code will extend the area spanned by the
|
|
* set of matches if the current match surpasses the end of
|
|
* the span. A match of length two that is far is not
|
|
* accepted, since it would normally be encoded as characters,
|
|
* thus allowing the paths to converge.
|
|
*/
|
|
if (EncMatchLength >= 3)
|
|
{
|
|
if (span < (unsigned long) (bufpos + EncMatchLength))
|
|
{
|
|
long end;
|
|
long i;
|
|
|
|
end = min(bufpos+EncMatchLength-bpos, LOOK-1);
|
|
|
|
/*
|
|
* These new positions are undefined for now, since we haven't
|
|
* gone there yet, so put in the costliest value
|
|
*/
|
|
for (i = span-bpos+1; i <= end; i++)
|
|
context_decision_node[i].numbits = (numbits_t) -1;
|
|
|
|
span = bufpos + EncMatchLength;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The following code will iterate through all combinations
|
|
* of match lengths for the current match. It will estimate
|
|
* the cost of the path from the beginning of LOOK to
|
|
* bufpos and to every locations spanned by the current
|
|
* match. If the path through bufpos with the found matches
|
|
* is estimated to take fewer number of bits to encode than
|
|
* the previously found match, then the path to the location
|
|
* is altered.
|
|
*
|
|
* The code relies on accurate estimation of the cost of
|
|
* encoding a character or a match. Furthermore, it requires
|
|
* a search engine that will store the smallest match offset
|
|
* of each possible match length.
|
|
*
|
|
* A match of length one is simply treated as an unmatched
|
|
* character.
|
|
*/
|
|
|
|
/*
|
|
* Get the estimated number of bits required to encode the
|
|
* path leading up to bufpos.
|
|
*/
|
|
cum_numbits = decision_node_ptr[bufpos].numbits;
|
|
|
|
/*
|
|
* Calculate the estimated cost of outputting the path through
|
|
* bufpos and outputting the next character as an unmatched byte
|
|
*/
|
|
est = cum_numbits + CHAR_EST(encoder->window[bufpos]);
|
|
|
|
/*
|
|
* Check if it is more efficient to encode the next character
|
|
* as an unmatched character rather than the previously found
|
|
* match. If so, then update the cheapest path to bufpos + 1.
|
|
*
|
|
* What happens if est == numbits[bufpos-bpos+1]; i.e. it
|
|
* works out as well to output a character as to output a
|
|
* match? It's a tough call; however, we will push the
|
|
* encoder to use matches where possible.
|
|
*/
|
|
if (est < decision_node_ptr[bufpos+1].numbits)
|
|
{
|
|
decision_node_ptr[bufpos+1].numbits = est;
|
|
decision_node_ptr[bufpos+1].path = bufpos;
|
|
}
|
|
|
|
/*
|
|
* Now, iterate through the remaining match lengths and
|
|
* compare the new path to the existing. Change the path
|
|
* if it is found to be more cost effective to go through
|
|
* bufpos.
|
|
*/
|
|
for (i = MIN_MATCH; i <= (unsigned long) EncMatchLength; i++)
|
|
{
|
|
MATCH_EST(i, matchpos_table[i], est);
|
|
est += cum_numbits;
|
|
|
|
/*
|
|
* If est == numbits[bufpos+i] we want to leave things
|
|
* alone, since this will tend to force the matches
|
|
* to be smaller in size, which is beneficial for most
|
|
* data.
|
|
*/
|
|
if (est < decision_node_ptr[bufpos+i].numbits)
|
|
{
|
|
decision_node_ptr[bufpos+i].numbits = est;
|
|
decision_node_ptr[bufpos+i].path = bufpos;
|
|
decision_node_ptr[bufpos+i].link = matchpos_table[i];
|
|
}
|
|
}
|
|
} /* continue to loop through span of matches */
|
|
|
|
/*
|
|
* Here bufpos == span, ie. a non-matchable character found. The
|
|
* following code will output the path properly.
|
|
*/
|
|
|
|
/*
|
|
* Unfortunately the path is stored in reverse; how to get from
|
|
* where we are now, to get back to where it all started.
|
|
*
|
|
* Traverse the path back to the original starting position
|
|
* of the LOOK span. Invert the path pointers in order to be
|
|
* able to traverse back to the current position from the start.
|
|
*/
|
|
|
|
/*
|
|
* Count the number of iterations we did, so when we go forwards
|
|
* we'll do the same amount
|
|
*/
|
|
iterations = 0;
|
|
|
|
NextPrevPos = decision_node_ptr[bufpos].path;
|
|
|
|
do
|
|
{
|
|
unsigned long PrevPos;
|
|
|
|
PrevPos = NextPrevPos;
|
|
|
|
NextPrevPos = decision_node_ptr[PrevPos].path;
|
|
decision_node_ptr[PrevPos].path = bufpos;
|
|
|
|
bufpos = PrevPos;
|
|
iterations++;
|
|
} while (bufpos != bpos);
|
|
|
|
/*
|
|
* Traverse from the beginning of the LOOK span to the end of
|
|
* the span along the stored path, outputting matches and
|
|
* characters appropriately.
|
|
*/
|
|
do
|
|
{
|
|
if (decision_node_ptr[bufpos].path > bufpos+1)
|
|
{
|
|
/*
|
|
* Path skips over more than 1 character; therefore it's a match
|
|
*/
|
|
RECORD_MATCH(
|
|
decision_node_ptr[bufpos].path - bufpos,
|
|
decision_node_ptr[ decision_node_ptr[bufpos].path ].link
|
|
);
|
|
|
|
bufpos = decision_node_ptr[bufpos].path;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Path goes to the next character; therefore it's a symbol
|
|
*/
|
|
RECORD_CHAR(encoder->window[bufpos]);
|
|
bufpos++;
|
|
}
|
|
} while (--iterations != 0);
|
|
}
|
|
else /* EncMatchLength >= FAST_DECISION_THRESHOLD */
|
|
{
|
|
/*
|
|
* This code reflects a speed optimization that will always take
|
|
* a match of length >= FAST_DECISION_THRESHOLD characters.
|
|
*/
|
|
|
|
/*
|
|
* The position associated with the match we found
|
|
*/
|
|
MatchPos = encoder->matchpos_table[EncMatchLength];
|
|
|
|
/*
|
|
* Quickly insert match substrings into search tree
|
|
* (don't look for new matches; just insert the strings)
|
|
*/
|
|
#ifndef INSERT_NEAR_LONG_MATCHES
|
|
if (MatchPos == 3 && EncMatchLength > 16)
|
|
{
|
|
optimal_insert(
|
|
context,
|
|
bufpos + 1,
|
|
bufpos - WINDOW_SIZE + 2
|
|
);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
for (i = 1; i < (unsigned long) EncMatchLength; i++)
|
|
optimal_insert(
|
|
context,
|
|
bufpos + i,
|
|
bufpos + i - WINDOW_SIZE + 1
|
|
);
|
|
}
|
|
|
|
/*
|
|
* Advance our position in the window
|
|
*/
|
|
bufpos += EncMatchLength;
|
|
|
|
/*
|
|
* Output the match
|
|
*/
|
|
RECORD_MATCH(EncMatchLength, MatchPos);
|
|
|
|
} /* EncMatchLength >= FAST_DECISION_THRESHOLD */
|
|
} /* end while ... bufpos <= bufpos_end */
|
|
|
|
if (LITERAL_BUFFERS_FULL())
|
|
{
|
|
_ASSERT(context->outputting_block_num_literals <= OPT_ENCODER_MAX_ITEMS);
|
|
|
|
// flush our recording matches bit buffer
|
|
FLUSH_RECORDING_BITBUF();
|
|
|
|
// BUGBUG Should check for failure result. Luckily the only failure condition is
|
|
// that the tree didn't fit into 500 bytes, which is basically impossible anyway.
|
|
(void) OptimalEncoderOutputBlock(context);
|
|
|
|
// fix estimates for optimal parser
|
|
fixOptimalEstimates(context);
|
|
|
|
encoder->next_tree_update = FIRST_TREE_UPDATE;
|
|
|
|
// did we output the whole block?
|
|
if (context->state == STATE_NORMAL)
|
|
{
|
|
// reset literal recording
|
|
recording_bufptr = encoder->recording_bufptr;
|
|
recording_bitbuf = encoder->recording_bitbuf;
|
|
recording_bitcount = encoder->recording_bitcount;
|
|
goto after_output_block;
|
|
}
|
|
}
|
|
|
|
// save recording state
|
|
encoder->recording_bufptr = recording_bufptr;
|
|
encoder->recording_bitbuf = recording_bitbuf;
|
|
encoder->recording_bitcount = recording_bitcount;
|
|
|
|
context->bufpos = bufpos;
|
|
|
|
VERIFY_HASHES(bufpos);
|
|
removeNodes(context);
|
|
VERIFY_HASHES(bufpos);
|
|
|
|
if (context->bufpos == 2*WINDOW_SIZE)
|
|
OptimalEncoderMoveWindows(context);
|
|
}
|
|
|
|
|
|
//
|
|
// Move the search windows when bufpos reaches 2*WINDOW_SIZE
|
|
//
|
|
static void OptimalEncoderMoveWindows(t_encoder_context *context)
|
|
{
|
|
long delta;
|
|
int i;
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
t_search_node *search_tree_root = encoder->search_tree_root;
|
|
t_search_node *left = encoder->search_left;
|
|
t_search_node *right = encoder->search_right;
|
|
|
|
_ASSERT(context->bufpos == 2*WINDOW_SIZE);
|
|
|
|
VERIFY_HASHES(context->bufpos);
|
|
|
|
delta = context->bufpos - WINDOW_SIZE;
|
|
|
|
memcpy(&encoder->window[0], &encoder->window[context->bufpos - WINDOW_SIZE], WINDOW_SIZE);
|
|
|
|
for (i = 0; i < NUM_DIRECT_LOOKUP_TABLE_ELEMENTS; i++)
|
|
{
|
|
long val = ((long) search_tree_root[i]) - delta;
|
|
|
|
if (val <= 0)
|
|
search_tree_root[i] = (t_search_node) 0;
|
|
else
|
|
search_tree_root[i] = (t_search_node) val;
|
|
|
|
_ASSERT(search_tree_root[i] < WINDOW_SIZE);
|
|
}
|
|
|
|
memcpy(&left[0], &left[context->bufpos - WINDOW_SIZE], sizeof(t_search_node)*WINDOW_SIZE);
|
|
memcpy(&right[0], &right[context->bufpos - WINDOW_SIZE], sizeof(t_search_node)*WINDOW_SIZE);
|
|
|
|
for (i = 0; i < WINDOW_SIZE; i++)
|
|
{
|
|
long val;
|
|
|
|
// left
|
|
val = ((long) left[i]) - delta;
|
|
|
|
if (val <= 0)
|
|
left[i] = (t_search_node) 0;
|
|
else
|
|
left[i] = (t_search_node) val;
|
|
|
|
// right
|
|
val = ((long) right[i]) - delta;
|
|
|
|
if (val <= 0)
|
|
right[i] = (t_search_node) 0;
|
|
else
|
|
right[i] = (t_search_node) val;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
// force any search table references to be invalid
|
|
memset(&encoder->window[WINDOW_SIZE], 0, WINDOW_SIZE);
|
|
#endif
|
|
|
|
context->bufpos = WINDOW_SIZE;
|
|
context->bufpos_end = context->bufpos;
|
|
|
|
VERIFY_HASHES(context->bufpos);
|
|
}
|
|
|
|
|
|
//
|
|
// Calculate the frequencies of all literal and distance codes, for tree-making, then
|
|
// make the trees
|
|
//
|
|
static void calculateUpdatedEstimates(t_encoder_context *context)
|
|
{
|
|
USHORT code[MAX_LITERAL_TREE_ELEMENTS];
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
|
|
// create the trees, we're interested only in len[], not code[]
|
|
// BUGBUG perf optimisation: make makeTree() not call MakeCode() in this situation
|
|
makeTree(
|
|
MAX_LITERAL_TREE_ELEMENTS,
|
|
15,
|
|
encoder->literal_tree_freq,
|
|
code,
|
|
encoder->literal_tree_len
|
|
);
|
|
|
|
makeTree(
|
|
MAX_DIST_TREE_ELEMENTS,
|
|
15,
|
|
encoder->dist_tree_freq,
|
|
code,
|
|
encoder->dist_tree_len
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Zero the running frequency counts
|
|
//
|
|
// Also set freq[END_OF_BLOCK_CODE] = 1
|
|
//
|
|
void OptimalEncoderZeroFrequencyCounts(t_optimal_encoder *encoder)
|
|
{
|
|
_ASSERT(encoder != NULL);
|
|
|
|
memset(encoder->literal_tree_freq, 0, sizeof(encoder->literal_tree_freq));
|
|
memset(encoder->dist_tree_freq, 0, sizeof(encoder->dist_tree_freq));
|
|
encoder->literal_tree_freq[END_OF_BLOCK_CODE] = 1;
|
|
}
|
|
|
|
|
|
void OptimalEncoderReset(t_encoder_context *context)
|
|
{
|
|
t_optimal_encoder *encoder = context->optimal_encoder;
|
|
|
|
_ASSERT(encoder != NULL);
|
|
|
|
encoder->recording_bitbuf = 0;
|
|
encoder->recording_bitcount = 0;
|
|
encoder->recording_bufptr = encoder->lit_dist_buffer;
|
|
|
|
context->window_size = WINDOW_SIZE;
|
|
context->bufpos = context->window_size;
|
|
context->bufpos_end = context->bufpos;
|
|
|
|
DeflateInitRecordingTables(
|
|
encoder->recording_literal_tree_len,
|
|
encoder->recording_literal_tree_code,
|
|
encoder->recording_dist_tree_len,
|
|
encoder->recording_dist_tree_code
|
|
);
|
|
|
|
// clear the search table
|
|
memset(
|
|
encoder->search_tree_root,
|
|
0,
|
|
sizeof(encoder->search_tree_root)
|
|
);
|
|
|
|
encoder->next_tree_update = FIRST_TREE_UPDATE;
|
|
|
|
initOptimalEstimates(context);
|
|
OptimalEncoderZeroFrequencyCounts(encoder);
|
|
}
|
|
|
|
|
|
BOOL OptimalEncoderInit(t_encoder_context *context)
|
|
{
|
|
context->optimal_encoder = (t_optimal_encoder *) LocalAlloc(LMEM_FIXED, sizeof(t_optimal_encoder));
|
|
|
|
if (context->optimal_encoder == NULL)
|
|
return FALSE;
|
|
|
|
OptimalEncoderReset(context);
|
|
return TRUE;
|
|
}
|