forked from htailor/cpp_progress_bar
-
Notifications
You must be signed in to change notification settings - Fork 3
/
progress_bar.cpp
204 lines (165 loc) · 6.07 KB
/
progress_bar.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include "progress_bar.hpp"
#include <iomanip>
#include <cmath>
#include <cassert>
#include <cstdio>
#include <chrono>
#include <ctime>
#include <sstream>
#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)))
#include <unistd.h>
#if !defined(_POSIX_VERSION)
#include <io.h>
#endif
#else
#include <io.h>
#endif
const size_t kMessageSize = 20;
const double kTotalPercentage = 100.0;
const size_t kCharacterWidthPercentage = 7;
const int kDefaultConsoleWidth = 100;
const int kMaxBarWidth = 120;
bool to_terminal(const std::ostream &os) {
#if _WINDOWS
if (os.rdbuf() == std::cout.rdbuf() && !_isatty(_fileno(stdout)))
return false;
if (os.rdbuf() == std::cerr.rdbuf() && !_isatty(_fileno(stderr)))
return false;
#else
if (os.rdbuf() == std::cout.rdbuf() && !isatty(fileno(stdout)))
return false;
if (os.rdbuf() == std::cerr.rdbuf() && !isatty(fileno(stderr)))
return false;
#endif
return true;
}
ProgressBar::ProgressBar(uint64_t total,
const std::string &description,
std::ostream &out_,
bool silent)
: silent_(silent), total_(total), description_(description) {
if (silent_)
return;
frequency_update = std::max(static_cast<uint64_t>(1), total_ / 1000);
out = &out_;
if ((logging_mode_ = !to_terminal(*out)))
*out << description_ << std::endl;
description_.resize(kMessageSize, ' ');
ShowProgress(0);
if (progress_ == total_)
*out << std::endl;
}
ProgressBar::~ProgressBar() {
if (progress_ != total_) {
// this is not supposed to happen, but may be useful for debugging
ShowProgress(progress_);
if (!silent_)
*out << "\n";
}
}
void ProgressBar::SetFrequencyUpdate(uint64_t frequency_update_) {
std::lock_guard<std::mutex> lock(mu_);
if(frequency_update_ > total_){
frequency_update = total_; // prevents crash if freq_updates_ > total_
} else{
frequency_update = frequency_update_;
}
}
void ProgressBar::SetStyle(char unit_bar, char unit_space) {
std::lock_guard<std::mutex> lock(mu_);
unit_bar_ = unit_bar;
unit_space_ = unit_space;
}
int ProgressBar::GetConsoleWidth() const {
int width = kDefaultConsoleWidth;
#ifdef _WINDOWS
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
width = csbi.srWindow.Right - csbi.srWindow.Left;
#else
struct winsize win;
if (ioctl(0, TIOCGWINSZ, &win) != -1)
width = win.ws_col;
#endif
return width;
}
int ProgressBar::GetBarLength() const {
// get console width and according adjust the length of the progress bar
return std::min(GetConsoleWidth(), kMaxBarWidth)
- 9
- description_.size()
- kCharacterWidthPercentage
- std::floor(std::log10(std::max((uint64_t)2, total_)) + 1) * 2;
}
std::string get_progress_summary(double progress_ratio) {
std::string buffer = std::string(kCharacterWidthPercentage, ' ');
// in some implementations, snprintf always appends null terminal character
snprintf((char *)buffer.data(), kCharacterWidthPercentage,
"%5.1f%%", progress_ratio * kTotalPercentage);
// erase the last null terminal character
buffer.pop_back();
return buffer;
}
void ProgressBar::ShowProgress(uint64_t progress) const {
if (silent_)
return;
std::lock_guard<std::mutex> lock(mu_);
// calculate percentage of progress
double progress_ratio = total_ ? static_cast<double>(progress) / total_
: 1.0;
assert(progress_ratio >= 0.0);
assert(progress_ratio <= 1.0);
if (logging_mode_) {
// get current time
auto now = std::chrono::system_clock::now();
std::time_t time = std::chrono::system_clock::to_time_t(now);
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()) % 1000;
std::stringstream os;
os << std::put_time(std::localtime(&time), "[%F %T.")
<< std::setfill('0') << std::setw(3) << ms.count() << "]\t"
<< get_progress_summary(progress_ratio)
<< ", " + std::to_string(progress) + "/" + std::to_string(total_) + '\n';
*out << os.str() << std::flush;
return;
}
try {
// clear previous progressbar
*out << std::string(buffer_.size(), ' ') + '\r' << std::flush;
buffer_.clear();
// calculate the size of the progress bar
int bar_size = GetBarLength();
if (bar_size < 1)
return;
// write the state of the progress bar
buffer_ = " " + description_
+ " ["
+ std::string(size_t(bar_size * progress_ratio), unit_bar_)
+ std::string(bar_size - size_t(bar_size * progress_ratio), unit_space_)
+ "] " + get_progress_summary(progress_ratio)
+ ", " + std::to_string(progress) + "/" + std::to_string(total_) + '\r';
*out << buffer_ << std::flush;
} catch (uint64_t e) {
std::cerr << "PROGRESS_BAR_EXCEPTION: _idx ("
<< e << ") went out of bounds, greater than total_ ("
<< total_ << ")." << std::endl << std::flush;
}
}
ProgressBar& ProgressBar::operator++() {
return (*this) += 1;
}
ProgressBar& ProgressBar::operator+=(uint64_t delta) {
if (silent_ || !delta)
return *this;
uint64_t after_update
= progress_.fetch_add(delta, std::memory_order_relaxed) + delta;
assert(after_update <= total_);
// determines whether to update the progress bar from frequency_update
if (after_update == total_
|| (after_update - delta) / frequency_update
< after_update / frequency_update)
ShowProgress(after_update);
if (after_update == total_)
*out << std::endl;
return *this;
}