#include "config.h" #ifdef HAVE_UNISTD_H #include #endif #include #include #include // #include #include #include #include "types.h" #include "error.h" #include "fs.h" #include "estring.h" #include "strfmt.h" #include "rmath.h" #include "timer.h" /** Given a timer value, return a string in the form of "YYYY.MM.DD HH:MM:SS" */ const std::string make_time_string_(timer::value_type a_t) { std::string es; struct tm* l = 0; estring tmp_str; std::string str; l = localtime(&a_t); if (l == 0) { TRY_nomem(es = "Could not convert time_t to localtime: \""); TRY_nomem(es += estring(a_t)); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } TRY(str += estring(l->tm_year + 1900).fmt_str(4,estring::right,'0','x'), "Error generating string from year"); TRY_nomem(str += "."); TRY(str += estring(l->tm_mon + 1).fmt_str(2,estring::right,'0','x'), "Error generating string from month"); TRY_nomem(str += "."); TRY(str += estring(l->tm_mday).fmt_str(2,estring::right,'0','x'), "Error generating string from day"); TRY_nomem(str += " "); TRY(str += estring(l->tm_hour).fmt_str(2,estring::right,'0','x'), "Error generating string from hour"); TRY_nomem(str += ":"); TRY(str += estring(l->tm_min).fmt_str(2,estring::right,'0','x'), "Error generating string from minute"); TRY_nomem(str += ":"); TRY(str += estring(l->tm_sec).fmt_str(2,estring::right,'0','x'), "Error generating string from seconds"); return(str); } /** C'tor */ timer::timer() { clear(); start(); } /** C'tor */ timer::timer(const timer& a_timer) { assign(a_timer); } /** C'tor */ timer::timer(const value_type a_start) { assign(a_start); } /** C'tor */ timer::timer(const value_type a_start, const value_type a_stop) { assign(a_start,a_stop); } /** Clear the timer */ void timer::clear(void) { m_start = 0; m_stop = 0; m_started = false; m_stopped = false; m_duration = 0; m_use_localtime = true; } /** Set the timer start value */ void timer::mf_start_value(const timer::value_type a_t) { m_start = a_t; m_started = true; } /** Set the timer stop value */ void timer::mf_stop_value(const timer::value_type a_t) { m_stop = a_t; m_stopped = true; } /** Calculate the duration between start and stop */ const timer::duration_type timer::mf_calculate_duration( const timer::value_type a_start, const timer::value_type a_stop ) const { duration_type duration; duration = difftime(a_stop,a_start); return(duration); } /** Start (or restart) the timer */ void timer::start(void) { mf_start_value(time(0)); } /** Stop the timer */ void timer::stop(void) { mf_stop_value(time(0)); m_duration = mf_calculate_duration(m_start,m_stop); } /** Return the timer start value */ const timer::value_type timer::start_value(void) const { return(m_start); } /** Return the timer stop value */ const timer::value_type timer::stop_value(void) const { return(m_stop); } /** Assign timer values from another timer instance */ void timer::assign(const timer& a_timer) { clear(); mf_start_value(a_timer.start_value()); mf_stop_value(a_timer.stop_value()); m_started = a_timer.is_started(); m_stopped = a_timer.is_stopped(); if (m_stopped) m_duration = mf_calculate_duration(m_start,m_stop); } /** Assign timer from a start value */ void timer::assign(const timer::value_type a_start) { clear(); mf_start_value(a_start); } /** Assign timer from start and stop values */ void timer::assign( const timer::value_type a_start, const timer::value_type a_stop) { clear(); mf_start_value(a_start); mf_stop_value(a_stop); m_duration = mf_calculate_duration(m_start,m_stop); } /** Assignment */ timer& timer::operator=(const timer& a_timer) { assign(a_timer); return(*this); } /** Set whether to use localtime or GMT when constructing strings */ void timer::use_localtime(const bool a_switch) { m_use_localtime = a_switch; } /** Return whether to use localtime or GMT */ const bool timer::use_localtime(void) const { return(m_use_localtime); } /** Generate a string in a regular human-readable format */ const std::string timer::mf_make_timer_string(const value_type a_t) const { std::string es; struct tm* l = 0; std::string str; const char* month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if (m_use_localtime) { l = localtime(&a_t); if (l == 0) { TRY_nomem(es = "Could not convert time_t to localtime: \""); TRY_nomem(es += estring(a_t)); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } } else { l = gmtime(&a_t); if (l == 0) { TRY_nomem(es = "Could not convert time_t to GMT: \""); TRY_nomem(es += estring(a_t)); TRY_nomem(es += "\""); throw(ERROR(errno,es)); } } TRY(str += estring(l->tm_year + 1900).fmt_str(4,estring::right,'0','x'), "Error generating string from year"); TRY_nomem(str += " "); TRY(str += estring(month_names[l->tm_mon]).fmt_str(3,estring::right,' ','x'), "Error generating string from month"); TRY_nomem(str += " "); TRY(str += estring(l->tm_mday).fmt_str(2,estring::right,' ','x'), "Error generating string from day"); TRY_nomem(str += " "); TRY(str += estring(l->tm_hour).fmt_str(2,estring::right,'0','x'), "Error generating string from hour"); TRY_nomem(str += ":"); TRY(str += estring(l->tm_min).fmt_str(2,estring::right,'0','x'), "Error generating string from minute"); TRY_nomem(str += ":"); TRY(str += estring(l->tm_sec).fmt_str(2,estring::right,'0','x'), "Error generating string from seconds"); return(str); } /** Generate a string in a timestamp format */ const std::string timer::mf_make_string(const timer::value_type a_t) const { std::string str; TRY_nomem(str = mf_make_timer_string(a_t)); return(str); } /** Generate a duration string */ const std::string timer::mf_make_string(const timer::duration_type a_d) const { uint64 deciseconds = 0; uint64 seconds = 0; uint64 minutes = 0; uint64 hours = 0; uint64 days = 0; uint64 years = 0; std::string str; bool negative = false; if (a_d < 0) { negative = true; deciseconds = static_cast(-a_d * 10.0); } else deciseconds = static_cast(a_d * 10.0); if (deciseconds >= 10) { seconds = deciseconds / 10; deciseconds %= 10; } if (seconds >= 60) { minutes = seconds / 60; seconds %= 60; } if (minutes >= 60) { hours = minutes / 60; minutes %= 60; } if (hours >= 24) { days = hours / 24; hours %= 24; } if (days >= 365) { years = days / 365; days %= 365; } if (negative) { TRY_nomem(str += "-"); } if (years > 0) { TRY_nomem(str += estring(years)); TRY_nomem(str += "y "); } if ((years > 0) || (days > 0)) { TRY_nomem(str += estring(days)); TRY_nomem(str += "d "); } if ((years > 0) || (days > 0) || (hours > 0)) { TRY(str += estring(hours).fmt_str(2,estring::right,'0','x'), "Error generating string from hours"); TRY_nomem(str += ":"); } TRY(str += estring(minutes).fmt_str(2,estring::right,'0','x'), "Error generating string from minutes"); TRY_nomem(str += ":"); TRY(str += estring(seconds).fmt_str(2,estring::right,'0','x'), "Error generating string from seconds"); TRY_nomem(str += "."); TRY(str += estring(deciseconds).fmt_str(1,estring::right,'0','x'), "Error generating string from deciseconds"); return(str); } /** Generate a started-at string */ const std::string timer::started_at(void) const { std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Request for start time from an unstarted timer")); TRY_nomem(str = mf_make_string(m_start)); return(str); } /** Generate a stopped-at string */ const std::string timer::stopped_at(void) const { std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Request for stop time from an unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Request for stop time from a running timer")); TRY_nomem(str = mf_make_string(m_stop)); return(str); } /** Generate a duration string */ const std::string timer::duration(void) const { std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Request for stop time from an unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Request for duration from a running timer")); TRY_nomem(str = mf_make_string(m_duration)); return(str); } /** Return whether or not the timer has been started */ const bool timer::is_started(void) const { return(m_started); } /** Return whether or not the timer has been stopped */ const bool timer::is_stopped(void) const { return(m_stopped); } /** Reutrn the duration in seconds */ const timer::duration_type timer::duration_secs(void) const { if (!is_started()) throw(INTERNAL_ERROR(0,"Request for duration from an unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Request for duration from a running timer")); return(m_duration); } /** Return the duration in minutes */ const timer::duration_type timer::duration_mins(void) const { duration_type value; value = duration_secs()/static_cast(60.0); return(value); } /** Return the duration in hours */ const timer::duration_type timer::duration_hours(void) const { duration_type value; value = duration_mins()/static_cast(60.0); return(value); } /** Return the duration in days */ const timer::duration_type timer::duration_days(void) const { duration_type value; value = duration_hours()/static_cast(24.0); return(value); } /** Return the duration in years */ const timer::duration_type timer::duration_years(void) const { duration_type value; value = duration_days()/static_cast(365.0); return(value); } /** Given a percent-complete for some unknown task, estimate a time to completion */ const std::string timer::eta(unsigned int a_percent_complete) const { safe_num total_duration; safe_num eta; std::string es; std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for a running timer")); if (a_percent_complete == 0) { TRY_nomem(str = "??:??.?"); return(str); } TRY_nomem(es = "Could not calculate ETA"); TRY( total_duration = m_duration; total_duration *= 100; total_duration /= a_percent_complete; eta = total_duration - safe_num(m_duration); ,es); TRY_nomem(str = mf_make_string(eta.value())); return(str); } /** Given a step number complete out of some total number of steps for some unknown task, estimate a time to completion */ const std::string timer::eta( unsigned int a_complete, unsigned int a_total) const { safe_num percent_complete; std::string es; std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for a running timer")); if (a_total == 0) { TRY_nomem(str = "??:??.?"); return(str); } TRY_nomem(es = "Could not calculate ETA"); TRY( percent_complete = a_complete; percent_complete *= 100; percent_complete /= a_total; ,es); TRY_nomem(str = eta(percent_complete.value())); return(str); } /** Given a number of bytes, estimate the BPS */ const std::string timer::bps(uint64 a_bytes) const { uint64 bps; std::string str; if (!is_started()) throw(INTERNAL_ERROR(0,"Attempt to calculate bps for unstarted timer")); if (!is_stopped()) throw(INTERNAL_ERROR(0,"Attempt to calculate bps for a running timer")); if (m_duration == 0) bps = a_bytes; else bps = static_cast(a_bytes / m_duration); TRY_nomem(str = throughput_to_string(bps)); return(str); } /** Return the current time */ const std::string current_time(void) { std::string str; TRY_nomem(str = make_time_string_(time(0))); return(str); } /** Generate a timstamp string */ std::string stamp(const pid_t a_pid, const int a_indention) { std::string str; int indent; pid_t this_pid; indent = a_indention; TRY_nomem(str = current_time()); TRY_nomem(str += " ["); if (a_pid == 0) this_pid = pid(); else this_pid = a_pid; /* * pid_t has to be static_cast because some compilers wind up calling * estring(const long& ...) instead of estring(const unsigned long& ...), * and then the value in estring can wind up being misinterpreted as a * negative number. * * 4 digits is just a guess. I've never seen a PID over four digits. Is * there an easy, portable way to determine how many digits pid_t could wind * up being? If gcc 2.95.x had numeric_limits<> then it would be... */ TRY_nomem(str += estring(static_cast(this_pid)).fmt_str( 6,estring::right,' ',' ' ) ); TRY_nomem(str += "]"); TRY_nomem(str += "> "); for (; indent > 0; str += " ", --indent); return(str); }