Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zstd: Support negative compression-levels #1352

Merged
merged 1 commit into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 65 additions & 6 deletions libarchive/archive_write_add_filter_zstd.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,15 @@ struct private_data {
#endif
};

/* If we don't have the library use default range values (zstdcli.c v1.4.0) */
#define CLEVEL_MIN -99
#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */
#define CLEVEL_DEFAULT 3
#define CLEVEL_STD_MAX 19 /* without using --ultra */
#define CLEVEL_MAX 22

#define MINVER_NEGCLEVEL 10304
#define MINVER_MINCLEVEL 10306

static int archive_compressor_zstd_options(struct archive_write_filter *,
const char *, const char *);
Expand Down Expand Up @@ -98,7 +106,7 @@ archive_write_add_filter_zstd(struct archive *_a)
f->free = &archive_compressor_zstd_free;
f->code = ARCHIVE_FILTER_ZSTD;
f->name = "zstd";
data->compression_level = 3; /* Default level used by the zstd CLI */
data->compression_level = CLEVEL_DEFAULT;
#if HAVE_ZSTD_H && HAVE_LIBZSTD
data->cstream = ZSTD_createCStream();
if (data->cstream == NULL) {
Expand Down Expand Up @@ -137,6 +145,31 @@ archive_compressor_zstd_free(struct archive_write_filter *f)
return (ARCHIVE_OK);
}

static int string_is_numeric (const char* value)
{
size_t len = strlen(value);
size_t i;

if (len == 0) {
return (ARCHIVE_WARN);
}
else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {
return (ARCHIVE_WARN);
}
else if (!(value[0] >= '0' && value[0] <= '9') &&
value[0] != '-' && value[0] != '+') {
return (ARCHIVE_WARN);
}

for (i = 1; i < len; i++) {
if (!(value[i] >= '0' && value[i] <= '9')) {
return (ARCHIVE_WARN);
}
}

return (ARCHIVE_OK);
}

/*
* Set write options.
*/
Expand All @@ -148,12 +181,24 @@ archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,

if (strcmp(key, "compression-level") == 0) {
int level = atoi(value);
#if HAVE_ZSTD_H && HAVE_LIBZSTD
if (level < 1 || level > ZSTD_maxCLevel()) {
#else
/* If we don't have the library, hard-code the max level */
if (level < 1 || level > 22) {
int minimum = CLEVEL_MIN;
int maximum = CLEVEL_MAX;
if (string_is_numeric(value) != ARCHIVE_OK) {
return (ARCHIVE_WARN);
}
#if HAVE_ZSTD_H && HAVE_LIBZSTD
maximum = ZSTD_maxCLevel();
#endif
#if HAVE_ZSTD_H && HAVE_LIBZSTD && ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {
minimum = ZSTD_minCLevel();
}
else if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {
minimum = CLEVEL_STD_MIN;
}
#endif
if (level < minimum || level > maximum) {
return (ARCHIVE_WARN);
}
data->compression_level = level;
Expand Down Expand Up @@ -300,7 +345,21 @@ archive_compressor_zstd_open(struct archive_write_filter *f)

archive_string_init(&as);
/* --no-check matches library default */
archive_string_sprintf(&as, "zstd -%d --no-check", data->compression_level);
archive_strcpy(&as, "zstd --no-check");

if (data->compression_level < CLEVEL_STD_MIN) {
struct archive_string as2;
archive_string_init(&as2);
archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);
archive_string_concat(&as, &as2);
archive_string_free(&as2);
} else {
struct archive_string as2;
archive_string_init(&as2);
archive_string_sprintf(&as2, " -%d", data->compression_level);
archive_string_concat(&as, &as2);
archive_string_free(&as2);
}

if (data->compression_level > CLEVEL_STD_MAX) {
archive_strcat(&as, " --ultra");
Expand Down
3 changes: 2 additions & 1 deletion libarchive/archive_write_set_options.3
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ If supported, the default value is read from
.Bl -tag -compact -width indent
.It Cm compression-level
The value is interpreted as a decimal integer specifying the
compression level. Supported values are from 1 to 22.
compression level. Supported values depend on the library version,
common values are from 1 to 22.
.El
.It Format 7zip
.Bl -tag -compact -width indent
Expand Down
3 changes: 3 additions & 0 deletions libarchive/test/test_write_filter_zstd.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ DEFINE_TEST(test_write_filter_zstd)
archive_write_set_filter_option(a, NULL, "compression-level", "25")); /* too big */
assertEqualIntA(a, ARCHIVE_OK,
archive_write_set_filter_option(a, NULL, "compression-level", "9"));
/* Following is disabled as it will fail on library versions < 1.3.4 */
/* assertEqualIntA(a, ARCHIVE_OK,
archive_write_set_filter_option(a, NULL, "compression-level", "-1")); */
assertEqualIntA(a, ARCHIVE_OK,
archive_write_set_filter_option(a, NULL, "compression-level", "7"));
assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, buffsize, &used2));
Expand Down
3 changes: 2 additions & 1 deletion tar/bsdtar.1
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,8 @@ A decimal integer from 4 to 7 specifying the lz4 compression block size
Use the previous block of the block being compressed for
a compression dictionary to improve compression ratio.
.It Cm zstd:compression-level
A decimal integer from 1 to 22 specifying the zstd compression level.
A decimal integer specifying the zstd compression level. Supported values depend
on the library version, common values are from 1 to 22.
.It Cm lzop:compression-level
A decimal integer from 1 to 9 specifying the lzop compression level.
.It Cm xz:compression-level
Expand Down