This patch applies to zlib-1.1.3. It fixes a problem with 256-byte sliding windows, that exists in all the zlib versions up to 1.1.3, and it is proposed to be included in the future zlib-1.1.4 release. The problem occurs because MIN_LOOKAHEAD in deflate.c is usually bigger than 256, and this confuses the longest_match() function.
The validity of the patch is enabled by this spec correction. The particular implementation of the reference library limits the distance codes to 2^windowBits - MIN_LOOKAHEAD. When windowBits = 9, all the distance codes are no bigger than 250, so the CINFO flag in the zlib stream can safely be 0.
The patch also extends the functionality of deflateInit(), so that other (smaller) window sizes are also accepted. This way, the library becomes more extensible, allowing future additions of newer strategies optimized for speed and/or low memory requirements, such as the Z_RLE strategy. If a certain value of windowBits is not directly supported by the implementation, it can be adjusted to the nearest supported value: valid zlib streams are still produced.
--- deflate.c Thu Jul 09 18:06:12 1998 +++ deflate.c.fixed Thu Apr 12 04:02:36 2001 @@ -242,7 +242,7 @@ windowBits = -windowBits; } if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || - windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { return Z_STREAM_ERROR; } @@ -252,7 +252,11 @@ s->strm = strm; s->noheader = noheader; - s->w_bits = windowBits; +#if MIN_LOOKAHEAD < 256 + s->w_bits = (windowBits >= 8) ? windowBits : 8; +#else + s->w_bits = (windowBits >= 9) ? windowBits : 9; +#endif s->w_size = 1 << s->w_bits; s->w_mask = s->w_size - 1; @@ -460,7 +464,12 @@ /* Write the zlib header */ if (s->status == INIT_STATE) { +#if MIN_LOOKAHEAD < 256 uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; +#else + uInt optimized_cinfo = (s->w_bits > 9) ? s->w_bits - 8 : 0; + uInt header = (Z_DEFLATED + (optimized_cinfo<<4)) << 8; +#endif uInt level_flags = (s->level-1) >> 1; if (level_flags > 3) level_flags = 3; |