1 #include "config.h"
2
3 #ifdef HAVE_UNISTD_H
4 #include <unistd.h>
5 #endif
6
7 #include <iostream>
8 #include <iomanip>
9 #include <string>
10 // #include <sstream>
11
12 #include <time.h>
13 #include <sys/types.h>
14
15 #include "types.h"
16 #include "error.h"
17 #include "fs.h"
18 #include "estring.h"
19 #include "strfmt.h"
20 #include "rmath.h"
21
22 #include "timer.h"
23
24 /** Given a timer value, return a string in the form of "YYYY.MM.DD HH:MM:SS"
25 */
make_time_string_(timer::value_type a_t)26 const std::string make_time_string_(timer::value_type a_t)
27 {
28 std::string es;
29 struct tm* l = 0;
30 estring tmp_str;
31 std::string str;
32
33 l = localtime(&a_t);
34 if (l == 0) {
35 TRY_nomem(es = "Could not convert time_t to localtime: \"");
36 TRY_nomem(es += estring(a_t));
37 TRY_nomem(es += "\"");
38 throw(ERROR(errno,es));
39 }
40
41 TRY(str += estring(l->tm_year + 1900).fmt_str(4,estring::right,'0','x'),
42 "Error generating string from year");
43
44 TRY_nomem(str += ".");
45
46 TRY(str += estring(l->tm_mon + 1).fmt_str(2,estring::right,'0','x'),
47 "Error generating string from month");
48
49 TRY_nomem(str += ".");
50
51 TRY(str += estring(l->tm_mday).fmt_str(2,estring::right,'0','x'),
52 "Error generating string from day");
53
54 TRY_nomem(str += " ");
55
56 TRY(str += estring(l->tm_hour).fmt_str(2,estring::right,'0','x'),
57 "Error generating string from hour");
58
59 TRY_nomem(str += ":");
60
61 TRY(str += estring(l->tm_min).fmt_str(2,estring::right,'0','x'),
62 "Error generating string from minute");
63
64 TRY_nomem(str += ":");
65
66 TRY(str += estring(l->tm_sec).fmt_str(2,estring::right,'0','x'),
67 "Error generating string from seconds");
68
69 return(str);
70 }
71
72 /** C'tor */
timer()73 timer::timer()
74 {
75 clear();
76 start();
77 }
78
79 /** C'tor */
timer(const timer & a_timer)80 timer::timer(const timer& a_timer)
81 {
82 assign(a_timer);
83 }
84
85 /** C'tor */
timer(const value_type a_start)86 timer::timer(const value_type a_start)
87 {
88 assign(a_start);
89 }
90
91 /** C'tor */
timer(const value_type a_start,const value_type a_stop)92 timer::timer(const value_type a_start, const value_type a_stop)
93 {
94 assign(a_start,a_stop);
95 }
96
97 /** Clear the timer */
clear(void)98 void timer::clear(void)
99 {
100 m_start = 0;
101 m_stop = 0;
102 m_started = false;
103 m_stopped = false;
104 m_duration = 0;
105 m_use_localtime = true;
106 }
107
108 /** Set the timer start value */
mf_start_value(const timer::value_type a_t)109 void timer::mf_start_value(const timer::value_type a_t)
110 {
111 m_start = a_t;
112 m_started = true;
113 }
114
115 /** Set the timer stop value */
mf_stop_value(const timer::value_type a_t)116 void timer::mf_stop_value(const timer::value_type a_t)
117 {
118 m_stop = a_t;
119 m_stopped = true;
120 }
121
122 /** Calculate the duration between start and stop */
123 const timer::duration_type
mf_calculate_duration(const timer::value_type a_start,const timer::value_type a_stop) const124 timer::mf_calculate_duration(
125 const timer::value_type a_start,
126 const timer::value_type a_stop
127 ) const
128 {
129 duration_type duration;
130
131 duration = difftime(a_stop,a_start);
132
133 return(duration);
134 }
135
136 /** Start (or restart) the timer */
start(void)137 void timer::start(void)
138 {
139 mf_start_value(time(0));
140 }
141
142 /** Stop the timer */
stop(void)143 void timer::stop(void)
144 {
145 mf_stop_value(time(0));
146 m_duration = mf_calculate_duration(m_start,m_stop);
147 }
148
149 /** Return the timer start value */
start_value(void) const150 const timer::value_type timer::start_value(void) const
151 {
152 return(m_start);
153 }
154
155 /** Return the timer stop value */
stop_value(void) const156 const timer::value_type timer::stop_value(void) const
157 {
158 return(m_stop);
159 }
160
161 /** Assign timer values from another timer instance */
assign(const timer & a_timer)162 void timer::assign(const timer& a_timer)
163 {
164 clear();
165 mf_start_value(a_timer.start_value());
166 mf_stop_value(a_timer.stop_value());
167 m_started = a_timer.is_started();
168 m_stopped = a_timer.is_stopped();
169 if (m_stopped)
170 m_duration = mf_calculate_duration(m_start,m_stop);
171 }
172
173 /** Assign timer from a start value */
assign(const timer::value_type a_start)174 void timer::assign(const timer::value_type a_start)
175 {
176 clear();
177 mf_start_value(a_start);
178 }
179
180 /** Assign timer from start and stop values */
assign(const timer::value_type a_start,const timer::value_type a_stop)181 void timer::assign(
182 const timer::value_type a_start,
183 const timer::value_type a_stop)
184 {
185 clear();
186 mf_start_value(a_start);
187 mf_stop_value(a_stop);
188 m_duration = mf_calculate_duration(m_start,m_stop);
189 }
190
191 /** Assignment */
operator =(const timer & a_timer)192 timer& timer::operator=(const timer& a_timer)
193 {
194 assign(a_timer);
195
196 return(*this);
197 }
198
199 /** Set whether to use localtime or GMT when constructing strings */
use_localtime(const bool a_switch)200 void timer::use_localtime(const bool a_switch)
201 {
202 m_use_localtime = a_switch;
203 }
204
205 /** Return whether to use localtime or GMT */
use_localtime(void) const206 const bool timer::use_localtime(void) const
207 {
208 return(m_use_localtime);
209 }
210
211 /** Generate a string in a regular human-readable format */
mf_make_timer_string(const value_type a_t) const212 const std::string timer::mf_make_timer_string(const value_type a_t) const
213 {
214 std::string es;
215 struct tm* l = 0;
216 std::string str;
217 const char* month_names[] = {
218 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
219 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
220 };
221
222 if (m_use_localtime) {
223 l = localtime(&a_t);
224 if (l == 0) {
225 TRY_nomem(es = "Could not convert time_t to localtime: \"");
226 TRY_nomem(es += estring(a_t));
227 TRY_nomem(es += "\"");
228 throw(ERROR(errno,es));
229 }
230 }
231 else {
232 l = gmtime(&a_t);
233 if (l == 0) {
234 TRY_nomem(es = "Could not convert time_t to GMT: \"");
235 TRY_nomem(es += estring(a_t));
236 TRY_nomem(es += "\"");
237 throw(ERROR(errno,es));
238 }
239 }
240
241 TRY(str += estring(l->tm_year + 1900).fmt_str(4,estring::right,'0','x'),
242 "Error generating string from year");
243
244 TRY_nomem(str += " ");
245
246 TRY(str += estring(month_names[l->tm_mon]).fmt_str(3,estring::right,' ','x'),
247 "Error generating string from month");
248
249 TRY_nomem(str += " ");
250
251 TRY(str += estring(l->tm_mday).fmt_str(2,estring::right,' ','x'),
252 "Error generating string from day");
253
254 TRY_nomem(str += " ");
255
256 TRY(str += estring(l->tm_hour).fmt_str(2,estring::right,'0','x'),
257 "Error generating string from hour");
258
259 TRY_nomem(str += ":");
260
261 TRY(str += estring(l->tm_min).fmt_str(2,estring::right,'0','x'),
262 "Error generating string from minute");
263
264 TRY_nomem(str += ":");
265
266 TRY(str += estring(l->tm_sec).fmt_str(2,estring::right,'0','x'),
267 "Error generating string from seconds");
268
269 return(str);
270 }
271
272 /** Generate a string in a timestamp format */
mf_make_string(const timer::value_type a_t) const273 const std::string timer::mf_make_string(const timer::value_type a_t) const
274 {
275 std::string str;
276
277 TRY_nomem(str = mf_make_timer_string(a_t));
278
279 return(str);
280 }
281
282 /** Generate a duration string */
mf_make_string(const timer::duration_type a_d) const283 const std::string timer::mf_make_string(const timer::duration_type a_d) const
284 {
285 uint64 deciseconds = 0;
286 uint64 seconds = 0;
287 uint64 minutes = 0;
288 uint64 hours = 0;
289 uint64 days = 0;
290 uint64 years = 0;
291 std::string str;
292 bool negative = false;
293
294 if (a_d < 0) {
295 negative = true;
296 deciseconds = static_cast<uint64>(-a_d * 10.0);
297 }
298 else
299 deciseconds = static_cast<uint64>(a_d * 10.0);
300
301 if (deciseconds >= 10) {
302 seconds = deciseconds / 10;
303 deciseconds %= 10;
304 }
305 if (seconds >= 60) {
306 minutes = seconds / 60;
307 seconds %= 60;
308 }
309 if (minutes >= 60) {
310 hours = minutes / 60;
311 minutes %= 60;
312 }
313 if (hours >= 24) {
314 days = hours / 24;
315 hours %= 24;
316 }
317 if (days >= 365) {
318 years = days / 365;
319 days %= 365;
320 }
321
322 if (negative) {
323 TRY_nomem(str += "-");
324 }
325
326 if (years > 0) {
327 TRY_nomem(str += estring(years));
328 TRY_nomem(str += "y ");
329 }
330
331 if ((years > 0) || (days > 0)) {
332 TRY_nomem(str += estring(days));
333 TRY_nomem(str += "d ");
334 }
335
336 if ((years > 0) || (days > 0) || (hours > 0)) {
337 TRY(str += estring(hours).fmt_str(2,estring::right,'0','x'),
338 "Error generating string from hours");
339 TRY_nomem(str += ":");
340 }
341
342 TRY(str += estring(minutes).fmt_str(2,estring::right,'0','x'),
343 "Error generating string from minutes");
344
345 TRY_nomem(str += ":");
346
347 TRY(str += estring(seconds).fmt_str(2,estring::right,'0','x'),
348 "Error generating string from seconds");
349
350 TRY_nomem(str += ".");
351
352 TRY(str += estring(deciseconds).fmt_str(1,estring::right,'0','x'),
353 "Error generating string from deciseconds");
354
355 return(str);
356 }
357
358 /** Generate a started-at string */
started_at(void) const359 const std::string timer::started_at(void) const
360 {
361 std::string str;
362
363 if (!is_started())
364 throw(INTERNAL_ERROR(0,"Request for start time from an unstarted timer"));
365 TRY_nomem(str = mf_make_string(m_start));
366
367 return(str);
368 }
369
370 /** Generate a stopped-at string */
stopped_at(void) const371 const std::string timer::stopped_at(void) const
372 {
373 std::string str;
374
375 if (!is_started())
376 throw(INTERNAL_ERROR(0,"Request for stop time from an unstarted timer"));
377 if (!is_stopped())
378 throw(INTERNAL_ERROR(0,"Request for stop time from a running timer"));
379 TRY_nomem(str = mf_make_string(m_stop));
380
381 return(str);
382 }
383
384 /** Generate a duration string */
duration(void) const385 const std::string timer::duration(void) const
386 {
387 std::string str;
388
389 if (!is_started())
390 throw(INTERNAL_ERROR(0,"Request for stop time from an unstarted timer"));
391 if (!is_stopped())
392 throw(INTERNAL_ERROR(0,"Request for duration from a running timer"));
393 TRY_nomem(str = mf_make_string(m_duration));
394
395 return(str);
396 }
397
398 /** Return whether or not the timer has been started */
is_started(void) const399 const bool timer::is_started(void) const
400 {
401 return(m_started);
402 }
403
404 /** Return whether or not the timer has been stopped */
is_stopped(void) const405 const bool timer::is_stopped(void) const
406 {
407 return(m_stopped);
408 }
409
410 /** Reutrn the duration in seconds */
duration_secs(void) const411 const timer::duration_type timer::duration_secs(void) const
412 {
413 if (!is_started())
414 throw(INTERNAL_ERROR(0,"Request for duration from an unstarted timer"));
415 if (!is_stopped())
416 throw(INTERNAL_ERROR(0,"Request for duration from a running timer"));
417
418 return(m_duration);
419 }
420
421 /** Return the duration in minutes */
duration_mins(void) const422 const timer::duration_type timer::duration_mins(void) const
423 {
424 duration_type value;
425
426 value = duration_secs()/static_cast<duration_type>(60.0);
427
428 return(value);
429 }
430
431 /** Return the duration in hours */
duration_hours(void) const432 const timer::duration_type timer::duration_hours(void) const
433 {
434 duration_type value;
435
436 value = duration_mins()/static_cast<duration_type>(60.0);
437
438 return(value);
439 }
440
441 /** Return the duration in days */
duration_days(void) const442 const timer::duration_type timer::duration_days(void) const
443 {
444 duration_type value;
445
446 value = duration_hours()/static_cast<duration_type>(24.0);
447
448 return(value);
449 }
450
451 /** Return the duration in years */
duration_years(void) const452 const timer::duration_type timer::duration_years(void) const
453 {
454 duration_type value;
455
456 value = duration_days()/static_cast<duration_type>(365.0);
457
458 return(value);
459 }
460
461 /** Given a percent-complete for some unknown task, estimate a time to
462 completion */
eta(unsigned int a_percent_complete) const463 const std::string timer::eta(unsigned int a_percent_complete) const
464 {
465 safe_num<duration_type> total_duration;
466 safe_num<duration_type> eta;
467 std::string es;
468 std::string str;
469
470 if (!is_started())
471 throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for unstarted timer"));
472 if (!is_stopped())
473 throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for a running timer"));
474 if (a_percent_complete == 0) {
475 TRY_nomem(str = "??:??.?");
476 return(str);
477 }
478
479 TRY_nomem(es = "Could not calculate ETA");
480 TRY(
481 total_duration = m_duration;
482 total_duration *= 100;
483 total_duration /= a_percent_complete;
484 eta = total_duration - safe_num<duration_type>(m_duration);
485 ,es);
486 TRY_nomem(str = mf_make_string(eta.value()));
487
488 return(str);
489 }
490
491 /** Given a step number complete out of some total number of steps for some
492 unknown task, estimate a time to completion */
eta(unsigned int a_complete,unsigned int a_total) const493 const std::string timer::eta(
494 unsigned int a_complete, unsigned int a_total) const
495 {
496 safe_num<unsigned int> percent_complete;
497 std::string es;
498 std::string str;
499
500 if (!is_started())
501 throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for unstarted timer"));
502 if (!is_stopped())
503 throw(INTERNAL_ERROR(0,"Attempt to calculate ETA for a running timer"));
504 if (a_total == 0) {
505 TRY_nomem(str = "??:??.?");
506 return(str);
507 }
508
509 TRY_nomem(es = "Could not calculate ETA");
510 TRY(
511 percent_complete = a_complete;
512 percent_complete *= 100;
513 percent_complete /= a_total;
514 ,es);
515
516 TRY_nomem(str = eta(percent_complete.value()));
517
518 return(str);
519 }
520
521 /** Given a number of bytes, estimate the BPS */
bps(uint64 a_bytes) const522 const std::string timer::bps(uint64 a_bytes) const
523 {
524 uint64 bps;
525 std::string str;
526
527 if (!is_started())
528 throw(INTERNAL_ERROR(0,"Attempt to calculate bps for unstarted timer"));
529 if (!is_stopped())
530 throw(INTERNAL_ERROR(0,"Attempt to calculate bps for a running timer"));
531
532 if (m_duration == 0)
533 bps = a_bytes;
534 else
535 bps = static_cast<uint64>(a_bytes / m_duration);
536 TRY_nomem(str = throughput_to_string(bps));
537
538 return(str);
539 }
540
541 /** Return the current time */
current_time(void)542 const std::string current_time(void)
543 {
544 std::string str;
545
546 TRY_nomem(str = make_time_string_(time(0)));
547
548 return(str);
549 }
550
551 /** Generate a timstamp string */
stamp(const pid_t a_pid,const int a_indention)552 std::string stamp(const pid_t a_pid, const int a_indention)
553 {
554 std::string str;
555 int indent;
556 pid_t this_pid;
557
558 indent = a_indention;
559 TRY_nomem(str = current_time());
560 TRY_nomem(str += " [");
561 if (a_pid == 0)
562 this_pid = pid();
563 else
564 this_pid = a_pid;
565
566 /*
567 * pid_t has to be static_cast because some compilers wind up calling
568 * estring(const long& ...) instead of estring(const unsigned long& ...),
569 * and then the value in estring can wind up being misinterpreted as a
570 * negative number.
571 *
572 * 4 digits is just a guess. I've never seen a PID over four digits. Is
573 * there an easy, portable way to determine how many digits pid_t could wind
574 * up being? If gcc 2.95.x had numeric_limits<> then it would be...
575 */
576 TRY_nomem(str +=
577 estring(static_cast<unsigned long>(this_pid)).fmt_str(
578 6,estring::right,' ',' '
579 )
580 );
581
582 TRY_nomem(str += "]");
583 TRY_nomem(str += "> ");
584 for (; indent > 0; str += " ", --indent);
585
586 return(str);
587 }
588
589