diff --git a/openVulkanoCpp/Math/ByteSize.hpp b/openVulkanoCpp/Math/ByteSize.hpp index e566d48..38fff61 100644 --- a/openVulkanoCpp/Math/ByteSize.hpp +++ b/openVulkanoCpp/Math/ByteSize.hpp @@ -6,43 +6,103 @@ #pragma once +#include #include #include #include #include #include #include +#include namespace openVulkanoCpp { + namespace ByteSizeUnitHelper + { + constexpr std::array BuildFactors() + { + std::array factors = { 1 }; + for(uint64_t j = 1, i = 10; i < 70; i += 10, j++) + { + factors[j] = 1uLL << i; + } + for(uint64_t j = 7, i = 1000; j < 13; i *= 1000, j++) + { + factors[j] = i; + } + return factors; + } + + constexpr std::array BuildDivisors() + { + std::array factors = BuildFactors(); + std::array divs{{1}}; + for (int i = 1; i < divs.size(); i++) + { + divs[i] = 1. / static_cast(factors[i]); + } + return divs; + } + } + + class ByteSizeUnit final + { + static constexpr std::array FACTORS = ByteSizeUnitHelper::BuildFactors(); + static constexpr std::array DIVISORS = ByteSizeUnitHelper::BuildDivisors(); + public: + enum Unit : int { B, kiB, MiB, GiB, TiB, PiB, EiB, kB, MB, GB, TB, PB, EB }; + + constexpr ByteSizeUnit(Unit unit) : unit(unit) {} + + [[nodiscard]] constexpr uint64_t GetFactor() const { return FACTORS[unit]; } + + [[nodiscard]] constexpr double GetDivisor() const { return DIVISORS[unit]; } + + [[nodiscard]] constexpr std::string_view GetName() const + { + return magic_enum::enum_name(unit); + } + + [[nodiscard]] static constexpr ByteSizeUnit FromName(std::string_view name) + { + auto result = magic_enum::enum_cast(name); + if (result) return { result.value() }; + return B; + } + + [[nodiscard]] static constexpr ByteSizeUnit GetClosestUnit(uint64_t size, bool si = false) + { + for (int i = (si ? EB : EiB); i <= (si ? kB : kiB); i--) + { + if (size > FACTORS[i]) return { static_cast(i) }; + } + return { B }; + } + + private: + Unit unit; + }; + class ByteSize final { - static inline const std::regex REGEX_PATTERN = std::regex("\\s*(\\d+(\\.\\d*)?|\\.\\d+)\\s*([kMGTPE]i?)[Bb]\\s*"); + static inline const std::regex REGEX_PATTERN = std::regex(R"(\s*(\d+(\.\d*)?|\.\d+)\s*([kMGTPE]i?B)\s*)"); - size_t bytes; + uint64_t bytes; public: - constexpr ByteSize(size_t size = 0) : bytes(size) {} + constexpr ByteSize(uint64_t size = 0) : bytes(size) {} + + template + constexpr ByteSize(T size, ByteSizeUnit unit) + : bytes(size * unit.GetFactor()) {} ByteSize(const std::string& str) { std::smatch match; if (!std::regex_match(str, match, REGEX_PATTERN)) throw std::runtime_error("Unable to parse '" + str + "' as size."); - double d=std::stod(match[1].str()); - size_t mult = 1; - if(match[3] == "ki") mult = 1LL<<10; - else if(match[3] == "Mi") mult = 1LL<<20; - else if(match[3] == "Gi") mult = 1LL<<30; - else if(match[3] == "Ti") mult = 1LL<<40; - else if(match[3] == "Pi") mult = 1LL<<50; - else if(match[3] == "Ei") mult = 1LL<<60; - else if(match[3] == "k") mult = 1'000LL; - else if(match[3] == "M") mult = 1'000'000LL; - else if(match[3] == "G") mult = 1'000'000'000LL; - else if(match[3] == "T") mult = 1'000'000'000'000LL; - else if(match[3] == "P") mult = 1'000'000'000'000'000LL; - else if(match[3] == "E") mult = 1'000'000'000'000'000'000LL; - bytes = d * mult; + double value = std::stod(match[1].str()); + uint64_t factor = ByteSizeUnit::FromName(match[3].str()).GetFactor(); + bytes = value * factor; } std::ostream& operator<<(std::ostream& os) const { return FormatStream(os); } @@ -50,26 +110,8 @@ namespace openVulkanoCpp std::ostream& FormatStream(std::ostream& oss, bool si = false) const { oss << std::setprecision(3); - if (si) - { - if (bytes < 1'000LL) oss << bytes << " B"; - else if (bytes < 1'000'000LL) oss << (bytes * 1. / 1000LL) << " kB"; - else if (bytes < 1'000'000'000LL) oss << (bytes * 1. / 1000'000LL) << " MB"; - else if (bytes < 1'000'000'000'000LL) oss << (bytes * 1. / 1000'000'000LL) << " GB"; - else if (bytes < 1'000'000'000'000'000LL) oss << (bytes * 1. / 1000'000'000'000LL) << " TB"; - else if (bytes < 1'000'000'000'000'000'000LL) oss << (bytes * 1. / 1000'000'000'000'000LL) << " PB"; - else oss << (bytes * 1. / 1000'000'000'000'000'000LL) << " EB"; - } - else - { - if (bytes < 1LL<<10) oss << bytes << " B"; - else if (bytes < 1LL<<20) oss << (bytes * 1. / (1LL << 10)) << " kiB"; - else if (bytes < 1LL<<30) oss << (bytes * 1. / (1LL << 20)) << " MiB"; - else if (bytes < 1LL<<40) oss << (bytes * 1. / (1LL << 30)) << " GiB"; - else if (bytes < 1LL<<50) oss << (bytes * 1. / (1LL << 40)) << " TiB"; - else if (bytes < 1LL<<60) oss << (bytes * 1. / (1LL << 50)) << " PiB"; - else oss << (bytes * 1. / (1LL << 60)) << " EiB"; - } + auto unit = ByteSizeUnit::GetClosestUnit(bytes, si); + oss << bytes * unit.GetDivisor() << ' ' << unit.GetName(); return oss; } @@ -80,35 +122,38 @@ namespace openVulkanoCpp return oss.str(); } - constexpr operator size_t() const { return bytes; } + constexpr operator uint64_t() const { return bytes; } operator std::string() const { return Format(); } }; - // bytes only with integer - inline ByteSize operator"" _B(unsigned long long int num){ return ByteSize(num); } // floating-point numbers, like 5.5_kB - inline ByteSize operator"" _kiB(long double num) { return ByteSize((1LL<<10) * num); } - inline ByteSize operator"" _MiB(long double num) { return ByteSize((1LL<<20) * num); } - inline ByteSize operator"" _GiB(long double num) { return ByteSize((1LL<<30) * num); } - inline ByteSize operator"" _TiB(long double num) { return ByteSize((1LL<<40) * num); } - inline ByteSize operator"" _PiB(long double num) { return ByteSize((1LL<<50) * num); } - inline ByteSize operator"" _kB(long double num) { return ByteSize(1'000LL * num); } - inline ByteSize operator"" _MB(long double num) { return ByteSize(1'000'000LL * num); } - inline ByteSize operator"" _GB(long double num) { return ByteSize(1'000'000'000LL * num); } - inline ByteSize operator"" _TB(long double num) { return ByteSize(1'000'000'000'000LL * num); } - inline ByteSize operator"" _PB(long double num) { return ByteSize(1'000'000'000'000'000LL * num); } + inline ByteSize operator"" _kiB(long double num) { return { num, ByteSizeUnit::kiB }; } + inline ByteSize operator"" _MiB(long double num) { return { num, ByteSizeUnit::MiB }; } + inline ByteSize operator"" _GiB(long double num) { return { num, ByteSizeUnit::GiB }; } + inline ByteSize operator"" _TiB(long double num) { return { num, ByteSizeUnit::TiB }; } + inline ByteSize operator"" _PiB(long double num) { return { num, ByteSizeUnit::PiB }; } + inline ByteSize operator"" _EiB(long double num) { return { num, ByteSizeUnit::EiB }; } + inline ByteSize operator"" _kB(long double num) { return { num, ByteSizeUnit::kB }; } + inline ByteSize operator"" _MB(long double num) { return { num, ByteSizeUnit::MB }; } + inline ByteSize operator"" _GB(long double num) { return { num, ByteSizeUnit::GB }; } + inline ByteSize operator"" _TB(long double num) { return { num, ByteSizeUnit::TB }; } + inline ByteSize operator"" _PB(long double num) { return { num, ByteSizeUnit::PB }; } + inline ByteSize operator"" _EB(long double num) { return { num, ByteSizeUnit::EB }; } // repeated for integer literals so that e.g. 5_kB works - inline ByteSize operator"" _kiB(unsigned long long int num) { return ByteSize((1LL<<10) * num); } - inline ByteSize operator"" _MiB(unsigned long long int num) { return ByteSize((1LL<<20) * num); } - inline ByteSize operator"" _GiB(unsigned long long int num) { return ByteSize((1LL<<30) * num); } - inline ByteSize operator"" _TiB(unsigned long long int num) { return ByteSize((1LL<<40) * num); } - inline ByteSize operator"" _PiB(unsigned long long int num) { return ByteSize((1LL<<50) * num); } - inline ByteSize operator"" _kB(unsigned long long int num) { return ByteSize(1'000LL * num); } - inline ByteSize operator"" _MB(unsigned long long int num) { return ByteSize(1'000'000LL * num); } - inline ByteSize operator"" _GB(unsigned long long int num) { return ByteSize(1'000'000'000LL * num); } - inline ByteSize operator"" _TB(unsigned long long int num) { return ByteSize(1'000'000'000'000LL * num); } - inline ByteSize operator"" _PB(unsigned long long int num) { return ByteSize(1'000'000'000'000'000LL * num); } + inline ByteSize operator"" _B(unsigned long long int num) { return { num }; } // bytes only with integer + inline ByteSize operator"" _kiB(unsigned long long int num) { return { num << 10 }; } + inline ByteSize operator"" _MiB(unsigned long long int num) { return { num << 20 }; } + inline ByteSize operator"" _GiB(unsigned long long int num) { return { num << 30 }; } + inline ByteSize operator"" _TiB(unsigned long long int num) { return { num << 40 }; } + inline ByteSize operator"" _PiB(unsigned long long int num) { return { num << 50 }; } + inline ByteSize operator"" _EiB(unsigned long long int num) { return { num << 60 }; } + inline ByteSize operator"" _kB(unsigned long long int num) { return { num, ByteSizeUnit::kB }; } + inline ByteSize operator"" _MB(unsigned long long int num) { return { num, ByteSizeUnit::MB }; } + inline ByteSize operator"" _GB(unsigned long long int num) { return { num, ByteSizeUnit::GB }; } + inline ByteSize operator"" _TB(unsigned long long int num) { return { num, ByteSizeUnit::TB }; } + inline ByteSize operator"" _PB(unsigned long long int num) { return { num, ByteSizeUnit::PB }; } + inline ByteSize operator"" _EB(unsigned long long int num) { return { num, ByteSizeUnit::EB }; } } template<> struct fmt::formatter