internal BlockState DeflateSlow(FlushType flush)
{
// short hash_head = 0; // head of hash chain
int hash_head = 0; // head of hash chain
bool bflush; // set if current block must be flushed
// Process the input block.
while (true)
{
// Make sure that we always have enough lookahead, except
// at the end of the input file. We need MAX_MATCH bytes
// for the next match, plus MIN_MATCH bytes to insert the
// string following the next match.
if (lookahead < MIN_LOOKAHEAD)
{
_fillWindow();
if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None)
return BlockState.NeedMore;
if (lookahead == 0)
break; // flush the current block
}
// Insert the string window[strstart .. strstart+2] in the
// dictionary, and set hash_head to the head of the hash chain:
if (lookahead >= MIN_MATCH)
{
ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
// prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = unchecked((short)strstart);
}
// Find the longest match, discarding those <= prev_length.
prev_length = match_length;
prev_match = match_start;
match_length = MIN_MATCH - 1;
if (hash_head != 0 && prev_length < config.MaxLazy &&
((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD)
{
// To simplify the code, we prevent matches with the string
// of window index 0 (in particular we have to avoid a match
// of the string with itself at the start of the input file).
if (compressionStrategy != CompressionStrategy.HuffmanOnly)
{
match_length = longest_match(hash_head);
}
// longest_match() sets match_start
if (match_length <= 5 && (compressionStrategy == CompressionStrategy.Filtered ||
(match_length == MIN_MATCH && strstart - match_start > 4096)))
{
// If prev_match is also MIN_MATCH, match_start is garbage
// but we will ignore the current match anyway.
match_length = MIN_MATCH - 1;
}
}
// If there was a match at the previous step and the current
// match is not better, output the previous match:
if (prev_length >= MIN_MATCH && match_length <= prev_length)
{
int max_insert = strstart + lookahead - MIN_MATCH;
// Do not insert strings in hash table beyond this.
// check_match(strstart-1, prev_match, prev_length);
bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH);
// Insert in hash table all strings up to the end of the match.
// strstart-1 and strstart are already inserted. If there is not
// enough lookahead, the last two strings are not inserted in
// the hash table.
lookahead -= (prev_length - 1);
prev_length -= 2;
do
{
if (++strstart <= max_insert)
{
ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask;
//prev[strstart&w_mask]=hash_head=head[ins_h];
hash_head = (head[ins_h] & 0xffff);
prev[strstart & w_mask] = head[ins_h];
head[ins_h] = unchecked((short)strstart);
}
}
while (--prev_length != 0);
match_available = 0;
match_length = MIN_MATCH - 1;
strstart++;
if (bflush)
{
flush_block_only(false);
if (_codec.AvailableBytesOut == 0)
return BlockState.NeedMore;
}
}
else if (match_available != 0)
{
// If there was no match at the previous position, output a
// single literal. If there was a match but the current match
// is longer, truncate the previous match to a single literal.
bflush = _tr_tally(0, window[strstart - 1] & 0xff);
if (bflush)
{
flush_block_only(false);
}
strstart++;
lookahead--;
if (_codec.AvailableBytesOut == 0)
return BlockState.NeedMore;
}
else
{
// There is no previous match to compare with, wait for
// the next step to decide.
match_available = 1;
strstart++;
lookahead--;
}
}
if (match_available != 0)
{
bflush = _tr_tally(0, window[strstart - 1] & 0xff);
match_available = 0;
}
flush_block_only(flush == FlushType.Finish);
if (_codec.AvailableBytesOut == 0)
{
if (flush == FlushType.Finish)
return BlockState.FinishStarted;
else
return BlockState.NeedMore;
}
return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone;
}