From: Ilya Leoshkevich Date: Thu, 23 May 2019 11:57:43 +0000 (+0200) Subject: Add two new public zng_deflate{Set,Get}Params() functions X-Git-Tag: 1.9.9-b1~467 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b927c6ab8774424ae006c185d19dfd327c0f3d6e;p=thirdparty%2Fzlib-ng.git Add two new public zng_deflate{Set,Get}Params() functions These functions allow zlib-ng callers to modify and query the compression parameters in a future-proof way. When the caller requests a parameter, which is not supported by the current zlib-ng version, this situation is detected and reported to the caller. The caller may modify or query multiple parameters at once. Currently only "level" and "strategy" parameters are supported. It is planned to add a "reproducible" parameter, which would affect whether IBM Z DEFLATE CONVERSION CALL is used. Passing enum and void * buffer was chosen over passing strings, because of simplicity for the caller. If strings were used, C callers would have to call snprintf() and strtoul() for setting and getting integer-valued parameters respectively, which is quite tedious. Bulk updates were chosen over updating individual parameters separately, because it might make sense to apply some parameters atomically, e.g. level and strategy. The new functions are defined only for zlib-ng, but not compat zlib. --- diff --git a/deflate.c b/deflate.c index dd4edff0..41d1f72a 100644 --- a/deflate.c +++ b/deflate.c @@ -1710,3 +1710,122 @@ void send_bits(deflate_state *s, int value, int length) { } } #endif + +#ifndef ZLIB_COMPAT +/* ========================================================================= + * Checks whether buffer size is sufficient and whether this parameter is a duplicate. + */ +static int deflateSetParamPre(zng_deflate_param_value **out, size_t min_size, zng_deflate_param_value *param) { + int buf_error = param->size < min_size; + + if (*out != NULL) { + (*out)->status = Z_BUF_ERROR; + buf_error = 1; + } + *out = param; + return buf_error; +} + +/* ========================================================================= */ +int ZEXPORT zng_deflateSetParams(zng_stream *strm, zng_deflate_param_value *params, size_t count) { + size_t i; + deflate_state *s; + zng_deflate_param_value *new_level = NULL; + zng_deflate_param_value *new_strategy = NULL; + int param_buf_error; + int version_error = 0; + int buf_error = 0; + int stream_error = 0; + int ret; + + /* Initialize the statuses. */ + for (i = 0; i < count; i++) + params[i].status = Z_OK; + + /* Check whether the stream state is consistent. */ + if (deflateStateCheck(strm)) + return Z_STREAM_ERROR; + s = strm->state; + + /* Check buffer sizes and detect duplicates. */ + for (i = 0; i < count; i++) { + switch (params[i].param) { + case Z_DEFLATE_LEVEL: + param_buf_error = deflateSetParamPre(&new_level, sizeof(int), ¶ms[i]); + break; + case Z_DEFLATE_STRATEGY: + param_buf_error = deflateSetParamPre(&new_strategy, sizeof(int), ¶ms[i]); + break; + default: + params[i].status = Z_VERSION_ERROR; + version_error = 1; + param_buf_error = 0; + break; + } + if (param_buf_error) { + params[i].status = Z_BUF_ERROR; + buf_error = 1; + } + } + /* Exit early if small buffers or duplicates are detected. */ + if (buf_error) + return Z_BUF_ERROR; + + /* Apply changes, remember if there were errors. */ + if (new_level != NULL || new_strategy != NULL) { + ret = PREFIX(deflateParams)(strm, new_level == NULL ? s->level : *(int *)new_level->buf, + new_strategy == NULL ? s->strategy : *(int *)new_strategy->buf); + if (ret != Z_OK) { + if (new_level != NULL) + new_level->status = Z_STREAM_ERROR; + if (new_strategy != NULL) + new_strategy->status = Z_STREAM_ERROR; + stream_error = 1; + } + } + + /* Report version errors only if there are no real errors. */ + return stream_error ? Z_STREAM_ERROR : (version_error ? Z_VERSION_ERROR : Z_OK); +} + +/* ========================================================================= */ +int ZEXPORT zng_deflateGetParams(zng_stream *strm, zng_deflate_param_value *params, size_t count) { + deflate_state *s; + size_t i; + int buf_error = 0; + int version_error = 0; + + /* Initialize the statuses. */ + for (i = 0; i < count; i++) + params[i].status = Z_OK; + + /* Check whether the stream state is consistent. */ + if (deflateStateCheck(strm)) + return Z_STREAM_ERROR; + s = strm->state; + + for (i = 0; i < count; i++) { + switch (params[i].param) { + case Z_DEFLATE_LEVEL: + if (params[i].size < sizeof(int)) + params[i].status = Z_BUF_ERROR; + else + *(int *)params[i].buf = s->level; + break; + case Z_DEFLATE_STRATEGY: + if (params[i].size < sizeof(int)) + params[i].status = Z_BUF_ERROR; + else + *(int *)params[i].buf = s->strategy; + break; + default: + params[i].status = Z_VERSION_ERROR; + version_error = 1; + break; + } + if (params[i].status == Z_BUF_ERROR) + buf_error = 1; + } + return buf_error ? Z_BUF_ERROR : (version_error ? Z_VERSION_ERROR : Z_OK); +} +#endif diff --git a/test/example.c b/test/example.c index 964a380d..f826b1ab 100644 --- a/test/example.c +++ b/test/example.c @@ -40,7 +40,7 @@ void test_compress (unsigned char *compr, z_size_t comprLen,unsigned char * void test_gzio (const char *fname, unsigned char *uncompr, z_size_t uncomprLen); void test_deflate (unsigned char *compr, size_t comprLen); void test_inflate (unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen); -void test_large_deflate (unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen); +void test_large_deflate (unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen, int zng_params); void test_large_inflate (unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen); void test_flush (unsigned char *compr, z_size_t *comprLen); void test_sync (unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen); @@ -241,10 +241,18 @@ static unsigned int diff; /* =========================================================================== * Test deflate() with large buffers and dynamic change of compression level */ -void test_large_deflate(unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen) +void test_large_deflate(unsigned char *compr, size_t comprLen, unsigned char *uncompr, size_t uncomprLen, int zng_params) { PREFIX3(stream) c_stream; /* compression stream */ int err; +#ifndef ZLIB_COMPAT + int level = -1; + int strategy = -1; + zng_deflate_param_value params[] = { + { .param = Z_DEFLATE_LEVEL, .buf = &level, .size = sizeof(level) }, + { .param = Z_DEFLATE_STRATEGY, .buf = &strategy, .size = sizeof(strategy) }, + }; +#endif c_stream.zalloc = zalloc; c_stream.zfree = zfree; @@ -269,7 +277,27 @@ void test_large_deflate(unsigned char *compr, size_t comprLen, unsigned char *un } /* Feed in already compressed data and switch to no compression: */ - PREFIX(deflateParams)(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + if (zng_params) { +#ifndef ZLIB_COMPAT + zng_deflateGetParams(&c_stream, params, sizeof(params) / sizeof(params[0])); + if (level != Z_BEST_SPEED) { + fprintf(stderr, "Expected compression level Z_BEST_SPEED, got %d\n", level); + exit(1); + } + if (strategy != Z_DEFAULT_STRATEGY) { + fprintf(stderr, "Expected compression strategy Z_DEFAULT_STRATEGY, got %d\n", strategy); + exit(1); + } + level = Z_NO_COMPRESSION; + strategy = Z_DEFAULT_STRATEGY; + zng_deflateSetParams(&c_stream, params, sizeof(params) / sizeof(params[0])); +#else + fprintf(stderr, "test_large_deflate() called with zng_params=1 in compat mode\n"); + exit(1); +#endif + } else { + PREFIX(deflateParams)(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + } c_stream.next_in = compr; diff = (unsigned int)(c_stream.next_out - compr); c_stream.avail_in = diff; @@ -277,7 +305,29 @@ void test_large_deflate(unsigned char *compr, size_t comprLen, unsigned char *un CHECK_ERR(err, "deflate"); /* Switch back to compressing mode: */ - PREFIX(deflateParams)(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + if (zng_params) { +#ifndef ZLIB_COMPAT + level = -1; + strategy = -1; + zng_deflateGetParams(&c_stream, params, sizeof(params) / sizeof(params[0])); + if (level != Z_NO_COMPRESSION) { + fprintf(stderr, "Expected compression level Z_NO_COMPRESSION, got %d\n", level); + exit(1); + } + if (strategy != Z_DEFAULT_STRATEGY) { + fprintf(stderr, "Expected compression strategy Z_DEFAULT_STRATEGY, got %d\n", strategy); + exit(1); + } + level = Z_BEST_COMPRESSION; + strategy = Z_FILTERED; + zng_deflateSetParams(&c_stream, params, sizeof(params) / sizeof(params[0])); +#else + fprintf(stderr, "test_large_deflate() called with zng_params=1 in compat mode\n"); + exit(1); +#endif + } else { + PREFIX(deflateParams)(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + } c_stream.next_in = uncompr; c_stream.avail_in = (unsigned int)uncomprLen; err = PREFIX(deflate)(&c_stream, Z_NO_FLUSH); @@ -535,8 +585,13 @@ int main(int argc, char *argv[]) test_deflate(compr, comprLen); test_inflate(compr, comprLen, uncompr, uncomprLen); - test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_deflate(compr, comprLen, uncompr, uncomprLen, 0); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + +#ifndef ZLIB_COMPAT + test_large_deflate(compr, comprLen, uncompr, uncomprLen, 1); test_large_inflate(compr, comprLen, uncompr, uncomprLen); +#endif test_flush(compr, &comprLen); test_sync(compr, comprLen, uncompr, uncomprLen); diff --git a/win32/zlib-ng.def b/win32/zlib-ng.def index fd1047f4..2525a861 100644 --- a/win32/zlib-ng.def +++ b/win32/zlib-ng.def @@ -17,6 +17,8 @@ EXPORTS zng_deflatePending zng_deflatePrime zng_deflateSetHeader + zng_deflateSetParams + zng_deflateGetParams zng_inflateSetDictionary zng_inflateGetDictionary zng_inflateSync diff --git a/zlib-ng.h b/zlib-ng.h index 3d972a0c..5662b27c 100644 --- a/zlib-ng.h +++ b/zlib-ng.h @@ -1790,6 +1790,46 @@ ZEXTERN int ZEXPORT zng_gzgetc_(gzFile file); /* backward compatibility */ #endif /* WITH_GZFILEOP */ +typedef enum { + Z_DEFLATE_LEVEL = 0, /* compression level, represented as an int */ + Z_DEFLATE_STRATEGY = 1, /* compression strategy, represented as an int */ +} zng_deflate_param; + +typedef struct { + zng_deflate_param param; /* parameter ID */ + void *buf; /* parameter value */ + size_t size; /* parameter value size */ + int status; /* result of the last set/get call */ +} zng_deflate_param_value; + +ZEXTERN int ZEXPORT zng_deflateSetParams(zng_stream *strm, zng_deflate_param_value *params, size_t count); +/* + Sets the values of the given zlib-ng deflate stream parameters. All the buffers are copied internally, so the + caller still owns them after this function returns. Returns Z_OK if success. + + If the size of at least one of the buffers is too small to hold the entire value of the corresponding parameter, + or if the same parameter is specified multiple times, Z_BUF_ERROR is returned. The caller may inspect status fields + in order to determine which of the parameters caused this error. No other changes are performed. + + If the stream state is inconsistent or if at least one of the values cannot be updated, Z_STREAM_ERROR is + returned. The caller may inspect status fields in order to determine which of the parameters caused this error. + Parameters, whose status field is equal to Z_OK, have been applied successfully. If all status fields are not equal + to Z_STREAM_ERROR, then the error was caused by a stream state inconsistency. + + If there are no other errors, but at least one parameter is not supported by the current zlib-ng version, + Z_VERSION_ERROR is returned. The caller may inspect status fields in order to determine which of the parameters + caused this error. +*/ + +ZEXTERN int ZEXPORT zng_deflateGetParams(zng_stream *strm, zng_deflate_param_value *params, size_t count); +/* + Copies the values of the given zlib-ng deflate stream parameters into the user-provided buffers. Returns Z_OK if + success, Z_VERSION_ERROR if at least one parameter is not supported by the current zlib-ng version, Z_STREAM_ERROR + if the stream state is inconsistent, and Z_BUF_ERROR if the size of at least one buffer is too small to hold the + entire value of the corresponding parameter. +*/ + + /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if * both are true, the application gets the *64 functions, and the regular diff --git a/zlib-ng.map b/zlib-ng.map index 107d0695..29744f5b 100644 --- a/zlib-ng.map +++ b/zlib-ng.map @@ -19,6 +19,7 @@ ZLIB_NG_1.9.9 { zng_deflateCopy; zng_deflateEnd; zng_deflateGetDictionary; + zng_deflateGetParams; zng_deflateInit_; zng_deflateInit2_; zng_deflateParams; @@ -28,6 +29,7 @@ ZLIB_NG_1.9.9 { zng_deflateResetKeep; zng_deflateSetDictionary; zng_deflateSetHeader; + zng_deflateSetParams; zng_deflateTune; zng_get_crc_table; zng_inflate;