Skip to content

Commit 7075567

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

File tree

4 files changed

+172
-14
lines changed

4 files changed

+172
-14
lines changed

src/libmain/progress-bar.cc

Lines changed: 62 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,65 @@ 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+
std::optional<size_t> commonUnitIdx;
530+
std::string s;
531+
532+
if (running || done || expected || failed) {
533+
if (running)
534+
if (expected != 0) {
535+
commonUnitIdx = getCommonSizeUnit({running, done, expected});
536+
s =
537+
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL "/%s",
538+
!commonUnitIdx ? renderSize(running) : renderSizeWithoutUnit(running, *commonUnitIdx),
539+
!commonUnitIdx ? renderSize(done) : renderSizeWithoutUnit(done, *commonUnitIdx),
540+
!commonUnitIdx ? renderSize(expected) : renderSizeWithoutUnit(expected, *commonUnitIdx));
541+
} else {
542+
commonUnitIdx = getCommonSizeUnit({running, done});
543+
s =
544+
fmt(ANSI_BLUE "%s" ANSI_NORMAL "/" ANSI_GREEN "%s" ANSI_NORMAL,
545+
!commonUnitIdx ? renderSize(running) : renderSizeWithoutUnit(running, *commonUnitIdx),
546+
!commonUnitIdx ? renderSize(done) : renderSizeWithoutUnit(done, *commonUnitIdx));
547+
}
548+
else if (expected != done)
549+
if (expected != 0) {
550+
commonUnitIdx = getCommonSizeUnit({done, expected});
551+
s =
552+
fmt(ANSI_GREEN "%s" ANSI_NORMAL "/%s",
553+
!commonUnitIdx ? renderSize(done) : renderSizeWithoutUnit(done, *commonUnitIdx),
554+
!commonUnitIdx ? renderSize(expected) : renderSizeWithoutUnit(expected, *commonUnitIdx));
555+
} else {
556+
commonUnitIdx = getSizeUnit(done);
557+
s = fmt(ANSI_GREEN "%s" ANSI_NORMAL, renderSizeWithoutUnit(done, *commonUnitIdx));
558+
}
559+
else {
560+
commonUnitIdx = getSizeUnit(done);
561+
s = fmt(done ? ANSI_GREEN "%s" ANSI_NORMAL : "%s", renderSizeWithoutUnit(done, *commonUnitIdx));
562+
}
563+
564+
if (commonUnitIdx)
565+
s = fmt("%s %siB", s, getUnitSuffix(*commonUnitIdx));
566+
567+
s = fmt(itemFmt, s);
568+
569+
if (failed)
570+
s += fmt(" (" ANSI_RED "%s failed" ANSI_NORMAL ")", renderSize(failed));
571+
}
572+
573+
return s;
574+
};
575+
519576
auto showActivity =
520577
[&](ActivityType type, const std::string & itemFmt, const std::string & numberFmt = "%d", double unit = 1) {
521578
auto s = renderActivity(type, itemFmt, numberFmt, unit);
@@ -529,7 +586,7 @@ class ProgressBar : public Logger
529586
showActivity(actBuilds, "%s built");
530587

531588
auto s1 = renderActivity(actCopyPaths, "%s copied");
532-
auto s2 = renderActivity(actCopyPath, "%s MiB", "%.1f", MiB);
589+
auto s2 = renderSizeActivity(actCopyPath);
533590

534591
if (!s1.empty() || !s2.empty()) {
535592
if (!res.empty())
@@ -545,12 +602,12 @@ class ProgressBar : public Logger
545602
}
546603
}
547604

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

