diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db2ec42..64e343f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -359,6 +359,7 @@ list( src/dwarfs/builtin_script.cpp src/dwarfs/cached_block.cpp src/dwarfs/categorizer.cpp + src/dwarfs/category_parser.cpp src/dwarfs/checksum.cpp src/dwarfs/chmod_transformer.cpp src/dwarfs/console_writer.cpp @@ -371,6 +372,7 @@ list( src/dwarfs/filesystem_extractor.cpp src/dwarfs/filesystem_v2.cpp src/dwarfs/filesystem_writer.cpp + src/dwarfs/fragment_order_parser.cpp src/dwarfs/fstypes.cpp src/dwarfs/fs_section.cpp src/dwarfs/global_entry_data.cpp diff --git a/include/dwarfs/categorizer.h b/include/dwarfs/categorizer.h index d5ff747f..74fe6a2a 100644 --- a/include/dwarfs/categorizer.h +++ b/include/dwarfs/categorizer.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -124,6 +125,8 @@ class categorizer_manager { public: categorizer_manager(logger& lgr); + static fragment_category default_category(); + void add(std::shared_ptr c) { impl_->add(std::move(c)); } categorizer_job job(std::filesystem::path const& path) const { @@ -134,6 +137,11 @@ class categorizer_manager { return impl_->category_name(c); } + std::optional + category_value(std::string_view name) const { + return impl_->category_value(name); + } + folly::dynamic category_metadata(fragment_category c) const { return impl_->category_metadata(c); } @@ -146,6 +154,8 @@ class categorizer_manager { virtual categorizer_job job(std::filesystem::path const& path) const = 0; virtual std::string_view category_name(fragment_category::value_type c) const = 0; + virtual std::optional + category_value(std::string_view name) const = 0; virtual folly::dynamic category_metadata(fragment_category c) const = 0; }; diff --git a/include/dwarfs/category_parser.h b/include/dwarfs/category_parser.h new file mode 100644 index 00000000..e4dd690a --- /dev/null +++ b/include/dwarfs/category_parser.h @@ -0,0 +1,43 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of dwarfs. + * + * dwarfs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dwarfs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dwarfs. If not, see . + */ + +#pragma once + +#include +#include + +#include "dwarfs/fragment_category.h" + +namespace dwarfs { + +class categorizer_manager; + +class category_parser { + public: + category_parser(std::shared_ptr catmgr); + + std::vector parse(std::string_view arg) const; + + private: + std::shared_ptr catmgr_; +}; + +} // namespace dwarfs diff --git a/include/dwarfs/contextual_option.h b/include/dwarfs/contextual_option.h new file mode 100644 index 00000000..8b617613 --- /dev/null +++ b/include/dwarfs/contextual_option.h @@ -0,0 +1,158 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of dwarfs. + * + * dwarfs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dwarfs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dwarfs. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace dwarfs { + +template +class contextual_option { + public: + using policy_type = Policy; + using context_argument_type = typename policy_type::ContextArgumentType; + using context_type = typename policy_type::ContextType; + using option_type = typename policy_type::OptionType; + + contextual_option() = default; + explicit contextual_option(option_type const& def) + : default_{def} {} + + void set_default(option_type const& val) { default_ = val; } + + void add_contextual(context_type const& ctx, option_type const& val) { + contextual_[ctx] = val; + } + + std::optional + get_optional(context_argument_type const& arg) const { + if constexpr (std::is_same_v) { + return get_optional_impl(arg); + } else { + return get_optional_impl(policy_type::context_from_arg(arg)); + } + } + + option_type get(context_argument_type const& arg) const { + if constexpr (std::is_same_v) { + return get_impl(arg); + } else { + return get_impl(policy_type::context_from_arg(arg)); + } + } + + std::optional get_optional() const { return default_; } + + option_type get() const { return default_.value(); } + + template + bool any_is(T&& pred) const { + for (auto e : contextual_) { + if (pred(e.second)) { + return true; + } + } + return default_ && pred(*default_); + } + + private: + std::optional get_optional_impl(context_type const& ctx) const { + if (auto it = contextual_.find(ctx); it != contextual_.end()) { + return it->second; + } + return default_; + } + + option_type get_impl(context_type const& ctx) const { + if (auto it = contextual_.find(ctx); it != contextual_.end()) { + return it->second; + } + return default_.value(); + } + + std::optional default_; + std::unordered_map contextual_; +}; + +template +class contextual_option_parser { + public: + using option_type = OptionType; + using policy_type = typename option_type::policy_type; + + contextual_option_parser(OptionType& opt, ContextParser const& cp, + OptionParser const& op) + : opt_{opt} + , cp_{cp} + , op_{op} {} + + void parse(std::string_view arg) const { + try { + auto pos = arg.find("::"); + + if (pos == arg.npos) { + opt_.set_default(op_.parse(arg)); + } else { + auto ctx = arg.substr(0, pos); + auto val = op_.parse(arg.substr(pos + 2)); + if constexpr (std::is_same_v< + std::invoke_result_t, + typename option_type::context_type>) { + opt_.add_contextual(cp_.parse(ctx), val); + } else { + for (auto c : cp_.parse(ctx)) { + opt_.add_contextual(c, val); + } + } + } + } catch (std::exception const& e) { + throw std::runtime_error( + fmt::format("failed to parse: {} ({})", arg, e.what())); + } + } + + void parse(std::span list) const { + for (auto const& arg : list) { + parse(arg); + } + } + + void parse(std::span list) const { + for (auto const& arg : list) { + parse(arg); + } + } + + private: + OptionType& opt_; + ContextParser const& cp_; + OptionParser const& op_; +}; + +} // namespace dwarfs diff --git a/include/dwarfs/file_scanner.h b/include/dwarfs/file_scanner.h index 6b8509c4..a4d05f3c 100644 --- a/include/dwarfs/file_scanner.h +++ b/include/dwarfs/file_scanner.h @@ -40,7 +40,6 @@ namespace detail { class file_scanner { public: file_scanner(worker_group& wg, os_access& os, inode_manager& im, - inode_options const& ino_opts, std::optional const& hash_algo, progress& prog); void scan(file* p) { impl_->scan(p); } diff --git a/include/dwarfs/fragment_category.h b/include/dwarfs/fragment_category.h index 4bc307ee..4425bb8e 100644 --- a/include/dwarfs/fragment_category.h +++ b/include/dwarfs/fragment_category.h @@ -25,6 +25,8 @@ #include #include +#include + namespace dwarfs { class fragment_category { @@ -88,9 +90,26 @@ class fragment_category { return subcategory_; } + auto operator<=>(fragment_category const&) const = default; + + size_t hash() const { + return folly::hash::hash_combine(value_, subcategory_); + } + private: value_type value_{uninitialized}; value_type subcategory_{uninitialized}; }; } // namespace dwarfs + +namespace std { + +template <> +struct hash { + std::size_t operator()(dwarfs::fragment_category const& k) const { + return k.hash(); + } +}; + +} // namespace std diff --git a/include/dwarfs/fragment_order_parser.h b/include/dwarfs/fragment_order_parser.h new file mode 100644 index 00000000..86023bd8 --- /dev/null +++ b/include/dwarfs/fragment_order_parser.h @@ -0,0 +1,37 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/** + * \author Marcus Holland-Moritz (github@mhxnet.de) + * \copyright Copyright (c) Marcus Holland-Moritz + * + * This file is part of dwarfs. + * + * dwarfs is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * dwarfs is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with dwarfs. If not, see . + */ + +#pragma once + +#include + +#include "dwarfs/options.h" + +namespace dwarfs { + +struct fragment_order_parser { + public: + static std::string choices(); + + file_order_options parse(std::string_view arg) const; +}; + +} // namespace dwarfs diff --git a/include/dwarfs/inode.h b/include/dwarfs/inode.h index 703e113c..b05e67db 100644 --- a/include/dwarfs/inode.h +++ b/include/dwarfs/inode.h @@ -47,7 +47,6 @@ class inode : public object { using files_vector = folly::small_vector; virtual void set_files(files_vector&& fv) = 0; - virtual void set_similarity_valid(inode_options const& opts) = 0; virtual void scan(mmif* mm, inode_options const& options) = 0; virtual void set_num(uint32_t num) = 0; virtual uint32_t num() const = 0; diff --git a/include/dwarfs/inode_fragments.h b/include/dwarfs/inode_fragments.h index 7bc9eeb1..aa044cdf 100644 --- a/include/dwarfs/inode_fragments.h +++ b/include/dwarfs/inode_fragments.h @@ -65,10 +65,17 @@ class inode_fragments { std::span span() const { return fragments_; } + size_t size() const { return fragments_.size(); } + bool empty() const { return fragments_.empty(); } void clear() { fragments_.clear(); } + fragment_category get_single_category() const { + assert(fragments_.size() == 1); + return fragments_.at(0).category(); + } + explicit operator bool() const { return !empty(); } std::ostream& diff --git a/include/dwarfs/inode_manager.h b/include/dwarfs/inode_manager.h index 1678fb53..fc616eba 100644 --- a/include/dwarfs/inode_manager.h +++ b/include/dwarfs/inode_manager.h @@ -32,27 +32,29 @@ namespace dwarfs { +class file; class inode; class logger; +class os_access; class progress; class script; +class worker_group; -struct file_order_options; +struct inode_options; class inode_manager { public: using inode_cb = std::function const&)>; using order_cb = std::function const&)>; - inode_manager(logger& lgr, progress& prog); + inode_manager(logger& lgr, progress& prog, inode_options const& opts); std::shared_ptr create_inode() { return impl_->create_inode(); } size_t count() const { return impl_->count(); } - void order_inodes(std::shared_ptr