From ab3e199f798c5fd5aebce48149f269f12d762e8d Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Wed, 20 Nov 2024 22:24:06 +0100 Subject: [PATCH] feat(lzma): support more lzma options (mode, mf, nice, depth) --- include/dwarfs/option_map.h | 14 ++++ src/compression/lzma.cpp | 126 +++++++++++++++++++++++++++--------- 2 files changed, 109 insertions(+), 31 deletions(-) diff --git a/include/dwarfs/option_map.h b/include/dwarfs/option_map.h index f60facac8..bc41ba45f 100644 --- a/include/dwarfs/option_map.h +++ b/include/dwarfs/option_map.h @@ -21,6 +21,7 @@ #pragma once +#include #include #include #include @@ -50,6 +51,19 @@ class option_map { return default_value; } + template + std::optional get_optional(const std::string& key) { + auto i = opt_.find(key); + + if (i != opt_.end()) { + std::string val = i->second; + opt_.erase(i); + return to(val); + } + + return std::nullopt; + } + size_t get_size(const std::string& key, size_t default_value = 0); void report(); diff --git a/src/compression/lzma.cpp b/src/compression/lzma.cpp index d477da8cb..4f6ad19d2 100644 --- a/src/compression/lzma.cpp +++ b/src/compression/lzma.cpp @@ -21,11 +21,16 @@ #include #include +#include #include #include +#include +#include +#include + #include #include #include @@ -51,6 +56,48 @@ std::unordered_map const lzma_error_desc{ // {LZMA_SEEK_NEEDED, "request to change the input file position"}, }; +std::array, 6> constexpr kBinaryModes{{ + {"x86", LZMA_FILTER_X86}, + {"powerpc", LZMA_FILTER_POWERPC}, + {"ia64", LZMA_FILTER_IA64}, + {"arm", LZMA_FILTER_ARM}, + {"armthumb", LZMA_FILTER_ARMTHUMB}, + {"sparc", LZMA_FILTER_SPARC}, +}}; + +std::array, + 2> constexpr kCompressionModes{{ + {"fast", LZMA_MODE_FAST}, + {"normal", LZMA_MODE_NORMAL}, +}}; + +std::array, + 5> constexpr kMatchFinders{{ + {"hc3", LZMA_MF_HC3}, + {"hc4", LZMA_MF_HC4}, + {"bt2", LZMA_MF_BT2}, + {"bt3", LZMA_MF_BT3}, + {"bt4", LZMA_MF_BT4}, +}}; + +template +T find_option(std::array, N> const& options, + std::string_view name, std::string_view what) { + for (auto const& [key, value] : options) { + if (key == name) { + return value; + } + } + DWARFS_THROW(runtime_error, fmt::format("unknown {} '{}'", what, name)); +} + +template +std::string +option_names(std::array, N> const& options) { + return options | ranges::views::keys | ranges::views::join(", ") | + ranges::to; +} + std::string lzma_error_string(lzma_ret err) { if (auto it = lzma_error_desc.find(err); it != lzma_error_desc.end()) { return it->second; @@ -60,8 +107,7 @@ std::string lzma_error_string(lzma_ret err) { class lzma_block_compressor final : public block_compressor::impl { public: - lzma_block_compressor(unsigned level, bool extreme, - const std::string& binary_mode, unsigned dict_size); + explicit lzma_block_compressor(option_map& om); lzma_block_compressor(const lzma_block_compressor& rhs) = default; std::unique_ptr clone() const override { @@ -102,24 +148,12 @@ class lzma_block_compressor final : public block_compressor::impl { return preset; } - static lzma_vli get_vli(const std::string& binary) { - if (binary.empty()) { + static lzma_vli get_vli(std::optional binary) { + if (!binary) { return LZMA_VLI_UNKNOWN; } - std::unordered_map vm{ - {"x86", LZMA_FILTER_X86}, {"powerpc", LZMA_FILTER_POWERPC}, - {"ia64", LZMA_FILTER_IA64}, {"arm", LZMA_FILTER_ARM}, - {"armthumb", LZMA_FILTER_ARMTHUMB}, {"sparc", LZMA_FILTER_SPARC}, - }; - - auto i = vm.find(binary); - - if (i == vm.end()) { - DWARFS_THROW(runtime_error, "unsupported binary mode"); - } - - return i->second; + return find_option(kBinaryModes, *binary, "binary mode"); } lzma_options_lzma opt_lzma_; @@ -127,20 +161,48 @@ class lzma_block_compressor final : public block_compressor::impl { std::string description_; }; -lzma_block_compressor::lzma_block_compressor(unsigned level, bool extreme, - const std::string& binary_mode, - unsigned dict_size) - : binary_vli_{get_vli(binary_mode)} - , description_{ - fmt::format("lzma [level={}, dict_size={}{}{}]", level, dict_size, - extreme ? ", extreme" : "", - binary_mode.empty() ? "" : ", binary=" + binary_mode)} { +lzma_block_compressor::lzma_block_compressor(option_map& om) { + auto level = om.get("level", 9u); + auto extreme = om.get("extreme", false); + auto binary_mode = om.get_optional("binary"); + auto dict_size = om.get_optional("dict_size"); + auto mode = om.get_optional("mode"); + auto mf = om.get_optional("mf"); + auto nice = om.get_optional("nice"); + auto depth = om.get_optional("depth"); + + description_ = fmt::format( + "lzma [level={}{}{}{}{}{}{}{}]", level, + dict_size ? ", dict_size=" + std::to_string(*dict_size) : "", + extreme ? ", extreme" : "", binary_mode ? ", binary=" + *binary_mode : "", + mode ? ", mode=" + *mode : "", mf ? ", mf=" + *mf : "", + nice ? ", nice=" + std::to_string(*nice) : "", + depth ? ", depth=" + std::to_string(*depth) : ""); + + binary_vli_ = get_vli(binary_mode); + if (lzma_lzma_preset(&opt_lzma_, get_preset(level, extreme))) { DWARFS_THROW(runtime_error, "unsupported preset, possibly a bug"); } - if (dict_size > 0) { - opt_lzma_.dict_size = 1 << dict_size; + if (dict_size) { + opt_lzma_.dict_size = 1 << *dict_size; + } + + if (mode) { + opt_lzma_.mode = find_option(kCompressionModes, *mode, "compression mode"); + } + + if (mf) { + opt_lzma_.mf = find_option(kMatchFinders, *mf, "match finder"); + } + + if (nice) { + opt_lzma_.nice_len = *nice; + } + + if (depth) { + opt_lzma_.depth = *depth; } } @@ -367,9 +429,7 @@ class lzma_compression_factory : public compression_factory { std::unique_ptr make_compressor(option_map& om) const override { - return std::make_unique( - om.get("level", 9u), om.get("extreme", false), - om.get("binary"), om.get("dict_size", 0u)); + return std::make_unique(om); } std::unique_ptr @@ -384,7 +444,11 @@ class lzma_compression_factory : public compression_factory { "level=[0..9]", "dict_size=[12..30]", "extreme", - "binary={x86,powerpc,ia64,arm,armthumb,sparc}", + "binary={" + option_names(kBinaryModes) + "}", + "mode={" + option_names(kCompressionModes) + "}", + "mf={" + option_names(kMatchFinders) + "}", + "nice=[0..273]", + "depth=[0..4294967295]", }; };