550607
{
551608
auto s = renderActivity(actOptimiseStore, "%s paths optimised");
552609
if (s != "") {
553-
s += fmt(", %.1f MiB / %d inodes freed", state.bytesLinked / MiB, state.filesLinked);
610+
s += fmt(", %s / %d inodes freed", renderSize(state.bytesLinked), state.filesLinked);
554611
if (!res.empty())
555612
res += ", ";
556613
res += s;

src/libutil-tests/util.cc

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,59 @@ 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+
* getCommonSizeUnit
170+
* --------------------------------------------------------------------------*/
171+
172+
TEST(getCommonSizeUnit, misc)
173+
{
174+
ASSERT_EQ(*getCommonSizeUnit({0}), 0);
175+
ASSERT_EQ(*getCommonSizeUnit({0, 100}), 0);
176+
ASSERT_EQ(*getCommonSizeUnit({100, 0}), 0);
177+
ASSERT_EQ(getCommonSizeUnit({100, 1024 * 1024}), std::nullopt);
178+
ASSERT_EQ(getCommonSizeUnit({1024 * 1024, 100}), std::nullopt);
179+
ASSERT_EQ(*getCommonSizeUnit({1024 * 1024, 1024 * 1024}), 1);
180+
ASSERT_EQ(*getCommonSizeUnit({2100ULL * 1024 * 1024 * 1024, 2100ULL * 1024 * 1024 * 1024}), 4);
181+
}
182+
183+
/* ----------------------------------------------------------------------------
184+
* renderSizeWithoutUnit
185+
* --------------------------------------------------------------------------*/
186+
187+
TEST(renderSizeWithoutUnit, misc)
188+
{
189+
ASSERT_EQ(renderSizeWithoutUnit(0, 0, true), " 0.0");
190+
ASSERT_EQ(renderSizeWithoutUnit(100, 0, true), " 0.1");
191+
ASSERT_EQ(renderSizeWithoutUnit(100, 0), "0.1");
192+
ASSERT_EQ(renderSizeWithoutUnit(972, 0, true), " 0.9");
193+
ASSERT_EQ(renderSizeWithoutUnit(973, 0, true), " 1.0"); // FIXME: should round down
194+
ASSERT_EQ(renderSizeWithoutUnit(1024, 0, true), " 1.0");
195+
ASSERT_EQ(renderSizeWithoutUnit(-1024, 0, true), " -1.0");
196+
ASSERT_EQ(renderSizeWithoutUnit(1024 * 1024, 1, true), "1024.0");
197+
ASSERT_EQ(renderSizeWithoutUnit(1100 * 1024, 2, true), " 1.1");
198+
ASSERT_EQ(renderSizeWithoutUnit(2ULL * 1024 * 1024 * 1024, 3, true), " 2.0");
199+
ASSERT_EQ(renderSizeWithoutUnit(2100ULL * 1024 * 1024 * 1024, 4, true), " 2.1");
200+
}
201+
149202
/* ----------------------------------------------------------------------------
150203
* renderSize
151204
* --------------------------------------------------------------------------*/

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,18 @@ 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+
/**
105+
* Returns the unit index if all values would be rendered using the same unit
106+
* otherwise returns `std::nullopt`.
107+
*/
108+
std::optional<size_t> getCommonSizeUnit(std::initializer_list<int64_t> values);
109+
110+
std::string renderSizeWithoutUnit(int64_t value, size_t unitIdx, bool align = false);
111+
112+
char getUnitSuffix(size_t unitIdx);
113+
102114
/**
103115
* Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6
104116
* GiB`. If `align` is set, the number will be right-justified by

src/libutil/util.cc

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,53 @@ 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)
139+
{
140+
size_t unitIdx = 0;
141+
uint64_t absValue = std::abs(value);
142+
while (absValue > conversionNumber && unitIdx < untiSuffixes.size()) {
143+
++unitIdx;
144+
absValue /= conversionNumber;
145+
}
146+
return unitIdx;
147+
}
148+
149+
std::optional<size_t> getCommonSizeUnit(std::initializer_list<int64_t> values)
136150
{
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;
151+
assert(values.size() > 0);
152+
153+
auto it = values.begin();
154+
size_t unitIdx = getSizeUnit(*it);
155+
it++;
156+
157+
for (; it != values.end(); it++) {
158+
if (unitIdx != getSizeUnit(*it)) {
159+
return std::nullopt;
160+
}
143161
}
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));
162+
163+
return unitIdx;
164+
}
165+
166+
std::string renderSizeWithoutUnit(int64_t value, size_t unitIdx, bool align)
167+
{
168+
// bytes should also displayed as KiB => 100 Bytes => 0.1 KiB
169+
double result = (double) value / std::pow(conversionNumber, std::max((size_t) 1, unitIdx));
170+
return fmt(align ? "%6.1f" : "%.1f", result);
171+
}
172+
173+
char getUnitSuffix(size_t unitIdx)
174+
{
175+
return untiSuffixes.at(unitIdx);
176+
}
177+
178+
std::string renderSize(int64_t value, bool align)
179+
{
180+
size_t unitIdx = getSizeUnit(value);
181+
return fmt("%s %ciB", renderSizeWithoutUnit(value, unitIdx, align), untiSuffixes.at(unitIdx));
146182
}
147183

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

0 commit comments

Comments
 (0)