1 #include "util/stream/multi_progress.hh"
2 
3 // TODO: merge some functionality with the simple progress bar?
4 #include "util/ersatz_progress.hh"
5 
6 #include <iostream>
7 #include <limits>
8 
9 #include <cstring>
10 
11 #if !defined(_WIN32) && !defined(_WIN64)
12 #include <unistd.h>
13 #endif
14 
15 namespace util { namespace stream {
16 
17 namespace {
18 const char kDisplayCharacters[] = "-+*#0123456789";
19 
Next(unsigned char stone,uint64_t complete)20 uint64_t Next(unsigned char stone, uint64_t complete) {
21   return (static_cast<uint64_t>(stone + 1) * complete + MultiProgress::kWidth - 1) / MultiProgress::kWidth;
22 }
23 
24 } // namespace
25 
MultiProgress()26 MultiProgress::MultiProgress() : active_(false), complete_(std::numeric_limits<uint64_t>::max()), character_handout_(0) {}
27 
~MultiProgress()28 MultiProgress::~MultiProgress() {
29   if (active_ && complete_ != std::numeric_limits<uint64_t>::max())
30     std::cerr << '\n';
31 }
32 
Activate()33 void MultiProgress::Activate() {
34   active_ =
35 #if !defined(_WIN32) && !defined(_WIN64)
36     // Is stderr a terminal?
37     (isatty(2) == 1)
38 #else
39     true
40 #endif
41     ;
42 }
43 
SetTarget(uint64_t complete)44 void MultiProgress::SetTarget(uint64_t complete) {
45   if (!active_) return;
46   complete_ = complete;
47   if (!complete) complete_ = 1;
48   memset(display_, 0, sizeof(display_));
49   character_handout_ = 0;
50   std::cerr << kProgressBanner;
51 }
52 
Add()53 WorkerProgress MultiProgress::Add() {
54   if (!active_)
55     return WorkerProgress(std::numeric_limits<uint64_t>::max(), *this, '\0');
56   std::size_t character_index;
57   {
58     boost::unique_lock<boost::mutex> lock(mutex_);
59     character_index = character_handout_++;
60     if (character_handout_ == sizeof(kDisplayCharacters) - 1)
61       character_handout_ = 0;
62   }
63   return WorkerProgress(Next(0, complete_), *this, kDisplayCharacters[character_index]);
64 }
65 
Finished()66 void MultiProgress::Finished() {
67   if (!active_ || complete_ == std::numeric_limits<uint64_t>::max()) return;
68   std::cerr << '\n';
69   complete_ = std::numeric_limits<uint64_t>::max();
70 }
71 
Milestone(WorkerProgress & worker)72 void MultiProgress::Milestone(WorkerProgress &worker) {
73   if (!active_ || complete_ == std::numeric_limits<uint64_t>::max()) return;
74   unsigned char stone = std::min(static_cast<uint64_t>(kWidth), worker.current_ * kWidth / complete_);
75   for (char *i = &display_[worker.stone_]; i < &display_[stone]; ++i) {
76     *i = worker.character_;
77   }
78   worker.next_ = Next(stone, complete_);
79   worker.stone_ = stone;
80   {
81     boost::unique_lock<boost::mutex> lock(mutex_);
82     std::cerr << '\r' << display_ << std::flush;
83   }
84 }
85 
86 }} // namespaces
87