]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
zstd: Support negative compression-levels 1352/head
authorHarry Mallon <hjmallon@gmail.com>
Wed, 11 Mar 2020 12:47:49 +0000 (12:47 +0000)
committerHarry Mallon <harry.mallon@codex.online>
Tue, 7 Apr 2020 18:17:14 +0000 (19:17 +0100)
libarchive/archive_write_add_filter_zstd.c
libarchive/archive_write_set_options.3
libarchive/test/test_write_filter_zstd.c
tar/bsdtar.1

index e02d1c1cb65a4527fde3db0bf10fbea86e5b3c73..3ec52c5a0895030ef228352062035e69acc9d5c2 100644 (file)
@@ -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 *);
@@ -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) {
@@ -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.
  */
@@ -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;
@@ -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");
index cffe571e90a6118957f73105f6e4ef6d15a665d7..d4a52e322ea8f852149141abe8eeb66691888d0f 100644 (file)
@@ -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
index ba1b6bfe716b82b9dbe004f1d0eb6ae3efa92d84..b5f061a001aa77bc1fe5bd84b53e273bbb734e0a 100644 (file)
@@ -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));
index f1574234905c7b3e009fd7c5cf7818569d797114..86a06bbd13bbb41936030ceeaf728685aa2bff93 100644 (file)
@@ -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