#include "multi_progress.hh" // TODO: merge some functionality with the simple progress bar? #include "../ersatz_progress.hh" #include #include #include #if !defined(_WIN32) && !defined(_WIN64) #include #endif namespace util { namespace stream { namespace { const char kDisplayCharacters[] = "-+*#0123456789"; uint64_t Next(unsigned char stone, uint64_t complete) { return (static_cast(stone + 1) * complete + MultiProgress::kWidth - 1) / MultiProgress::kWidth; } } // namespace MultiProgress::MultiProgress() : active_(false), complete_(std::numeric_limits::max()), character_handout_(0) {} MultiProgress::~MultiProgress() { if (active_ && complete_ != std::numeric_limits::max()) std::cerr << '\n'; } void MultiProgress::Activate() { active_ = #if !defined(_WIN32) && !defined(_WIN64) // Is stderr a terminal? (isatty(2) == 1) #else true #endif ; } void MultiProgress::SetTarget(uint64_t complete) { if (!active_) return; complete_ = complete; if (!complete) complete_ = 1; memset(display_, 0, sizeof(display_)); character_handout_ = 0; std::cerr << kProgressBanner; } WorkerProgress MultiProgress::Add() { if (!active_) return WorkerProgress(std::numeric_limits::max(), *this, '\0'); std::size_t character_index; { boost::unique_lock lock(mutex_); character_index = character_handout_++; if (character_handout_ == sizeof(kDisplayCharacters) - 1) character_handout_ = 0; } return WorkerProgress(Next(0, complete_), *this, kDisplayCharacters[character_index]); } void MultiProgress::Finished() { if (!active_ || complete_ == std::numeric_limits::max()) return; std::cerr << '\n'; complete_ = std::numeric_limits::max(); } void MultiProgress::Milestone(WorkerProgress &worker) { if (!active_ || complete_ == std::numeric_limits::max()) return; unsigned char stone = std::min(static_cast(kWidth), worker.current_ * kWidth / complete_); for (char *i = &display_[worker.stone_]; i < &display_[stone]; ++i) { *i = worker.character_; } worker.next_ = Next(stone, complete_); worker.stone_ = stone; { boost::unique_lock lock(mutex_); std::cerr << '\r' << display_ << std::flush; } } }} // namespaces