Tighten deflateBound bounds.

This improves the non-default expansion from 14% down to 4% in
most cases, and 13% in the remainder.
This commit is contained in:
Mark Adler 2022-10-03 20:05:58 -07:00
parent 7fabcb5357
commit 4078713e3b
1 changed files with 37 additions and 22 deletions

View File

@ -674,36 +674,50 @@ int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
}
/* =========================================================================
* For the default windowBits of 15 and memLevel of 8, this function returns
* a close to exact, as well as small, upper bound on the compressed size.
* They are coded as constants here for a reason--if the #define's are
* changed, then this function needs to be changed as well. The return
* value for 15 and 8 only works for those exact settings.
* For the default windowBits of 15 and memLevel of 8, this function returns a
* close to exact, as well as small, upper bound on the compressed size. This
* is an expansion of ~0.03%, plus a small constant.
*
* For any setting other than those defaults for windowBits and memLevel,
* the value returned is a conservative worst case for the maximum expansion
* resulting from using fixed blocks instead of stored blocks, which deflate
* can emit on compressed data for some combinations of the parameters.
* For any setting other than those defaults for windowBits and memLevel, one
* of two worst case bounds is returned. This is at most an expansion of ~4% or
* ~13%, plus a small constant.
*
* This function could be more sophisticated to provide closer upper bounds for
* every combination of windowBits and memLevel. But even the conservative
* upper bound of about 14% expansion does not seem onerous for output buffer
* allocation.
* Both the 0.03% and 4% derive from the overhead of stored blocks. The first
* one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second
* is for stored blocks of 127 bytes (the worst case memLevel == 1). The
* expansion results from five bytes of header for each stored block.
*
* The larger expansion of 13% results from a window size less than or equal to
* the symbols buffer size (windowBits <= memLevel + 7). In that case some of
* the data being compressed may have slid out of the sliding window, impeding
* a stored block from being emitted. Then the only choice is a fixed or
* dynamic block, where a fixed block limits the maximum expansion to 9 bits
* per 8-bit byte, plus 10 bits for every block. The smallest block size for
* which this can occur is 255 (memLevel == 2).
*
* Shifts are used to approximate divisions, for speed.
*/
uLong ZEXPORT deflateBound(strm, sourceLen)
z_streamp strm;
uLong sourceLen;
{
deflate_state *s;
uLong complen, wraplen;
uLong fixedlen, storelen, wraplen;
/* conservative upper bound for compressed data */
complen = sourceLen +
((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5;
/* upper bound for fixed blocks with 9-bit literals and length 255
(memLevel == 2, which is the lowest that may not use stored blocks) --
~13% overhead plus a small constant */
fixedlen = sourceLen + (sourceLen >> 3) + (sourceLen >> 8) +
(sourceLen >> 9) + 4;
/* if can't get parameters, return conservative bound plus zlib wrapper */
/* upper bound for stored blocks with length 127 (memLevel == 1) --
~4% overhead plus a small constant */
storelen = sourceLen + (sourceLen >> 5) + (sourceLen >> 7) +
(sourceLen >> 11) + 7;
/* if can't get parameters, return larger bound plus a zlib wrapper */
if (deflateStateCheck(strm))
return complen + 6;
return (fixedlen > storelen ? fixedlen : storelen) + 6;
/* compute wrapper length */
s = strm->state;
@ -740,11 +754,12 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
wraplen = 6;
}
/* if not default parameters, return conservative bound */
/* if not default parameters, return one of the conservative bounds */
if (s->w_bits != 15 || s->hash_bits != 8 + 7)
return complen + wraplen;
return (s->w_bits <= s->hash_bits ? fixedlen : storelen) + wraplen;
/* default settings: return tight bound for that case */
/* default settings: return tight bound for that case -- ~0.03% overhead
plus a small constant */
return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
(sourceLen >> 25) + 13 - 6 + wraplen;
}