// 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 std::fabs(), std::floor() #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) noexcept { 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 display_name(UnitsEnum multiplier) const noexcept { return display_name(static_cast(multiplier)); } [[nodiscard]] constexpr auto multiplier(UnitsEnum multiplier) const noexcept { return multipliers_[static_cast(multiplier)]; } private: constexpr void set_base(Base base) noexcept { 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)) } { } [[nodiscard]] constexpr auto base_quantity() const noexcept { return base_quantity_; } [[nodiscard]] constexpr auto is_zero() const noexcept { return base_quantity_ == 0U; } [[nodiscard]] constexpr auto count(Units tgt) const noexcept { return base_quantity_ / (1.0 * units_.multiplier(tgt)); } constexpr auto& operator+=(Value const& that) noexcept { base_quantity_ += that.base_quantity_; return *this; } [[nodiscard]] constexpr auto operator+(Value const& that) const 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) const 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) const 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 }; auto val = 1.0 * base_quantity_; for (;;) { if (std::fabs(val - std::floor(val)) < 0.001 && (val < 999.5 || std::empty(units_.display_name(idx + 1)))) { *fmt::format_to_n(buf, buflen - 1, "{:.0Lf} {:s}", val, units_.display_name(idx)).out = '\0'; return buf; } 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; } val /= units_.base(); ++idx; } } [[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