Skip to content

Commit 8ba0a5a

Browse files
committed
progress-bar: use dynamic size units
1 parent da637a0 commit 8ba0a5a

File tree

4 files changed

+131
-14
lines changed

4 files changed

+131
-14
lines changed

src/libmain/progress-bar.cc

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,6 @@ class ProgressBar : public Logger
467467

468468
std::string getStatus(State & state)
469469
{
470-
auto MiB = 1024.0 * 1024.0;
471-
472470
std::string res;
473471

474472
auto renderActivity =
@@ -516,6 +514,62 @@ class ProgressBar : public Logger
516514
return s;
517515
};
518516

517+
auto renderSizeActivity = [&](ActivityType type, const std::string & itemFmt = "%s") {
518+
auto & act = state.activitiesByType[type];
519+
uint64_t done = act.done, expected = act.done, running = 0, failed = act.failed;
520+
for (auto & j : act.its) {
521+
done += j.second->done;
522+
expected += j.second->expected;
523+
running += j.second->running;
524+
failed += j.second->failed;
525+
}
526+
527+
expected = std::max(expected, act.expected);
528+
529+
size_t unitIdx;
530+
std::string s;
531+
532+
if (running || done || expected || failed) {
533+
if (running)
534+
if (expected != 0) {
535+
unitIdx = getSizeUnit(std::max({running, done, expected}));
536+
s =
537+
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL "/%s",
538+
renderSizeWithoutUnit(running, unitIdx),
539+
renderSizeWithoutUnit(done, unitIdx),
540+
renderSizeWithoutUnit(expected, unitIdx));
541+
} else {
542+
unitIdx = getSizeUnit(std::max(running, done));
543+
s =
544+
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL,
545+
renderSizeWithoutUnit(running, unitIdx),
546+
renderSizeWithoutUnit(done, unitIdx));
547+
}
548+
else if (expected != done)
549+
if (expected != 0) {
550+
unitIdx = getSizeUnit(std::max(done, expected));
551+
s =
552+
fmt(ANSI_GREEN "%s" ANSI_NORMAL "/%s",
553+
renderSizeWithoutUnit(done, unitIdx),
554+
renderSizeWithoutUnit(expected, unitIdx));
555+
} else {
556+
unitIdx = getSizeUnit(done);
557+
s = fmt(ANSI_GREEN "%s" ANSI_NORMAL, renderSizeWithoutUnit(done, unitIdx));
558+
}
559+
else {
560+
unitIdx = getSizeUnit(done);
561+
s = fmt(done ? ANSI_GREEN "%s" ANSI_NORMAL : "%s", renderSizeWithoutUnit(done, unitIdx));
562+
}
563+
s = fmt("%s %siB", s, getUnitSuffix(unitIdx));
564+
s = fmt(itemFmt, s);
565+
566+
if (failed)
567+
s += fmt(" (" ANSI_RED "%s failed" ANSI_NORMAL ")", renderSize(failed));
568+
}
569+
570+
return s;
571+
};
572+
519573
auto showActivity =
520574
[&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
521575
auto s = renderActivity(type, itemFmt, numberFmt, unit);
@@ -529,7 +583,7 @@ class ProgressBar : public Logger
529583
showActivity(actBuilds, "%s built");
530584

531585
auto s1 = renderActivity(actCopyPaths, "%s copied");
532-
auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);
586+
auto s2 = renderSizeActivity(actCopyPath);
533587

534588
if (!s1.empty() || !s2.empty()) {
535589
if (!res.empty())
@@ -545,12 +599,12 @@ class ProgressBar : public Logger
545599
}
546600
}
547601

548-
showActivity(actFileTransfer, "%s MiB DL", "%.1f", MiB);
602+
renderSizeActivity(actFileTransfer, "%s DL");
549603

