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