diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Config.cpp | 14 | ||||
-rw-r--r-- | src/Config.hpp | 19 | ||||
-rw-r--r-- | src/core/FileRecompressor.cpp | 4 | ||||
-rw-r--r-- | src/core/Statistics.cpp | 15 | ||||
-rw-r--r-- | src/core/mainoptions.cpp | 90 | ||||
-rw-r--r-- | src/storage/local/LocalStorage.cpp | 119 | ||||
-rw-r--r-- | src/storage/local/LocalStorage.hpp | 10 | ||||
-rw-r--r-- | src/util/string.cpp | 48 | ||||
-rw-r--r-- | src/util/string.hpp | 14 |
9 files changed, 207 insertions, 126 deletions
diff --git a/src/Config.cpp b/src/Config.cpp index bf79acea..840925ca 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -247,9 +247,9 @@ format_bool(bool value) } std::string -format_cache_size(uint64_t value) +format_cache_size(uint64_t value, util::SizeUnitPrefixType prefix_type) { - return util::format_parsable_size_with_suffix(value); + return util::format_human_readable_size(value, prefix_type); } CompilerType @@ -792,7 +792,7 @@ Config::get_string_value(const std::string& key) const return FMT("{}", m_max_files); case ConfigItem::max_size: - return format_cache_size(m_max_size); + return format_cache_size(m_max_size, m_size_suffix_type); case ConfigItem::msvc_dep_prefix: return m_msvc_dep_prefix; @@ -1036,9 +1036,13 @@ Config::set_item(const std::string& key, util::parse_unsigned(value, std::nullopt, std::nullopt, "max_files")); break; - case ConfigItem::max_size: - m_max_size = util::value_or_throw<core::Error>(util::parse_size(value)); + case ConfigItem::max_size: { + const auto [size, prefix_type] = + util::value_or_throw<core::Error>(util::parse_size(value)); + m_max_size = size; + m_size_suffix_type = prefix_type; break; + } case ConfigItem::msvc_dep_prefix: m_msvc_dep_prefix = Util::expand_environment_variables(value); diff --git a/src/Config.hpp b/src/Config.hpp index 43c6a7fc..90569667 100644 --- a/src/Config.hpp +++ b/src/Config.hpp @@ -21,6 +21,7 @@ #include "NonCopyable.hpp" #include <core/Sloppiness.hpp> +#include <util/string.hpp> #include <sys/types.h> @@ -102,6 +103,7 @@ public: // Return true for MSVC (cl.exe), clang-cl, and icl. bool is_compiler_group_msvc() const; + util::SizeUnitPrefixType size_unit_prefix_type() const; std::string default_temporary_dir() const; void set_base_dir(const std::string& value); @@ -117,7 +119,6 @@ public: void set_ignore_options(const std::string& value); void set_inode_cache(bool value); void set_max_files(uint64_t value); - void set_max_size(uint64_t value); void set_msvc_dep_prefix(const std::string& value); void set_run_second_cpp(bool value); void set_temporary_dir(const std::string& value); @@ -190,7 +191,7 @@ private: bool m_keep_comments_cpp = false; std::string m_log_file; uint64_t m_max_files = 0; - uint64_t m_max_size = 5ULL * 1000 * 1000 * 1000; + uint64_t m_max_size = 5ULL * 1024 * 1024 * 1024; std::string m_msvc_dep_prefix = "Note: including file:"; std::string m_path; bool m_pch_external_checksum = false; @@ -211,6 +212,8 @@ private: std::optional<mode_t> m_umask; bool m_temporary_dir_configured_explicitly = false; + util::SizeUnitPrefixType m_size_suffix_type = + util::SizeUnitPrefixType::binary; std::unordered_map<std::string /*key*/, std::string /*origin*/> m_origins; @@ -494,6 +497,12 @@ Config::umask() const return m_umask; } +inline util::SizeUnitPrefixType +Config::size_unit_prefix_type() const +{ + return m_size_suffix_type; +} + inline void Config::set_base_dir(const std::string& value) { @@ -576,12 +585,6 @@ Config::set_max_files(uint64_t value) } inline void -Config::set_max_size(uint64_t value) -{ - m_max_size = value; -} - -inline void Config::set_msvc_dep_prefix(const std::string& value) { m_msvc_dep_prefix = value; diff --git a/src/core/FileRecompressor.cpp b/src/core/FileRecompressor.cpp index 636d6761..92aa6c7a 100644 --- a/src/core/FileRecompressor.cpp +++ b/src/core/FileRecompressor.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Joel Rosdahl and other contributors +// Copyright (C) 2022-2023 Joel Rosdahl and other contributors // // See doc/AUTHORS.adoc for a complete list of contributors. // @@ -64,7 +64,7 @@ FileRecompressor::recompress(const Stat& stat, util::set_timestamps(stat.path(), stat.mtime(), stat.atime()); } - m_content_size += header.entry_size; + m_content_size += util::likely_size_on_disk(header.entry_size); m_old_size += stat.size_on_disk(); m_new_size += (new_stat ? *new_stat : stat).size_on_disk(); diff --git a/src/core/Statistics.cpp b/src/core/Statistics.cpp index 459d1f13..aa928a72 100644 --- a/src/core/Statistics.cpp +++ b/src/core/Statistics.cpp @@ -305,7 +305,13 @@ Statistics::format_human_readable(const Config& config, add_ratio_row(table, " Preprocessed:", p_hits, p_hits + p_misses); } - const uint64_t g = 1'000'000'000; + const char* size_unit = + config.size_unit_prefix_type() == util::SizeUnitPrefixType::binary ? "GiB" + : "GB"; + const uint64_t size_divider = + config.size_unit_prefix_type() == util::SizeUnitPrefixType::binary + ? 1024 * 1024 * 1024 + : 1000 * 1000 * 1000; const uint64_t local_hits = S(local_storage_hit); const uint64_t local_misses = S(local_storage_miss); const uint64_t local_reads = @@ -326,12 +332,13 @@ Statistics::format_human_readable(const Config& config, } if (!from_log) { std::vector<C> size_cells{ - " Cache size (GB):", - C(FMT("{:.1f}", static_cast<double>(local_size) / g)).right_align()}; + FMT(" Cache size ({}):", size_unit), + C(FMT("{:.1f}", static_cast<double>(local_size) / size_divider)) + .right_align()}; if (config.max_size() != 0) { size_cells.emplace_back("/"); size_cells.emplace_back( - C(FMT("{:.1f}", static_cast<double>(config.max_size()) / g)) + C(FMT("{:.1f}", static_cast<double>(config.max_size()) / size_divider)) .right_align()); size_cells.emplace_back(percent(local_size, config.max_size())); } diff --git a/src/core/mainoptions.cpp b/src/core/mainoptions.cpp index 1fba40db..bf2f3deb 100644 --- a/src/core/mainoptions.cpp +++ b/src/core/mainoptions.cpp @@ -28,6 +28,7 @@ #include <TemporaryFile.hpp> #include <ThreadPool.hpp> #include <UmaskScope.hpp> +#include <assertions.hpp> #include <ccache.hpp> #include <core/CacheEntry.hpp> #include <core/FileRecompressor.hpp> @@ -112,8 +113,9 @@ Common options: -F, --max-files NUM set maximum number of files in cache to NUM (use 0 for no limit) -M, --max-size SIZE set maximum size of cache to SIZE (use 0 for no - limit); available suffixes: k, M, G, T (decimal) - and Ki, Mi, Gi, Ti (binary); default suffix: G + limit); available suffixes: kB, MB, GB, TB + (decimal) and KiB, MiB, GiB, TiB (binary); + default suffix: GiB -X, --recompress LEVEL recompress the cache to level LEVEL (integer or "uncompressed") --recompress-threads THREADS @@ -140,8 +142,8 @@ Options for remote file-based storage: (note: don't use this option to trim the local cache) --trim-max-size SIZE specify the maximum size for --trim-dir; - available suffixes: k, M, G, T (decimal) and Ki, - Mi, Gi, Ti (binary); default suffix: G + available suffixes: kB, MB, GB, TB (decimal) and + KiB, MiB, GiB, TiB (binary); default suffix: GiB --trim-method METHOD specify the method (atime or mtime) for --trim-dir; default: atime --trim-recompress LEVEL @@ -233,39 +235,61 @@ inspect_path(const std::string& path) } static void -print_compression_statistics(const storage::local::CompressionStatistics& cs) +print_compression_statistics(const Config& config, + const storage::local::CompressionStatistics& cs) { - const double ratio = cs.compr_size > 0 - ? static_cast<double>(cs.content_size) / cs.compr_size + const double ratio = cs.actual_size > 0 + ? static_cast<double>(cs.content_size) / cs.actual_size : 0.0; const double savings = ratio > 0.0 ? 100.0 - (100.0 / ratio) : 0.0; + auto human_readable = [&](uint64_t size) { + return util::format_human_readable_size(size, + config.size_unit_prefix_type()); + }; + + const auto [total_data_quantity, total_data_unit] = util::split_once( + human_readable(cs.actual_size + cs.incompressible_size), ' '); + ASSERT(total_data_unit); + const auto [compressed_data_quantity, compressed_data_unit] = + util::split_once(human_readable(cs.actual_size), ' '); + ASSERT(compressed_data_unit); + const auto [original_data_quantity, original_data_unit] = + util::split_once(human_readable(cs.content_size), ' '); + ASSERT(original_data_unit); + const auto [incompressible_data_quantity, incompressible_data_unit] = + util::split_once(human_readable(cs.incompressible_size), ' '); + ASSERT(incompressible_data_unit); + using C = util::TextTable::Cell; - auto human_readable = util::format_human_readable_size; util::TextTable table; table.add_row({ "Total data:", - C(human_readable(cs.compr_size + cs.incompr_size)).right_align(), - FMT("({} disk blocks)", human_readable(cs.on_disk_size)), + C(total_data_quantity).right_align(), + *total_data_unit, }); table.add_row({ "Compressed data:", - C(human_readable(cs.compr_size)).right_align(), + C(compressed_data_quantity).right_align(), + *compressed_data_unit, FMT("({:.1f}% of original size)", 100.0 - savings), }); table.add_row({ " Original size:", - C(human_readable(cs.content_size)).right_align(), + C(original_data_quantity).right_align(), + *original_data_unit, }); table.add_row({ " Compression ratio:", - C(FMT("{:.3f} x ", ratio)).right_align(), + C(FMT("{:.3f}", ratio)).right_align(), + "x", FMT("({:.1f}% space savings)", savings), }); table.add_row({ "Incompressible data:", - C(human_readable(cs.incompr_size)).right_align(), + C(incompressible_data_quantity).right_align(), + *incompressible_data_unit, }); PRINT_RAW(stdout, table.render()); @@ -274,6 +298,7 @@ print_compression_statistics(const storage::local::CompressionStatistics& cs) static void trim_dir(const std::string& dir, const uint64_t trim_max_size, + const util::SizeUnitPrefixType suffix_type, const bool trim_lru_mtime, std::optional<std::optional<int8_t>> recompress_level, uint32_t recompress_threads) @@ -329,11 +354,11 @@ trim_dir(const std::string& dir, recompression_diff = recompressor.new_size() - recompressor.old_size(); PRINT(stdout, "Recompressed {} to {} ({})\n", - util::format_human_readable_size(incompressible_size - + recompressor.old_size()), - util::format_human_readable_size(incompressible_size - + recompressor.new_size()), - util::format_human_readable_diff(recompression_diff)); + util::format_human_readable_size( + incompressible_size + recompressor.old_size(), suffix_type), + util::format_human_readable_size( + incompressible_size + recompressor.new_size(), suffix_type), + util::format_human_readable_diff(recompression_diff, suffix_type)); } uint64_t size_after_recompression = initial_size + recompression_diff; @@ -352,9 +377,10 @@ trim_dir(const std::string& dir, PRINT(stdout, "Trimmed {} to {} ({}, {}{} file{})\n", - util::format_human_readable_size(size_after_recompression), - util::format_human_readable_size(final_size), - util::format_human_readable_diff(final_size - size_after_recompression), + util::format_human_readable_size(size_after_recompression, suffix_type), + util::format_human_readable_size(final_size, suffix_type), + util::format_human_readable_diff(final_size - size_after_recompression, + suffix_type), removed_files == 0 ? "" : "-", removed_files, removed_files == 1 ? "" : "s"); @@ -452,6 +478,7 @@ process_main_options(int argc, const char* const* argv) uint8_t verbosity = 0; std::optional<uint64_t> trim_max_size; + std::optional<util::SizeUnitPrefixType> trim_suffix_type; bool trim_lru_mtime = false; std::optional<std::optional<int8_t>> trim_recompress; uint32_t trim_recompress_threads = std::thread::hardware_concurrency(); @@ -484,9 +511,13 @@ process_main_options(int argc, const char* const* argv) arg, 1, std::numeric_limits<uint32_t>::max(), "threads")); break; - case TRIM_MAX_SIZE: - trim_max_size = util::value_or_throw<Error>(util::parse_size(arg)); + case TRIM_MAX_SIZE: { + auto [size, suffix_type] = + util::value_or_throw<Error>(util::parse_size(arg)); + trim_max_size = size; + trim_suffix_type = suffix_type; break; + } case TRIM_METHOD: trim_lru_mtime = (arg == "ctime"); @@ -657,14 +688,16 @@ process_main_options(int argc, const char* const* argv) } case 'M': { // --max-size - uint64_t size = util::value_or_throw<Error>(util::parse_size(arg)); + auto [size, suffix_type] = + util::value_or_throw<Error>(util::parse_size(arg)); + uint64_t max_size = size; config.set_value_in_file(config.config_path(), "max_size", arg); - if (size == 0) { + if (max_size == 0) { PRINT_RAW(stdout, "Unset cache size limit\n"); } else { PRINT(stdout, "Set cache size limit to {}\n", - util::format_human_readable_size(size)); + util::format_human_readable_size(max_size, suffix_type)); } break; } @@ -715,6 +748,7 @@ process_main_options(int argc, const char* const* argv) } trim_dir(arg, *trim_max_size, + *trim_suffix_type, trim_lru_mtime, trim_recompress, trim_recompress_threads); @@ -739,7 +773,7 @@ process_main_options(int argc, const char* const* argv) if (isatty(STDOUT_FILENO)) { PRINT_RAW(stdout, "\n\n"); } - print_compression_statistics(compression_statistics); + print_compression_statistics(config, compression_statistics); break; } diff --git a/src/storage/local/LocalStorage.cpp b/src/storage/local/LocalStorage.cpp index 706d1eae..fe423989 100644 --- a/src/storage/local/LocalStorage.cpp +++ b/src/storage/local/LocalStorage.cpp @@ -39,6 +39,7 @@ #include <storage/local/StatsFile.hpp> #include <storage/local/util.hpp> #include <util/Duration.hpp> +#include <util/TextTable.hpp> #include <util/expected.hpp> #include <util/file.hpp> #include <util/string.hpp> @@ -51,6 +52,7 @@ #include <algorithm> #include <atomic> +#include <cstdlib> #include <memory> #include <numeric> #include <string> @@ -668,13 +670,12 @@ LocalStorage::get_compression_statistics( for (size_t i = 0; i < files.size(); ++i) { const auto& cache_file = files[i]; - cs.on_disk_size += cache_file.size_on_disk(); try { core::CacheEntry::Header header(cache_file.path()); - cs.compr_size += cache_file.size(); - cs.content_size += header.entry_size; + cs.actual_size += cache_file.size_on_disk(); + cs.content_size += util::likely_size_on_disk(header.entry_size); } catch (core::Error&) { - cs.incompr_size += cache_file.size(); + cs.incompressible_size += cache_file.size_on_disk(); } l2_progress_receiver(0.2 + 0.8 * i / files.size()); } @@ -775,42 +776,69 @@ LocalStorage::recompress(const std::optional<int8_t> level, : 0.0; const double new_savings = new_ratio > 0.0 ? 100.0 - (100.0 / new_ratio) : 0.0; - const int64_t size_difference = - static_cast<int64_t>(recompressor.new_size()) - - static_cast<int64_t>(recompressor.old_size()); - - const std::string old_compr_size_str = - util::format_human_readable_size(recompressor.old_size()); - const std::string new_compr_size_str = - util::format_human_readable_size(recompressor.new_size()); - const std::string content_size_str = - util::format_human_readable_size(recompressor.content_size()); - const std::string incompr_size_str = - util::format_human_readable_size(incompressible_size); - const std::string size_difference_str = - FMT("{}{}", - size_difference < 0 ? "-" : (size_difference > 0 ? "+" : " "), - util::format_human_readable_size( - size_difference < 0 ? -size_difference : size_difference)); - - PRINT(stdout, "Original data: {:>8s}\n", content_size_str); - PRINT(stdout, - "Old compressed data: {:>8s} ({:.1f}% of original size)\n", - old_compr_size_str, - 100.0 - old_savings); - PRINT(stdout, - " - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", - old_ratio, - old_savings); - PRINT(stdout, - "New compressed data: {:>8s} ({:.1f}% of original size)\n", - new_compr_size_str, - 100.0 - new_savings); - PRINT(stdout, - " - Compression ratio: {:>5.3f} x ({:.1f}% space savings)\n", - new_ratio, - new_savings); - PRINT(stdout, "Size change: {:>9s}\n", size_difference_str); + const int64_t size_diff = static_cast<int64_t>(recompressor.new_size()) + - static_cast<int64_t>(recompressor.old_size()); + + auto human_readable = [&](uint64_t size) { + return util::format_human_readable_size(size, + m_config.size_unit_prefix_type()); + }; + + const auto [old_compr_size_quantity, old_compr_size_unit] = + util::split_once(human_readable(recompressor.old_size()), ' '); + ASSERT(old_compr_size_unit); + const auto [new_compr_size_quantity, new_compr_size_unit] = + util::split_once(human_readable(recompressor.new_size()), ' '); + ASSERT(new_compr_size_unit); + const auto [content_size_quantity, content_size_unit] = + util::split_once(human_readable(recompressor.content_size()), ' '); + ASSERT(content_size_unit); + const auto [incompr_size_quantity, incompr_size_unit] = + util::split_once(human_readable(incompressible_size), ' '); + ASSERT(incompr_size_unit); + const auto [size_diff_quantity, size_diff_unit] = + util::split_once(human_readable(std::abs(size_diff)), ' '); + ASSERT(size_diff_unit); + + using C = util::TextTable::Cell; + util::TextTable table; + + table.add_row({ + "Original data:", + C(content_size_quantity).right_align(), + *content_size_unit, + }); + table.add_row({ + "Old compressed data:", + C(old_compr_size_quantity).right_align(), + *old_compr_size_unit, + FMT("({:.1f}% of original size)", 100.0 - old_savings), + }); + table.add_row({ + " Compression ratio:", + C(FMT("{:5.3f}", old_ratio)).right_align(), + "x", + FMT("({:.1f}% space savings)", old_savings), + }); + table.add_row({ + "New compressed data:", + C(new_compr_size_quantity).right_align(), + *new_compr_size_unit, + FMT("({:.1f}% of original size)", 100.0 - new_savings), + }); + table.add_row({ + " Compression ratio:", + C(FMT("{:5.3f}", new_ratio)).right_align(), + "x", + FMT("({:.1f}% space savings)", new_savings), + }); + table.add_row({ + "Size change:", + C(FMT("{}{}", size_diff < 0 ? "-" : "", size_diff_quantity)).right_align(), + *size_diff_unit, + }); + + PRINT_RAW(stdout, table.render()); } // Private methods @@ -1173,13 +1201,16 @@ LocalStorage::evaluate_cleanup() }); std::string max_size_str = - m_config.max_size() > 0 ? FMT( - ", max size {}", util::format_human_readable_size(m_config.max_size())) - : ""; + m_config.max_size() > 0 + ? FMT(", max size {}", + util::format_human_readable_size(m_config.max_size(), + m_config.size_unit_prefix_type())) + : ""; std::string max_files_str = m_config.max_files() > 0 ? FMT(", max files {}", m_config.max_files()) : ""; std::string info_str = FMT("size {}, files {}{}{}", - util::format_human_readable_size(total_size), + util::format_human_readable_size( + total_size, m_config.size_unit_prefix_type()), total_files, max_size_str, max_files_str); diff --git a/src/storage/local/LocalStorage.hpp b/src/storage/local/LocalStorage.hpp index 4989ef09..29f8a028 100644 --- a/src/storage/local/LocalStorage.hpp +++ b/src/storage/local/LocalStorage.hpp @@ -42,10 +42,14 @@ namespace storage::local { struct CompressionStatistics { - uint64_t compr_size; + // Storage that would be needed to store the content of compressible entries + // uncompressed (without headers), rounded up to disk blocks. uint64_t content_size; - uint64_t incompr_size; - uint64_t on_disk_size; + // Actual size of compressible entries (including headers), rounded up to disk + // blocks. + uint64_t actual_size; + // Actual size of incompressible entries, rounded up to disk blocks. + uint64_t incompressible_size; }; enum class FileType { result, manifest, raw, unknown }; diff --git a/src/util/string.cpp b/src/util/string.cpp index c33bd9f5..48d4b8b0 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -28,21 +28,25 @@ namespace util { std::string -format_human_readable_diff(int64_t diff) +format_human_readable_diff(int64_t diff, SizeUnitPrefixType prefix_type) { const char* sign = diff == 0 ? "" : (diff > 0 ? "+" : "-"); - return FMT("{}{}", sign, format_human_readable_size(std::abs(diff))); + return FMT( + "{}{}", sign, format_human_readable_size(std::abs(diff), prefix_type)); } std::string -format_human_readable_size(uint64_t size) +format_human_readable_size(uint64_t size, SizeUnitPrefixType prefix_type) { - if (size >= 1000 * 1000 * 1000) { - return FMT("{:.1f} GB", size / ((double)(1000 * 1000 * 1000))); - } else if (size >= 1000 * 1000) { - return FMT("{:.1f} MB", size / ((double)(1000 * 1000))); - } else if (size >= 1000) { - return FMT("{:.1f} kB", size / 1000.0); + const double factor = prefix_type == SizeUnitPrefixType::binary ? 1024 : 1000; + const char* infix = prefix_type == SizeUnitPrefixType::binary ? "i" : ""; + if (size >= factor * factor * factor) { + return FMT("{:.1f} G{}B", size / (factor * factor * factor), infix); + } else if (size >= factor * factor) { + return FMT("{:.1f} M{}B", size / (factor * factor), infix); + } else if (size >= factor) { + const char* k = prefix_type == SizeUnitPrefixType::binary ? "K" : "k"; + return FMT("{:.1f} {}{}B", size / factor, k, infix); } else if (size == 1) { return "1 byte"; } else { @@ -50,18 +54,6 @@ format_human_readable_size(uint64_t size) } } -std::string -format_parsable_size_with_suffix(uint64_t size) -{ - if (size >= 1000 * 1000 * 1000) { - return FMT("{:.1f}G", size / ((double)(1000 * 1000 * 1000))); - } else if (size >= 1000 * 1000) { - return FMT("{:.1f}M", size / ((double)(1000 * 1000))); - } else { - return FMT("{}", size); - } -} - nonstd::expected<double, std::string> parse_double(const std::string& value) { @@ -114,7 +106,7 @@ parse_signed(std::string_view value, } } -nonstd::expected<uint64_t, std::string> +nonstd::expected<std::pair<uint64_t, SizeUnitPrefixType>, std::string> parse_size(const std::string& value) { errno = 0; @@ -129,8 +121,12 @@ parse_size(const std::string& value) ++p; } + SizeUnitPrefixType prefix_type; if (*p != '\0') { - unsigned multiplier = *(p + 1) == 'i' ? 1024 : 1000; + prefix_type = *(p + 1) == 'i' ? SizeUnitPrefixType::binary + : SizeUnitPrefixType::decimal; + unsigned multiplier = + prefix_type == SizeUnitPrefixType::binary ? 1024 : 1000; switch (*p) { case 'T': result *= multiplier; @@ -149,11 +145,11 @@ parse_size(const std::string& value) return nonstd::make_unexpected(FMT("invalid size: \"{}\"", value)); } } else { - // Default suffix: G. - result *= 1000 * 1000 * 1000; + result *= 1024 * 1024 * 1024; + prefix_type = SizeUnitPrefixType::binary; } - return static_cast<uint64_t>(result); + return std::make_pair(static_cast<uint64_t>(result), prefix_type); } nonstd::expected<mode_t, std::string> diff --git a/src/util/string.hpp b/src/util/string.hpp index 3b23fa4e..6e1fcf4d 100644 --- a/src/util/string.hpp +++ b/src/util/string.hpp @@ -36,17 +36,18 @@ namespace util { // --- Interface --- +enum class SizeUnitPrefixType { binary, decimal }; + // Return true if `suffix` is a suffix of `string`. bool ends_with(std::string_view string, std::string_view suffix); // Format `diff` as a human-readable string. -std::string format_human_readable_diff(int64_t diff); +std::string format_human_readable_diff(int64_t diff, + SizeUnitPrefixType prefix_type); // Format `size` as a human-readable string. -std::string format_human_readable_size(uint64_t size); - -// Format `size` as a parsable string. -std::string format_parsable_size_with_suffix(uint64_t size); +std::string format_human_readable_size(uint64_t size, + SizeUnitPrefixType prefix_type); // Join stringified elements of `container` delimited by `delimiter` into a // string. There must exist an `std::string to_string(T::value_type)` function. @@ -80,7 +81,8 @@ parse_signed(std::string_view value, // Parse a "size value", i.e. a string that can end in k, M, G, T (10-based // suffixes) or Ki, Mi, Gi, Ti (2-based suffixes). For backward compatibility, K // is also recognized as a synonym of k. -nonstd::expected<uint64_t, std::string> parse_size(const std::string& value); +nonstd::expected<std::pair<uint64_t, util::SizeUnitPrefixType>, std::string> +parse_size(const std::string& value); // Parse `value` (an octal integer). nonstd::expected<mode_t, std::string> parse_umask(std::string_view value); |