550604
{
551605
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
552606
if (s != "") {
553-
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
607+
s += fmt(", %s / %d inodes freed", renderSize(state.bytesLinked), state.filesLinked);
554608
if (!res.empty())
555609
res += ", ";
556610
res += s;

src/libutil-tests/util.cc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,44 @@ TEST(string2Int, trivialConversions)
146146
ASSERT_EQ(string2Int<int>("-100"), -100);
147147
}
148148

149+
/* ----------------------------------------------------------------------------
150+
* getSizeUnit
151+
* --------------------------------------------------------------------------*/
152+
153+
TEST(getSizeUnit, misc)
154+
{
155+
ASSERT_EQ(getSizeUnit(0), 0);
156+
ASSERT_EQ(getSizeUnit(100), 0);
157+
ASSERT_EQ(getSizeUnit(100), 0);
158+
ASSERT_EQ(getSizeUnit(972), 0);
159+
ASSERT_EQ(getSizeUnit(973), 0); // FIXME: should round down
160+
ASSERT_EQ(getSizeUnit(1024), 0);
161+
ASSERT_EQ(getSizeUnit(-1024), 0);
162+
ASSERT_EQ(getSizeUnit(1024 * 1024), 1);
163+
ASSERT_EQ(getSizeUnit(1100 * 1024), 2);
164+
ASSERT_EQ(getSizeUnit(2ULL * 1024 * 1024 * 1024), 3);
165+
ASSERT_EQ(getSizeUnit(2100ULL * 1024 * 1024 * 1024), 4);
166+
}
167+
168+
/* ----------------------------------------------------------------------------
169+
* renderSizeWithoutUnit
170+
* --------------------------------------------------------------------------*/
171+
172+
TEST(renderSizeWithoutUnit, misc)
173+
{
174+
ASSERT_EQ(renderSizeWithoutUnit(0, 0, true), " 0.0");
175+
ASSERT_EQ(renderSizeWithoutUnit(100, 0, true), " 0.1");
176+
ASSERT_EQ(renderSizeWithoutUnit(100, 0), "0.1");
177+
ASSERT_EQ(renderSizeWithoutUnit(972, 0, true), " 0.9");
178+
ASSERT_EQ(renderSizeWithoutUnit(973, 0, true), " 1.0"); // FIXME: should round down
179+
ASSERT_EQ(renderSizeWithoutUnit(1024, 0, true), " 1.0");
180+
ASSERT_EQ(renderSizeWithoutUnit(-1024, 0, true), " -1.0");
181+
ASSERT_EQ(renderSizeWithoutUnit(1024 * 1024, 1, true), "1024.0");
182+
ASSERT_EQ(renderSizeWithoutUnit(1100 * 1024, 2, true), " 1.1");
183+
ASSERT_EQ(renderSizeWithoutUnit(2ULL * 1024 * 1024 * 1024, 3, true), " 2.0");
184+
ASSERT_EQ(renderSizeWithoutUnit(2100ULL * 1024 * 1024 * 1024, 4, true), " 2.1");
185+
}
186+
149187
/* ----------------------------------------------------------------------------
150188
* renderSize
151189
* --------------------------------------------------------------------------*/

src/libutil/include/nix/util/util.hh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ N string2IntWithUnitPrefix(std::string_view s)
9999
throw UsageError("'%s' is not an integer", s);
100100
}
101101

102+
size_t getSizeUnit(int64_t value);
103+
104+
std::string renderSizeWithoutUnit(int64_t value, size_t unitIdx, bool align = false);
105+
106+
char getUnitSuffix(size_t unitIdx);
107+
102108
/**
103109
* Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6
104110
* GiB`. If `align` is set, the number will be right-justified by

src/libutil/util.cc

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,36 @@ std::optional<N> string2Float(const std::string_view s)
132132
template std::optional<double> string2Float<double>(const std::string_view s);
133133
template std::optional<float> string2Float<float>(const std::string_view s);
134134

135-
std::string renderSize(int64_t value, bool align)
135+
static const int64_t conversionNumber = 1024;
136+
static const std::array<char, 9> untiSuffixes{{'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}};
137+
138+
size_t getSizeUnit(int64_t value)
136139
{
137-
static const std::array<char, 9> prefixes{{'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'}};
138-
size_t power = 0;
139-
double abs_value = std::abs(value);
140-
while (abs_value > 1024 && power < prefixes.size()) {
141-
++power;
142-
abs_value /= 1024;
140+
size_t unitIdx = 0;
141+
uint64_t absValue = std::abs(value);
142+
while (absValue > conversionNumber && unitIdx < untiSuffixes.size()) {
143+
++unitIdx;
144+
absValue /= conversionNumber;
143145
}
144-
double res = (double) value / std::pow(1024.0, power);
145-
return fmt(align ? "%6.1f %ciB" : "%.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power));
146+
return unitIdx;
147+
}
148+
149+
std::string renderSizeWithoutUnit(int64_t value, size_t unitIdx, bool align)
150+
{
151+
// bytes should also displayed as KiB => 100 Bytes => 0.1 KiB
152+
double result = (double) value / std::pow(conversionNumber, std::max((size_t) 1, unitIdx));
153+
return fmt(align ? "%6.1f" : "%.1f", result);
154+
}
155+
156+
char getUnitSuffix(size_t unitIdx)
157+
{
158+
return untiSuffixes.at(unitIdx);
159+
}
160+
161+
std::string renderSize(int64_t value, bool align)
162+
{
163+
size_t unitIdx = getSizeUnit(value);
164+
return fmt("%s %ciB", renderSizeWithoutUnit(value, unitIdx, align), untiSuffixes.at(unitIdx));
146165
}
147166

148167
bool hasPrefix(std::string_view s, std::string_view prefix)

0 commit comments

Comments
 (0)