From 2e32789193fb4141de27a9e8b0c8151d9c7b9bd6 Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Thu, 9 Nov 2023 08:39:06 -0600 Subject: [PATCH] refactor: add libtransmission::Values (#6215) --- cli/cli.cc | 29 -- daemon/daemon.cc | 21 -- libtransmission/CMakeLists.txt | 1 + libtransmission/utils.cc | 335 ++++++------------ libtransmission/utils.h | 54 +-- libtransmission/values.h | 259 ++++++++++++++ tests/libtransmission/CMakeLists.txt | 1 + tests/libtransmission/test-fixtures.h | 34 -- tests/libtransmission/values-test.cc | 46 +++ .../utils/assets/bitcomet.utf-8.special.show | 6 +- .../assets/bittorrent-v2-hybrid-test.show | 2 +- utils/create.cc | 5 - utils/remote.cc | 22 -- utils/show.cc | 5 - utils/units.h | 26 -- 15 files changed, 439 insertions(+), 407 deletions(-) create mode 100644 libtransmission/values.h create mode 100644 tests/libtransmission/values-test.cc delete mode 100644 utils/units.h diff --git a/cli/cli.cc b/cli/cli.cc index 621d1abf9..04dd34461 100644 --- a/cli/cli.cc +++ b/cli/cli.cc @@ -26,32 +26,7 @@ using namespace std::chrono_literals; -/*** -**** -***/ - -static auto constexpr MemK = size_t{ 1024 }; -static char constexpr MemKStr[] = "KiB"; -static char constexpr MemMStr[] = "MiB"; -static char constexpr MemGStr[] = "GiB"; -static char constexpr MemTStr[] = "TiB"; - -static auto constexpr DiskK = size_t{ 1000 }; -static char constexpr DiskKStr[] = "kB"; -static char constexpr DiskMStr[] = "MB"; -static char constexpr DiskGStr[] = "GB"; -static char constexpr DiskTStr[] = "TB"; - -static auto constexpr SpeedK = size_t{ 1000 }; #define SPEED_K_STR "kB/s" -static char constexpr SpeedKStr[] = SPEED_K_STR; -static char constexpr SpeedMStr[] = "MB/s"; -static char constexpr SpeedGStr[] = "GB/s"; -static char constexpr SpeedTStr[] = "TB/s"; - -/*** -**** -***/ static auto constexpr LineWidth = int{ 80 }; @@ -204,10 +179,6 @@ int tr_main(int argc, char* argv[]) tr_locale_set_global(""); - tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); - tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); - tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); - printf("%s %s\n", MyReadableName, LONG_VERSION_STRING); /* user needs to pass in at least one argument */ diff --git a/daemon/daemon.cc b/daemon/daemon.cc index e3a71d9ae..ac77a3bcb 100644 --- a/daemon/daemon.cc +++ b/daemon/daemon.cc @@ -73,24 +73,6 @@ static char constexpr Usage[] = "Transmission " LONG_VERSION_STRING "\n" "Usage: transmission-daemon [options]"; -static auto constexpr MemK = size_t{ 1024 }; -static char constexpr MemKStr[] = "KiB"; -static char constexpr MemMStr[] = "MiB"; -static char constexpr MemGStr[] = "GiB"; -static char constexpr MemTStr[] = "TiB"; - -static auto constexpr DiskK = size_t{ 1000 }; -static char constexpr DiskKStr[] = "kB"; -static char constexpr DiskMStr[] = "MB"; -static char constexpr DiskGStr[] = "GB"; -static char constexpr DiskTStr[] = "TB"; - -static auto constexpr SpeedK = size_t{ 1000 }; -static char constexpr SpeedKStr[] = "kB/s"; -static char constexpr SpeedMStr[] = "MB/s"; -static char constexpr SpeedGStr[] = "GB/s"; -static char constexpr SpeedTStr[] = "TB/s"; - /*** **** Config File ***/ @@ -723,9 +705,6 @@ int tr_daemon::start([[maybe_unused]] bool foreground) } /* start the session */ - tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); - tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); - tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); session = tr_sessionInit(cdir, true, settings_); tr_sessionSetRPCCallback(session, on_rpc_callback, this); tr_logAddInfo(fmt::format(_("Loading settings from '{path}'"), fmt::arg("path", cdir))); diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index 6ef3a9b92..b994b4bb4 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -323,6 +323,7 @@ if(INSTALL_LIB) tr-strbuf.h transmission.h utils.h + values.h variant.h watchdir.h web-utils.h diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index 697b2f5ed..4e637e662 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -54,6 +54,7 @@ #include "libtransmission/tr-assert.h" #include "libtransmission/tr-strbuf.h" #include "libtransmission/utils.h" +#include "libtransmission/values.h" #include "libtransmission/variant.h" using namespace std::literals; @@ -664,242 +665,138 @@ uint64_t tr_ntohll(uint64_t netlonglong) #endif } -// --- +// --- VALUES / FORMATTER -namespace -{ -namespace formatter_impl +namespace libtransmission::Values { -struct formatter_unit -{ - std::array name; - uint64_t value; -}; +// default values; can be overridden by client apps +Config::Units Config::Memory{ Config::Base::Kibi, "B"sv, "KiB"sv, "MiB"sv, "GiB"sv, "TiB"sv }; +Config::Units Config::Speed{ Config::Base::Kilo, "B/s"sv, "kB/s"sv, "MB/s"sv, "GB/s"sv, "TB/s"sv }; +Config::Units Config::Storage{ Config::Base::Kilo, "B"sv, "kB"sv, "MB"sv, "GB"sv, "TB"sv }; -using formatter_units = std::array; - -enum -{ - TR_FMT_KB, - TR_FMT_MB, - TR_FMT_GB, - TR_FMT_TB -}; - -void formatter_init(formatter_units& units, uint64_t kilo, char const* kb, char const* mb, char const* gb, char const* tb) -{ - uint64_t value = kilo; - tr_strlcpy(std::data(units[TR_FMT_KB].name), kb, std::size(units[TR_FMT_KB].name)); - units[TR_FMT_KB].value = value; - - value *= kilo; - tr_strlcpy(std::data(units[TR_FMT_MB].name), mb, std::size(units[TR_FMT_MB].name)); - units[TR_FMT_MB].value = value; - - value *= kilo; - tr_strlcpy(std::data(units[TR_FMT_GB].name), gb, std::size(units[TR_FMT_GB].name)); - units[TR_FMT_GB].value = value; - - value *= kilo; - tr_strlcpy(std::data(units[TR_FMT_TB].name), tb, std::size(units[TR_FMT_TB].name)); - units[TR_FMT_TB].value = value; -} - -char* formatter_get_size_str(formatter_units const& u, char* buf, uint64_t bytes, size_t buflen) -{ - formatter_unit const* unit = nullptr; - - if (bytes < u[1].value) - { - unit = std::data(u); - } - else if (bytes < u[2].value) - { - unit = &u[1]; - } - else if (bytes < u[3].value) - { - unit = &u[2]; - } - else - { - unit = &u[3]; - } - - double const value = static_cast(bytes) / unit->value; - auto const* const units = std::data(unit->name); - - auto precision = int{}; - if (unit->value == 1) - { - precision = 0; - } - else if (value < 100) - { - precision = 2; - } - else - { - precision = 1; - } - - auto const [out, len] = fmt::format_to_n(buf, buflen - 1, "{:.{}Lf} {:s}", value, precision, units); - *out = '\0'; - return buf; -} - -formatter_units size_units; -formatter_units speed_units; -formatter_units mem_units; - -} // namespace formatter_impl -} // namespace - -size_t tr_speed_K = 0; - -void tr_formatter_size_init(uint64_t kilo, char const* kb, char const* mb, char const* gb, char const* tb) -{ - using namespace formatter_impl; - formatter_init(size_units, kilo, kb, mb, gb, tb); -} - -std::string tr_formatter_size_B(uint64_t bytes) -{ - using namespace formatter_impl; - auto buf = std::array{}; - return formatter_get_size_str(size_units, std::data(buf), bytes, std::size(buf)); -} - -void tr_formatter_speed_init(size_t kilo, char const* kb, char const* mb, char const* gb, char const* tb) -{ - using namespace formatter_impl; - tr_speed_K = kilo; - formatter_init(speed_units, kilo, kb, mb, gb, tb); -} - -std::string tr_formatter_speed_KBps(double kilo_per_second) -{ - using namespace formatter_impl; - - auto speed = kilo_per_second; - - if (speed < 999.95) // 0.0 KB to 999.9 KB (0.0 KiB to 999.9 KiB) - { - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_KB].name)); - } - - double const kilo = speed_units[TR_FMT_KB].value; - speed /= kilo; - - if (speed < 99.995) // 0.98 MB to 99.99 MB (1.00 MiB to 99.99 MiB) - { - return fmt::format("{:.2Lf} {:s}", speed, std::data(speed_units[TR_FMT_MB].name)); - } - if (speed < 999.95) // 100.0 MB to 999.9 MB (100.0 MiB to 999.9 MiB) - { - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_MB].name)); - } - - speed /= kilo; - - if (speed < 99.995) // 0.98 GB to 99.99 GB (1.00 GiB to 99.99 GiB) - { - return fmt::format("{:.2Lf} {:s}", speed, std::data(speed_units[TR_FMT_GB].name)); - } - // 100.0 GB and above (100.0 GiB and above) - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_GB].name)); -} - -std::string tr_formatter_speed_compact_KBps(double kilo_per_second) -{ - using namespace formatter_impl; - - auto speed = kilo_per_second; - - if (speed < 99.95) // 0.0 KB to 99.9 KB (0.0 KiB to 99.9 KiB) - { - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_KB].name)); - } - if (speed < 999.5) // 100 KB to 999 KB (100 KiB to 999 KiB) - { - return fmt::format("{:.0Lf} {:s}", speed, std::data(speed_units[TR_FMT_KB].name)); - } - - double const kilo = speed_units[TR_FMT_KB].value; - speed /= kilo; - - if (speed < 9.995) // 0.98 MB to 9.99 MB (1.00 MiB to 9.99 MiB) - { - return fmt::format("{:.2Lf} {:s}", speed, std::data(speed_units[TR_FMT_MB].name)); - } - if (speed < 99.95) // 10.0 MB to 99.9 MB (10.0 MiB to 99.9 MiB) - { - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_MB].name)); - } - if (speed < 999.5) // 100 MB to 999 MB (100 MiB to 999 MiB) - { - return fmt::format("{:.0Lf} {:s}", speed, std::data(speed_units[TR_FMT_MB].name)); - } - - speed /= kilo; - - if (speed < 9.995) // 0.98 GB to 9.99 GB (1.00 GiB to 9.99 GiB) - { - return fmt::format("{:.2Lf} {:s}", speed, std::data(speed_units[TR_FMT_GB].name)); - } - if (speed < 99.95) // 10.0 GB to 99.9 GB (10.0 GiB to 99.9 GiB) - { - return fmt::format("{:.1Lf} {:s}", speed, std::data(speed_units[TR_FMT_GB].name)); - } - // 100 GB and above (100 GiB and above) - return fmt::format("{:.0Lf} {:s}", speed, std::data(speed_units[TR_FMT_GB].name)); -} - -size_t tr_mem_K = 0; - -void tr_formatter_mem_init(size_t kilo, char const* kb, char const* mb, char const* gb, char const* tb) -{ - using namespace formatter_impl; - - tr_mem_K = kilo; - formatter_init(mem_units, kilo, kb, mb, gb, tb); -} - -std::string tr_formatter_mem_B(size_t bytes_per_second) -{ - using namespace formatter_impl; - - auto buf = std::array{}; - return formatter_get_size_str(mem_units, std::data(buf), bytes_per_second, std::size(buf)); -} +} // namespace libtransmission::Values tr_variant tr_formatter_get_units() { - using namespace formatter_impl; + using namespace libtransmission::Values; - auto const make_units_vec = [](formatter_units const& units) + auto const make_units_vec = [](auto const& units) { auto units_vec = tr_variant::Vector{}; - units_vec.reserve(std::size(units)); - std::transform( - std::begin(units), - std::end(units), - std::back_inserter(units_vec), - [](auto const& unit) { return std::data(unit.name); }); + for (size_t i = 0;; ++i) + { + auto const display_name = units.display_name(i); + if (std::empty(display_name)) + { + break; + } + units_vec.emplace_back(display_name); + } return units_vec; }; auto units_map = tr_variant::Map{ 6U }; - units_map.try_emplace(TR_KEY_memory_bytes, mem_units[TR_FMT_KB].value); - units_map.try_emplace(TR_KEY_memory_units, make_units_vec(mem_units)); - units_map.try_emplace(TR_KEY_size_bytes, size_units[TR_FMT_KB].value); - units_map.try_emplace(TR_KEY_size_units, make_units_vec(size_units)); - units_map.try_emplace(TR_KEY_speed_bytes, speed_units[TR_FMT_KB].value); - units_map.try_emplace(TR_KEY_speed_units, make_units_vec(speed_units)); + units_map.try_emplace(TR_KEY_memory_bytes, Memory::units().base()); + units_map.try_emplace(TR_KEY_memory_units, make_units_vec(Memory::units())); + units_map.try_emplace(TR_KEY_size_bytes, Storage::units().base()); + units_map.try_emplace(TR_KEY_size_units, make_units_vec(Storage::units())); + units_map.try_emplace(TR_KEY_speed_bytes, Speed::units().base()); + units_map.try_emplace(TR_KEY_speed_units, make_units_vec(Speed::units())); return tr_variant{ std::move(units_map) }; } +// --- formatters: storage + +void tr_formatter_size_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb) +{ + namespace Values = libtransmission::Values; + + auto const kval = base == 1000U ? Values::Config::Base::Kilo : Values::Config::Base::Kibi; + Values::Config::Storage = { kval, "B", kb, mb, gb, tb }; +} + +std::string tr_formatter_size_B(uint64_t bytes) +{ + using Storage = libtransmission::Values::Storage; + + return Storage{ bytes, Storage::Units::Bytes }.to_string(); +} + +// --- formatters: speed + +size_t tr_speed_K = 0; + +void tr_formatter_speed_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb) +{ + namespace Values = libtransmission::Values; + + auto const kval = base == 1000U ? Values::Config::Base::Kilo : Values::Config::Base::Kibi; + Values::Config::Speed = { kval, "B/s", kb, mb, gb, tb }; + tr_speed_K = base; +} + +std::string tr_formatter_speed_KBps(double kbyps) +{ + using Speed = libtransmission::Values::Speed; + + return Speed{ kbyps, Speed::Units::KByps }.to_string(); +} +uint64_t tr_toSpeedBytes(size_t kbyps) +{ + using Speed = libtransmission::Values::Speed; + + return Speed{ kbyps, Speed::Units::KByps }.base_quantity(); +} + +double tr_toSpeedKBps(size_t byps) +{ + using Speed = libtransmission::Values::Speed; + + return Speed{ byps, Speed::Units::Byps }.count(Speed::Units::KByps); +} + +// --- formatters: memory + +size_t tr_mem_K = 0; + +void tr_formatter_mem_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb) +{ + namespace Values = libtransmission::Values; + + auto const kval = base == 1000U ? Values::Config::Base::Kilo : Values::Config::Base::Kibi; + Values::Config::Memory = { kval, "B", kb, mb, gb, tb }; + tr_mem_K = base; +} + +std::string tr_formatter_mem_B(uint64_t bytes) +{ + using Memory = libtransmission::Values::Memory; + + return Memory{ bytes, Memory::Units::Bytes }.to_string(); +} + +std::string tr_formatter_mem_MB(double mbytes) +{ + using Memory = libtransmission::Values::Memory; + + return Memory{ mbytes, Memory::Units::MBytes }.to_string(); +} + +uint64_t tr_toMemBytes(size_t mbytes) +{ + using Memory = libtransmission::Values::Memory; + + return Memory{ mbytes, Memory::Units::MBytes }.base_quantity(); +} + +double tr_toMemMB(uint64_t bytes) +{ + using Memory = libtransmission::Values::Memory; + + return Memory{ bytes, Memory::Units::Bytes }.count(Memory::Units::MBytes); +} + // --- ENVIRONMENT bool tr_env_key_exists(char const* key) diff --git a/libtransmission/utils.h b/libtransmission/utils.h index d31590e08..728148676 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -19,6 +19,7 @@ #include "libtransmission/tr-macros.h" #include "libtransmission/variant.h" +#include "libtransmission/values.h" struct tr_error; @@ -300,55 +301,24 @@ constexpr void tr_timeUpdate(time_t now) noexcept // --- /* example: tr_formatter_size_init(1024, _("KiB"), _("MiB"), _("GiB"), _("TiB")); */ - -void tr_formatter_size_init(uint64_t kilo, char const* kb, char const* mb, char const* gb, char const* tb); - -void tr_formatter_speed_init(size_t kilo, char const* kb, char const* mb, char const* gb, char const* tb); - -void tr_formatter_mem_init(size_t kilo, char const* kb, char const* mb, char const* gb, char const* tb); +void tr_formatter_size_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb); +void tr_formatter_speed_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb); +void tr_formatter_mem_init(size_t base, char const* kb, char const* mb, char const* gb, char const* tb); extern size_t tr_speed_K; extern size_t tr_mem_K; -extern uint64_t tr_size_K; /* unused? */ -/** @brief Format a speed from KBps into a user-readable string of at most 4 significant digits. */ -[[nodiscard]] std::string tr_formatter_speed_KBps(double kilo_per_second); -/** @brief Format a speed from KBps into a user-readable string of at most 3 significant digits. */ -[[nodiscard]] std::string tr_formatter_speed_compact_KBps(double kilo_per_second); +[[nodiscard]] double tr_toMemMB(uint64_t bytes); +[[nodiscard]] double tr_toSpeedKBps(size_t byps); +[[nodiscard]] uint64_t tr_toMemBytes(size_t mbytes); +[[nodiscard]] uint64_t tr_toSpeedBytes(size_t kbyps); -/** @brief Format a memory size from bytes into a user-readable string. */ -[[nodiscard]] std::string tr_formatter_mem_B(size_t bytes); - -/** @brief Format a memory size from MB into a user-readable string. */ -[[nodiscard]] static inline std::string tr_formatter_mem_MB(double MBps) -{ - return tr_formatter_mem_B((size_t)(MBps * tr_mem_K * tr_mem_K)); -} - -/** @brief Format a file size from bytes into a user-readable string. */ +[[nodiscard]] std::string tr_formatter_mem_B(uint64_t bytes); +[[nodiscard]] std::string tr_formatter_mem_MB(double mbytes); [[nodiscard]] std::string tr_formatter_size_B(uint64_t bytes); +[[nodiscard]] std::string tr_formatter_speed_KBps(double kbyps); -struct tr_variant tr_formatter_get_units(); - -[[nodiscard]] static inline size_t tr_toSpeedBytes(size_t KBps) -{ - return KBps * tr_speed_K; -} - -[[nodiscard]] static inline auto tr_toSpeedKBps(size_t Bps) -{ - return Bps / double(tr_speed_K); -} - -[[nodiscard]] static inline auto tr_toMemBytes(size_t MB) -{ - return uint64_t(tr_mem_K) * tr_mem_K * MB; -} - -[[nodiscard]] static inline auto tr_toMemMB(uint64_t B) -{ - return size_t(B / (tr_mem_K * tr_mem_K)); -} +[[nodiscard]] struct tr_variant tr_formatter_get_units(); // --- diff --git a/libtransmission/values.h b/libtransmission/values.h new file mode 100644 index 000000000..c22f4ff9b --- /dev/null +++ b/libtransmission/values.h @@ -0,0 +1,259 @@ +// This file Copyright © Mnemosyne LLC. +// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), +// or any future license endorsed by Mnemosyne LLC. +// License text can be found in the licenses/ folder. + +#pragma once + +#include +#include // for uint64_t +#include +#include + +#include + +namespace libtransmission::Values +{ +enum class MemoryUnits +{ + Bytes, + KBytes, + MBytes, + GBytes, + TBytes +}; + +using StorageUnits = MemoryUnits; + +enum class SpeedUnits +{ + Byps, + KByps, + MByps, + GByps, + TByps +}; + +struct Config +{ + enum class Base + { + Kilo = 1000U, + Kibi = 1024U + }; + + template + struct Units + { + template + Units(Base base, Names... names) + { + set_base(base); + + auto idx = size_t{ 0U }; + (set_name(idx++, names), ...); + } + + [[nodiscard]] constexpr auto base() const noexcept + { + return static_cast(base_); + } + + [[nodiscard]] constexpr auto display_name(size_t units) const noexcept + { + return std::string_view{ units < std::size(display_names_) ? std::data(display_names_[units]) : "" }; + } + + [[nodiscard]] constexpr auto multiplier(UnitsEnum multiplier) const noexcept + { + return multipliers_[static_cast(multiplier)]; + } + + private: + constexpr void set_base(Base base) + { + base_ = base; + + auto val = uint64_t{ 1U }; + for (auto& multiplier : multipliers_) + { + multiplier = val; + val *= static_cast(base); + } + } + + void set_name(size_t idx, std::string_view name) + { + *fmt::format_to_n(std::data(display_names_[idx]), std::size(display_names_[idx]) - 1, "{:s}", name).out = '\0'; + } + + std::array, 5> display_names_ = {}; + std::array multipliers_; + Base base_; + }; + + static Units Memory; + static Units Speed; + static Units Storage; +}; + +template const& units_> +class Value +{ +public: + using Units = UnitsEnum; + + constexpr Value() = default; + + constexpr Value(uint64_t value, Units multiple) + : base_quantity_{ value * units_.multiplier(multiple) } + { + } + + template + Value(Number value, Units multiple) + : base_quantity_{ static_cast(value * units_.multiplier(multiple)) } + { + } + + constexpr auto& operator+=(Value const& that) noexcept + { + base_quantity_ += that.base_quantity_; + return *this; + } + + [[nodiscard]] constexpr auto base_quantity() const noexcept + { + return base_quantity_; + } + + [[nodiscard]] constexpr auto count(Units tgt) const noexcept + { + return base_quantity_ / (1.0 * units_.multiplier(tgt)); + } + + [[nodiscard]] constexpr auto operator+(Value const& that) noexcept + { + auto ret = *this; + return ret += that; + } + + constexpr auto& operator*=(uint64_t mult) noexcept + { + base_quantity_ *= mult; + return *this; + } + + [[nodiscard]] constexpr auto operator*(uint64_t mult) noexcept + { + auto ret = *this; + return ret *= mult; + } + + constexpr auto& operator/=(uint64_t mult) noexcept + { + base_quantity_ /= mult; + return *this; + } + + [[nodiscard]] constexpr auto operator/(uint64_t mult) noexcept + { + auto ret = *this; + return ret /= mult; + } + + [[nodiscard]] constexpr auto operator<(Value const& that) const noexcept + { + return compare(that) < 0; + } + + [[nodiscard]] constexpr auto operator<=(Value const& that) const noexcept + { + return compare(that) <= 0; + } + + [[nodiscard]] constexpr auto operator==(Value const& that) const noexcept + { + return compare(that) == 0; + } + + [[nodiscard]] constexpr auto operator!=(Value const& that) const noexcept + { + return compare(that) != 0; + } + + [[nodiscard]] constexpr auto operator>(Value const& that) const noexcept + { + return compare(that) > 0; + } + + [[nodiscard]] constexpr auto operator>=(Value const& that) const noexcept + { + return compare(that) >= 0; + } + + std::string_view to_string(char* buf, size_t buflen) const noexcept + { + auto idx = size_t{ 0 }; + + if (base_quantity_ < 1000) // 0 to 999 + { + *fmt::format_to_n(buf, buflen - 1, "{:d} {:s}", base_quantity_, units_.display_name(idx)).out = '\0'; + return buf; + } + + auto val = 1.0 * base_quantity_; + for (;;) + { + ++idx; + val /= units_.base(); + + if (val < 99.995) // 0.98 to 99.99 + { + *fmt::format_to_n(buf, buflen - 1, "{:.2Lf} {:s}", val, units_.display_name(idx)).out = '\0'; + return buf; + } + + if (val < 999.95 || std::empty(units_.display_name(idx + 1))) // 100.0 to 999.9 + { + *fmt::format_to_n(buf, buflen - 1, "{:.1Lf} {:s}", val, units_.display_name(idx)).out = '\0'; + return buf; + } + } + } + + [[nodiscard]] std::string to_string() const + { + auto buf = std::array{}; + return std::string{ to_string(std::data(buf), std::size(buf)) }; + } + + [[nodiscard]] static constexpr auto const& units() noexcept + { + return units_; + } + +private: + uint64_t base_quantity_ = {}; + + [[nodiscard]] constexpr int compare(Value const& that) const noexcept // <=> + { + if (base_quantity_ < that.base_quantity_) + { + return -1; + } + + if (base_quantity_ > that.base_quantity_) + { + return 1; + } + + return 0; + } +}; + +using Memory = Value; +using Storage = Value; +using Speed = Value; + +} // namespace libtransmission::Values diff --git a/tests/libtransmission/CMakeLists.txt b/tests/libtransmission/CMakeLists.txt index 90c39394d..2040575ff 100644 --- a/tests/libtransmission/CMakeLists.txt +++ b/tests/libtransmission/CMakeLists.txt @@ -54,6 +54,7 @@ target_sources(libtransmission-test torrents-test.cc tr-peer-info-test.cc utils-test.cc + values-test.cc variant-test.cc watchdir-test.cc web-utils-test.cc) diff --git a/tests/libtransmission/test-fixtures.h b/tests/libtransmission/test-fixtures.h index 3fcbfd72b..49b4a4ab6 100644 --- a/tests/libtransmission/test-fixtures.h +++ b/tests/libtransmission/test-fixtures.h @@ -305,38 +305,6 @@ private: Sandbox sandbox_; }; -inline void ensureFormattersInited() -{ - static constexpr int MEM_K = 1024; - static char constexpr const* const MEM_K_STR = "KiB"; - static char constexpr const* const MEM_M_STR = "MiB"; - static char constexpr const* const MEM_G_STR = "GiB"; - static char constexpr const* const MEM_T_STR = "TiB"; - - static constexpr int DISK_K = 1000; - static char constexpr const* const DISK_K_STR = "kB"; - static char constexpr const* const DISK_M_STR = "MB"; - static char constexpr const* const DISK_G_STR = "GB"; - static char constexpr const* const DISK_T_STR = "TB"; - - static constexpr int SPEED_K = 1000; - static char constexpr const* const SPEED_K_STR = "kB/s"; - static char constexpr const* const SPEED_M_STR = "MB/s"; - static char constexpr const* const SPEED_G_STR = "GB/s"; - static char constexpr const* const SPEED_T_STR = "TB/s"; - - static std::once_flag flag; - - std::call_once( - flag, - []() - { - tr_formatter_mem_init(MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR); - tr_formatter_size_init(DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR); - tr_formatter_speed_init(SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR); - }); -} - class SessionTest : public SandboxedTest { private: @@ -344,8 +312,6 @@ private: tr_session* sessionInit(tr_variant& settings) { - ensureFormattersInited(); - auto* const settings_map = settings.get_if(); EXPECT_NE(settings_map, nullptr); diff --git a/tests/libtransmission/values-test.cc b/tests/libtransmission/values-test.cc new file mode 100644 index 000000000..c47a8ea8b --- /dev/null +++ b/tests/libtransmission/values-test.cc @@ -0,0 +1,46 @@ +// This file Copyright © Mnemosyne LLC. +// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), +// or any future license endorsed by Mnemosyne LLC. +// License text can be found in the licenses/ folder. + +#include + +#include +#include + +#include "gtest/gtest.h" + +using ValuesTest = ::testing::Test; + +TEST_F(ValuesTest, value) +{ + using Speed = libtransmission::Values::Speed; + + auto val = Speed{ 1, Speed::Units::MByps }; + EXPECT_EQ("1.00 MB/s", val.to_string()); + EXPECT_EQ(1000000UL, val.base_quantity()); + EXPECT_NEAR(1000U, val.count(Speed::Units::KByps), 0.0001); + EXPECT_NEAR(1U, val.count(Speed::Units::MByps), 0.0001); + EXPECT_NEAR(0.001, val.count(Speed::Units::GByps), 0.0001); + + val = Speed{ 1, Speed::Units::Byps }; + EXPECT_EQ(1U, val.base_quantity()); + EXPECT_EQ("1 B/s", val.to_string()); + + val = Speed{ 10, Speed::Units::KByps }; + EXPECT_EQ("10.00 kB/s", val.to_string()); + + val = Speed{ 999, Speed::Units::KByps }; + EXPECT_EQ("999.0 kB/s", val.to_string()); +} + +TEST_F(ValuesTest, valueHonorsFormatterInit) +{ + using Speed = libtransmission::Values::Speed; + + tr_formatter_speed_init(1024, "KayBeePerEss", "EmmBeePerEss", "GeeBeePerEss", "TeeBeePerEss"); + + auto const val = Speed{ 1, Speed::Units::MByps }; + EXPECT_EQ("1.00 EmmBeePerEss", val.to_string()); + EXPECT_EQ(1048576U, val.base_quantity()); +} diff --git a/tests/utils/assets/bitcomet.utf-8.special.show b/tests/utils/assets/bitcomet.utf-8.special.show index fb61f96f7..327931db9 100644 --- a/tests/utils/assets/bitcomet.utf-8.special.show +++ b/tests/utils/assets/bitcomet.utf-8.special.show @@ -11,7 +11,7 @@ GENERAL Source: 私たちの世界 Piece Count: 1 Piece Size: 1.00 MiB - Total Size: 0.01 kB + Total Size: 13 B Privacy: Public torrent TRACKERS @@ -21,6 +21,6 @@ TRACKERS FILES - modified test-utf8/別の世界f1.txt (0.01 kB) - modified test-utf8/別の世界f2.txt (0.01 kB) + modified test-utf8/別の世界f1.txt (6 B) + modified test-utf8/別の世界f2.txt (7 B) diff --git a/tests/utils/assets/bittorrent-v2-hybrid-test.show b/tests/utils/assets/bittorrent-v2-hybrid-test.show index 459d35851..281997fbd 100644 --- a/tests/utils/assets/bittorrent-v2-hybrid-test.show +++ b/tests/utils/assets/bittorrent-v2-hybrid-test.show @@ -32,7 +32,7 @@ FILES bittorrent-v1-v2-hybrid-test/eld-dust.mkv (61.64 MB) bittorrent-v1-v2-hybrid-test/fairlight_cncd-agenda_circling_forth-1080p30lq.mp4 (277.9 MB) bittorrent-v1-v2-hybrid-test/meet the deadline - Still _ Evoke 2014.mp4 (44.58 MB) - bittorrent-v1-v2-hybrid-test/readme.txt (0.06 kB) + bittorrent-v1-v2-hybrid-test/readme.txt (61 B) bittorrent-v1-v2-hybrid-test/tbl-goa.avi (26.30 MB) bittorrent-v1-v2-hybrid-test/tbl-tint.mpg (115.9 MB) diff --git a/utils/create.cc b/utils/create.cc index 180d95386..a8837fe7b 100644 --- a/utils/create.cc +++ b/utils/create.cc @@ -29,8 +29,6 @@ #include #include -#include "units.h" - using namespace std::literals; namespace @@ -139,9 +137,6 @@ int tr_main(int argc, char* argv[]) tr_locale_set_global(""); tr_logSetLevel(TR_LOG_ERROR); - tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); - tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); - tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); auto options = app_options{}; if (parseCommandLine(options, argc, (char const* const*)argv) != 0) diff --git a/utils/remote.cc b/utils/remote.cc index f2976439a..df85cab88 100644 --- a/utils/remote.cc +++ b/utils/remote.cc @@ -61,24 +61,6 @@ static char constexpr Usage[] = "transmission-remote " LONG_VERSION_STRING static auto constexpr Arguments = TR_KEY_arguments; -static auto constexpr MemK = size_t{ 1024 }; -static char constexpr MemKStr[] = "KiB"; -static char constexpr MemMStr[] = MEM_M_STR; -static char constexpr MemGStr[] = "GiB"; -static char constexpr MemTStr[] = "TiB"; - -static auto constexpr DiskK = size_t{ 1000 }; -static char constexpr DiskKStr[] = "kB"; -static char constexpr DiskMStr[] = "MB"; -static char constexpr DiskGStr[] = "GB"; -static char constexpr DiskTStr[] = "TB"; - -static auto constexpr SpeedK = size_t{ 1000 }; -static auto constexpr SpeedKStr = SPEED_K_STR; -static char constexpr SpeedMStr[] = "MB/s"; -static char constexpr SpeedGStr[] = "GB/s"; -static char constexpr SpeedTStr[] = "TB/s"; - struct Config { std::string auth; @@ -3329,10 +3311,6 @@ int tr_main(int argc, char* argv[]) return EXIT_FAILURE; } - tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); - tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); - tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); - getHostAndPortAndRpcUrl(&argc, argv, &host, &port, &rpcurl, config); if (std::empty(host)) diff --git a/utils/show.cc b/utils/show.cc index 5a6a99680..9efa2d41f 100644 --- a/utils/show.cc +++ b/utils/show.cc @@ -35,8 +35,6 @@ #include #include -#include "units.h" - using namespace std::literals; namespace @@ -409,9 +407,6 @@ int tr_main(int argc, char* argv[]) tr_logSetQueueEnabled(false); tr_logSetLevel(TR_LOG_ERROR); - tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr); - tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr); - tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr); auto opts = app_opts{}; if (parseCommandLine(opts, argc, (char const* const*)argv) != 0) diff --git a/utils/units.h b/utils/units.h deleted file mode 100644 index 1de88923b..000000000 --- a/utils/units.h +++ /dev/null @@ -1,26 +0,0 @@ -// This file Copyright © Mnemosyne LLC. -// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), -// or any future license endorsed by Mnemosyne LLC. -// License text can be found in the licenses/ folder. - -#pragma once - -#include - -static auto constexpr MemK = size_t{ 1024 }; -static char constexpr MemKStr[] = "KiB"; -static char constexpr MemMStr[] = "MiB"; -static char constexpr MemGStr[] = "GiB"; -static char constexpr MemTStr[] = "TiB"; - -static auto constexpr DiskK = size_t{ 1000 }; -static char constexpr DiskKStr[] = "kB"; -static char constexpr DiskMStr[] = "MB"; -static char constexpr DiskGStr[] = "GB"; -static char constexpr DiskTStr[] = "TB"; - -static auto constexpr SpeedK = size_t{ 1000 }; -static char constexpr SpeedKStr[] = "kB/s"; -static char constexpr SpeedMStr[] = "MB/s"; -static char constexpr SpeedGStr[] = "GB/s"; -static char constexpr SpeedTStr[] = "TB/s";