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

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 * For the default windowBits of 15 and memLevel of 8, this function returns a
* a close to exact, as well as small, upper bound on the compressed size. * close to exact, as well as small, upper bound on the compressed size. This
* They are coded as constants here for a reason--if the #define's are * is an expansion of ~0.03%, plus a small constant.
* changed, then this function needs to be changed as well. The return
* value for 15 and 8 only works for those exact settings.
* *
* For any setting other than those defaults for windowBits and memLevel, * For any setting other than those defaults for windowBits and memLevel, one
* the value returned is a conservative worst case for the maximum expansion * of two worst case bounds is returned. This is at most an expansion of ~4% or
* resulting from using fixed blocks instead of stored blocks, which deflate * ~13%, plus a small constant.
* can emit on compressed data for some combinations of the parameters.
* *
* This function could be more sophisticated to provide closer upper bounds for * Both the 0.03% and 4% derive from the overhead of stored blocks. The first
* every combination of windowBits and memLevel. But even the conservative * one is for stored blocks of 16383 bytes (memLevel == 8), whereas the second
* upper bound of about 14% expansion does not seem onerous for output buffer * is for stored blocks of 127 bytes (the worst case memLevel == 1). The
* allocation. * 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) uLong ZEXPORT deflateBound(strm, sourceLen)
z_streamp strm; z_streamp strm;
uLong sourceLen; uLong sourceLen;
{ {
deflate_state *s; deflate_state *s;
uLong complen, wraplen; uLong fixedlen, storelen, wraplen;
/* conservative upper bound for compressed data */ /* upper bound for fixed blocks with 9-bit literals and length 255
complen = sourceLen + (memLevel == 2, which is the lowest that may not use stored blocks) --
((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; ~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)) if (deflateStateCheck(strm))
return complen + 6; return (fixedlen > storelen ? fixedlen : storelen) + 6;
/* compute wrapper length */ /* compute wrapper length */
s = strm->state; s = strm->state;
@ -740,11 +754,12 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
wraplen = 6; 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) 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) + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) +
(sourceLen >> 25) + 13 - 6 + wraplen; (sourceLen >> 25) + 13 - 6 + wraplen;
} }