* Copy without compression as much as possible from the input stream, return
* the current block state.
*
- * In case deflateParams() is used to later switch to a non-zero compression
- * level, s->matches (otherwise unused when storing) keeps track of the number
- * of hash table slides to perform. If s->matches is 1, then one hash table
- * slide will be done when switching. If s->matches is 2, the maximum value
- * allowed here, then the hash table will be cleared, since two or more slides
- * is the same as a clear.
- *
* deflate_stored() is written to minimize the number of times an input byte is
* copied. It is most efficient with large input and output buffers, which
* maximizes the opportunities to have a single copy from next_in to next_out.
* therefore s->block_start == s->strstart.
*/
if (used >= w_size) { /* supplant the previous history */
- s->matches = 2; /* clear hash */
memcpy(window, s->strm->next_in - w_size, w_size);
s->strstart = w_size;
s->insert = s->strstart;
/* Slide the window down. */
s->strstart -= w_size;
memcpy(window, window + w_size, s->strstart);
- if (s->matches < 2)
- s->matches++; /* add a pending slide_hash() */
s->insert = MIN(s->insert, s->strstart);
}
memcpy(window + s->strstart, s->strm->next_in - used, used);
s->block_start -= (int)w_size;
s->strstart -= w_size;
memcpy(window, window + w_size, s->strstart);
- if (s->matches < 2)
- s->matches++; /* add a pending slide_hash() */
have += w_size; /* more space now */
s->insert = MIN(s->insert, s->strstart);
}
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
-#include <time.h>
#include "deflate.h"
#define UNCOMPR_BUFFER_SIZE (64 * 1024)
#define UNCOMPR_RAND_SIZE (8 * 1024)
+/* Fill a buffer with deterministic, effectively incompressible bytes. */
+static void fill_random(uint8_t *buf, uint32_t len) {
+ uint32_t r = 0xa5a5a5a5u;
+ for (uint32_t i = 0; i < len; i++) {
+ r = r * 1664525u + 1013904223u;
+ buf[i] = (uint8_t)(r >> 24);
+ }
+}
+
TEST(deflate, params) {
PREFIX3(stream) c_stream, d_stream;
uint8_t *compr, *uncompr;
uint32_t compr_len, uncompr_len;
uint32_t diff;
- int32_t i;
- time_t now;
int err;
#ifndef ZLIB_COMPAT
int level = -1;
compr_len = COMPR_BUFFER_SIZE;
uncompr_len = UNCOMPR_BUFFER_SIZE;
- srand((unsigned)time(&now));
- for (i = 0; i < UNCOMPR_RAND_SIZE; i++)
- uncompr[i] = (uint8_t)(rand() % 256);
+ fill_random(uncompr, UNCOMPR_RAND_SIZE);
err = PREFIX(deflateInit)(&c_stream, Z_BEST_SPEED);
EXPECT_EQ(err, Z_OK);
free(compr);
free(uncompr);
}
+
+/* deflateParams must preserve match history when the params change mid-stream.
+ * The second half of the input is an exact copy of the first and sits within
+ * the 32K window, so preserved history turns it into cross-boundary matches.
+ * If the chains were dropped at the switch the copy is re-emitted and the
+ * stream nearly doubles. */
+static void deflate_params_history(int from_level, int from_strategy, int to_level, int to_strategy) {
+ const uint32_t half = 16 * 1024;
+ const uint32_t len = 2 * half;
+
+ uint8_t *uncompr = (uint8_t *)calloc(1, len);
+ uint8_t *compr = (uint8_t *)calloc(1, 2 * len);
+ uint8_t *check = (uint8_t *)calloc(1, len);
+ ASSERT_TRUE(uncompr != NULL && compr != NULL && check != NULL);
+
+ fill_random(uncompr, half);
+ memcpy(uncompr + half, uncompr, half);
+
+ PREFIX3(stream) c_stream;
+ memset(&c_stream, 0, sizeof(c_stream));
+ EXPECT_EQ(PREFIX(deflateInit2)(&c_stream, from_level, Z_DEFLATED, 15, 8, from_strategy), Z_OK);
+
+ c_stream.next_out = compr;
+ c_stream.avail_out = 2 * len;
+ c_stream.next_in = uncompr;
+ c_stream.avail_in = half;
+ EXPECT_EQ(PREFIX(deflate)(&c_stream, Z_NO_FLUSH), Z_OK);
+ EXPECT_EQ(c_stream.avail_in, 0u);
+
+ EXPECT_EQ(PREFIX(deflateParams)(&c_stream, to_level, to_strategy), Z_OK);
+
+ c_stream.next_in = uncompr + half;
+ c_stream.avail_in = half;
+ EXPECT_EQ(PREFIX(deflate)(&c_stream, Z_FINISH), Z_STREAM_END);
+
+ uint32_t compr_len = (uint32_t)(c_stream.next_out - compr);
+ EXPECT_EQ(PREFIX(deflateEnd)(&c_stream), Z_OK);
+
+ PREFIX3(stream) d_stream;
+ memset(&d_stream, 0, sizeof(d_stream));
+ EXPECT_EQ(PREFIX(inflateInit)(&d_stream), Z_OK);
+ d_stream.next_in = compr;
+ d_stream.avail_in = compr_len;
+ d_stream.next_out = check;
+ d_stream.avail_out = len;
+ EXPECT_EQ(PREFIX(inflate)(&d_stream, Z_FINISH), Z_STREAM_END);
+ EXPECT_EQ(d_stream.total_out, len);
+ EXPECT_EQ(memcmp(uncompr, check, len), 0);
+ EXPECT_EQ(PREFIX(inflateEnd)(&d_stream), Z_OK);
+
+ EXPECT_LT(compr_len, half + half / 2);
+
+ free(uncompr);
+ free(compr);
+ free(check);
+}
+
+TEST(deflate, params_history_level9_down) {
+ deflate_params_history(9, Z_DEFAULT_STRATEGY, 6, Z_DEFAULT_STRATEGY);
+}
+
+TEST(deflate, params_history_level9_up) {
+ deflate_params_history(6, Z_DEFAULT_STRATEGY, 9, Z_DEFAULT_STRATEGY);
+}
+
+/* Switching from a hashless strategy (Z_RLE/Z_HUFFMAN_ONLY) back to a hashing one
+ * at the same level must also rebuild, since those strategies never insert. */
+TEST(deflate, params_history_rle) {
+ deflate_params_history(6, Z_RLE, 6, Z_DEFAULT_STRATEGY);
+}
+
+TEST(deflate, params_history_huffman) {
+ deflate_params_history(6, Z_HUFFMAN_ONLY, 6, Z_DEFAULT_STRATEGY);
+}