1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-15 23:12:44 +01:00

Merge pull request #14423 from MarcelCoding/progress-bar-units

progress-bar: use dynamic size units
This commit is contained in:
Jörg Thalheim 2025-11-10 17:15:12 +00:00 committed by GitHub
commit accb564889
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 205 additions and 14 deletions

View file

@ -99,6 +99,42 @@ N string2IntWithUnitPrefix(std::string_view s)
throw UsageError("'%s' is not an integer", s);
}
// Base also uses 'K', because it should also displayed as KiB => 100 Bytes => 0.1 KiB
#define NIX_UTIL_SIZE_UNITS \
NIX_UTIL_DEFINE_SIZE_UNIT(Base, 'K') \
NIX_UTIL_DEFINE_SIZE_UNIT(Kilo, 'K') \
NIX_UTIL_DEFINE_SIZE_UNIT(Mega, 'M') \
NIX_UTIL_DEFINE_SIZE_UNIT(Giga, 'G') \
NIX_UTIL_DEFINE_SIZE_UNIT(Tera, 'T') \
NIX_UTIL_DEFINE_SIZE_UNIT(Peta, 'P') \
NIX_UTIL_DEFINE_SIZE_UNIT(Exa, 'E') \
NIX_UTIL_DEFINE_SIZE_UNIT(Zetta, 'Z') \
NIX_UTIL_DEFINE_SIZE_UNIT(Yotta, 'Y')
enum class SizeUnit {
#define NIX_UTIL_DEFINE_SIZE_UNIT(name, suffix) name,
NIX_UTIL_SIZE_UNITS
#undef NIX_UTIL_DEFINE_SIZE_UNIT
};
constexpr inline auto sizeUnits = std::to_array<SizeUnit>({
#define NIX_UTIL_DEFINE_SIZE_UNIT(name, suffix) SizeUnit::name,
NIX_UTIL_SIZE_UNITS
#undef NIX_UTIL_DEFINE_SIZE_UNIT
});
SizeUnit getSizeUnit(int64_t value);
/**
* Returns the unit if all values would be rendered using the same unit
* otherwise returns `std::nullopt`.
*/
std::optional<SizeUnit> getCommonSizeUnit(std::initializer_list<int64_t> values);
std::string renderSizeWithoutUnit(int64_t value, SizeUnit unit, bool align = false);
char getSizeUnitSuffix(SizeUnit unit);
/**
* Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6
* GiB`. If `align` is set, the number will be right-justified by

View file

@ -132,17 +132,62 @@ std::optional<N> string2Float(const std::string_view s)
template std::optional<double> string2Float<double>(const std::string_view s);
template std::optional<float> string2Float<float>(const std::string_view s);
static const int64_t conversionNumber = 1024;
SizeUnit getSizeUnit(int64_t value)
{
auto unit = sizeUnits.begin();
uint64_t absValue = std::abs(value);
while (absValue > conversionNumber && unit < sizeUnits.end()) {
unit++;
absValue /= conversionNumber;
}
return *unit;
}
std::optional<SizeUnit> getCommonSizeUnit(std::initializer_list<int64_t> values)
{
assert(values.size() > 0);
auto it = values.begin();
SizeUnit unit = getSizeUnit(*it);
it++;
for (; it != values.end(); it++) {
if (unit != getSizeUnit(*it)) {
return std::nullopt;
}
}
return unit;
}
std::string renderSizeWithoutUnit(int64_t value, SizeUnit unit, bool align)
{
// bytes should also displayed as KiB => 100 Bytes => 0.1 KiB
auto power = std::max<std::underlying_type_t<SizeUnit>>(1, std::to_underlying(unit));
double denominator = std::pow(conversionNumber, power);
double result = (double) value / denominator;
return fmt(align ? "%6.1f" : "%.1f", result);
}
char getSizeUnitSuffix(SizeUnit unit)
{
switch (unit) {
#define NIX_UTIL_DEFINE_SIZE_UNIT(name, suffix) \
case SizeUnit::name: \
return suffix;
NIX_UTIL_SIZE_UNITS
#undef NIX_UTIL_DEFINE_SIZE_UNIT
}
assert(false);
}
std::string renderSize(int64_t value, bool align)
{
static const std::array<char, 9> prefixes{{'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}};
size_t power = 0;
double abs_value = std::abs(value);
while (abs_value > 1024 && power < prefixes.size()) {
++power;
abs_value /= 1024;
}
double res = (double) value / std::pow(1024.0, power);
return fmt(align ? "%6.1f %ciB" : "%.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power));
SizeUnit unit = getSizeUnit(value);
return fmt("%s %ciB", renderSizeWithoutUnit(value, unit, align), getSizeUnitSuffix(unit));
}
bool hasPrefix(std::string_view s, std::string_view prefix)