Line data Source code
1 : // Copyright (c) 2021-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : #pragma once
20 :
21 : // C++ lib
22 : //
23 : #include <cmath>
24 : #include <cstdint>
25 : #include <iomanip>
26 : #include <iostream>
27 : #include <sstream>
28 :
29 :
30 : // C lib
31 : //
32 : #include <stdlib.h>
33 : #include <sys/time.h>
34 :
35 :
36 :
37 :
38 : namespace snapdev
39 : {
40 :
41 : class timespec_ex
42 : : public timespec
43 : {
44 : public:
45 : /** \brief Initialize a timespec_ex to zero.
46 : *
47 : * This constructor is used to initialize a new timespec_ex to the
48 : * default value, which is 0.
49 : */
50 38 : timespec_ex()
51 38 : {
52 38 : set(0L);
53 38 : }
54 :
55 :
56 : /** \brief Initialize a timespec_ex from another.
57 : *
58 : * This constructors allows you to directly copy a timespec_ex
59 : * in another new timespec_ex object.
60 : *
61 : * \param[in] t The timespec_ex to directly copy.
62 : */
63 62 : timespec_ex(timespec_ex const & t)
64 62 : {
65 62 : set(t);
66 62 : }
67 :
68 :
69 : /** \brief Initialize a timespec_ex from a timespec structure.
70 : *
71 : * This constructors allows you to directly set a timespec_ex
72 : * to the specified timespec values.
73 : *
74 : * \param[in] t The timespec to directly copy.
75 : */
76 59 : timespec_ex(timespec const & t)
77 59 : {
78 59 : set(t);
79 59 : }
80 :
81 :
82 : /** \brief Initialize a timespec_ex from seconds and nanoseconds.
83 : *
84 : * This constructors allows you to directly initialize a timespec_ex
85 : * from seconds and nanoseconds.
86 : *
87 : * To create a valid timespec_ex object, you must pass a number
88 : * between 0 and 999'999'999 for the \p nsec parameter.
89 : *
90 : * \param[in] sec The number of seconds.
91 : * \param[in] nsec The number of nano-seconds.
92 : */
93 6 : timespec_ex(time_t sec, long nsec)
94 6 : {
95 6 : set(sec, nsec);
96 6 : }
97 :
98 :
99 : /** \brief Initialize a timespec_ex from an int64_t in nanoseconds.
100 : *
101 : * This constructors allows you to directly set a timespec_ex
102 : * to the specified \p nsec value.
103 : *
104 : * \param[in] nsec The nano-seconds to copy to timespec_ex.
105 : */
106 28 : timespec_ex(std::int64_t nsec)
107 28 : {
108 28 : set(nsec);
109 28 : }
110 :
111 :
112 : /** \brief Initialize a timespec_ex from an double in seconds.
113 : *
114 : * This constructors allows you to directly set a timespec_ex
115 : * to the specified \p sec value.
116 : *
117 : * \param[in] sec The seconds to copy to timespec_ex.
118 : */
119 14 : timespec_ex(double sec)
120 14 : {
121 14 : set(sec);
122 14 : }
123 :
124 :
125 : /** \brief Set the timespec_ex to the specified timespec_ex.
126 : *
127 : * This function copies the specified timespec_ex (\p t) to this
128 : * timespec_ex object.
129 : *
130 : * \param[in] t The timespec to copy in this timespec_ex.
131 : *
132 : * \return A reference to this object.
133 : */
134 47 : timespec_ex & operator = (timespec_ex t)
135 : {
136 47 : tv_sec = t.tv_sec;
137 47 : tv_nsec = t.tv_nsec;
138 47 : return *this;
139 : }
140 :
141 :
142 : /** \brief Set the timespec_ex to the specified timespec.
143 : *
144 : * This function copies the specified timespec to this timespec_ex
145 : * object.
146 : *
147 : * \param[in] t The timespec to copy in this timespec_ex.
148 : *
149 : * \return A reference to this object.
150 : */
151 1 : timespec_ex & operator = (timespec const & t)
152 : {
153 1 : tv_sec = t.tv_sec;
154 1 : tv_nsec = t.tv_nsec;
155 1 : return *this;
156 : }
157 :
158 :
159 : /** \brief Set the timespec_ex to the number of nanoseconds.
160 : *
161 : * This function saves the number of nanoseconds in \p nsec as a
162 : * tv_sec and tv_nsec representation.
163 : *
164 : * \param[in] nsec The nano-seconds to save in this timespec_ex.
165 : *
166 : * \return A reference to this object.
167 : */
168 1 : timespec_ex & operator = (std::int64_t nsec)
169 : {
170 1 : set(nsec);
171 1 : return *this;
172 : }
173 :
174 :
175 : /** \brief Set this timespec_ex to the number of seconds in \p sec.
176 : *
177 : * This function transforms the specified double \p sec in a timespec_ex.
178 : *
179 : * \note
180 : * At this time, the number of nanoseconds is floored.
181 : *
182 : * \param[in] sec The number of seconds defined in a double.
183 : *
184 : * \return A reference to this object.
185 : */
186 3 : timespec_ex & operator = (double sec)
187 : {
188 3 : set(sec);
189 3 : return *this;
190 : }
191 :
192 :
193 : /** \brief Set the timespec_ex to the number of nanoseconds.
194 : *
195 : * This function saves the number of nanoseconds in \p nsec as a
196 : * tv_sec and tv_nsec representation.
197 : *
198 : * \param[in] t The nano-seconds to save in this timespec_ex.
199 : *
200 : * \return A reference to this object.
201 : */
202 62 : void set(timespec_ex const & t)
203 : {
204 62 : tv_sec = t.tv_sec;
205 62 : tv_nsec = t.tv_nsec;
206 62 : }
207 :
208 :
209 : /** \brief Set the timespec_ex to the specified timespec.
210 : *
211 : * This function copies the timespec in \p t in this timespec_ex object.
212 : *
213 : * \param[in] t The timespec to save in this timespec_ex.
214 : *
215 : * \return A reference to this object.
216 : */
217 59 : void set(timespec const & t)
218 : {
219 59 : tv_sec = t.tv_sec;
220 59 : tv_nsec = t.tv_nsec;
221 59 : }
222 :
223 :
224 : /** \brief Set the timespec_ex to the specified values.
225 : *
226 : * This function allows you to set the timespec_ex to the specified
227 : * number of seconds (\p sec) and nano-seconds (\p nsec).
228 : *
229 : * \param[in] sec The new number of seconds.
230 : * \param[in] nsec The new number of nano-seconds.
231 : */
232 6 : void set(time_t sec, long nsec)
233 : {
234 6 : tv_sec = sec;
235 6 : tv_nsec = nsec;
236 6 : }
237 :
238 :
239 : /** \brief Set the timespec_ex to the number of nanoseconds.
240 : *
241 : * This function saves the number of nanoseconds in \p nsec as a
242 : * tv_sec and tv_nsec representation.
243 : *
244 : * \param[in] nsec The nano-seconds to save in this timespec_ex.
245 : *
246 : * \return A reference to this object.
247 : */
248 67 : void set(std::int64_t nsec)
249 : {
250 67 : bool const neg(nsec < 0);
251 67 : if(neg)
252 : {
253 8 : nsec = -nsec;
254 : }
255 67 : tv_sec = static_cast<time_t>(nsec / 1'000'000'000LL);
256 67 : tv_nsec = static_cast<long>(nsec % 1'000'000'000LL);
257 67 : if(neg)
258 : {
259 8 : *this = -*this;
260 : }
261 67 : }
262 :
263 :
264 : /** \brief Set this timespec_ex to the number of seconds in \p sec.
265 : *
266 : * This function transforms the specified double \p sec in a timespec_ex.
267 : *
268 : * \note
269 : * At this time, the number of nanoseconds is floored.
270 : *
271 : * \param[in] sec The number of seconds defined in a double.
272 : *
273 : * \return A reference to this object.
274 : */
275 17 : void set(double sec)
276 : {
277 17 : bool const neg(sec < 0.0);
278 17 : if(neg)
279 : {
280 1 : sec = -sec;
281 : }
282 17 : tv_sec = static_cast<time_t>(floor(sec));
283 17 : tv_nsec = static_cast<long>((sec - floor(sec)) * 1.0e9);
284 17 : if(neg)
285 : {
286 1 : *this = -*this;
287 : }
288 17 : }
289 :
290 : /** \brief Get system time.
291 : *
292 : * This function reads the system time and saves it into this
293 : * timespec_ex object.
294 : *
295 : * \param[in] clk_id The type of clock you want to query.
296 : *
297 : * \return A timespec_ex representing the specified \p clk_id.
298 : */
299 1 : static timespec_ex gettime(clockid_t clk_id = CLOCK_REALTIME)
300 : {
301 1 : timespec_ex result;
302 1 : clock_gettime(clk_id, &result);
303 1 : return result;
304 : }
305 :
306 :
307 : /** \brief Extract the timespec_ex as an int64_t value.
308 : *
309 : * This function transforms a timespec_ex structure in an int64_t
310 : * in nanoseconds.
311 : *
312 : * \return This timespec_ex converted to nanoseconds.
313 : */
314 1 : std::int64_t to_nsec() const
315 : {
316 1 : return tv_nsec
317 1 : + tv_sec * 1'000'000'000LL;
318 : }
319 :
320 :
321 : /** \brief Extract the timespec_ex as a double value.
322 : *
323 : * This function transforms a timespec_ex structure into a double
324 : * in seconds. The nanoseconds are added as one billionth of a
325 : * second.
326 : *
327 : * \return The timespec_ex converted the seconds.
328 : */
329 6 : double to_sec() const
330 : {
331 6 : return static_cast<double>(tv_sec)
332 6 : + static_cast<double>(tv_nsec) / 1.0e9;
333 : }
334 :
335 :
336 : /** \brief Validate this timespec_ex structure.
337 : *
338 : * This function returns true if this timespec_ex structure is considered
339 : * valid.
340 : *
341 : * At this time, the validation consists of verifying that the
342 : * nanoseconds is a number between 0 and 1 billion (maximum excluded).
343 : *
344 : * \note
345 : * Negative timespec_ex are represented by a negative tv_sec. The
346 : * tv_nsec can never be negative after a valid operation.
347 : *
348 : * \return true if the timespec_ex is considered valid.
349 : */
350 7 : bool valid() const
351 : {
352 7 : return tv_nsec < 1'000'000'000LL;
353 : }
354 :
355 :
356 : /** \brief Check whether this timespec_ex is negative.
357 : *
358 : * This function checks whether the number represents a negative
359 : * timespec_ex. This is true if the number of seconds is negative.
360 : *
361 : * \note
362 : * The first negative timespec_ex is { -1, 999,999,999 }.
363 : *
364 : * \return true if the timespec_ex is considered negative.
365 : */
366 72 : bool negative() const
367 : {
368 72 : return tv_sec < 0LL;
369 : }
370 :
371 :
372 : /** \brief Add two timespec_ex together.
373 : *
374 : * This function adds \p rhs to this timespec_ex value and returns a
375 : * new timespec_ex with the result. This timespec_ex is not modified.
376 : *
377 : * \param[in] rhs The right handside to add to this number.
378 : *
379 : * \return A new timespec_ex representing the sum of 'this' and rhs.
380 : */
381 33 : timespec_ex add(timespec_ex const & rhs) const
382 : {
383 33 : bool const lneg(negative());
384 33 : bool const rneg(rhs.negative());
385 :
386 33 : timespec_ex lp(lneg ? -*this : *this);
387 33 : timespec_ex rp(rneg ? -rhs : rhs);
388 :
389 33 : timespec_ex result;
390 :
391 33 : switch((lneg ? 1 : 0) + (rneg ? 2 : 0))
392 : {
393 16 : case 0: // positive + positive
394 : case 3: // negative + negative
395 16 : result.tv_sec = lp.tv_sec + rp.tv_sec;
396 16 : result.tv_nsec = lp.tv_nsec + rp.tv_nsec;
397 16 : break;
398 :
399 3 : case 1: // negative + positive
400 3 : result.tv_sec = rp.tv_sec - lp.tv_sec;
401 3 : result.tv_nsec = rp.tv_nsec - lp.tv_nsec;
402 3 : break;
403 :
404 14 : case 2: // positive + negative
405 14 : result.tv_sec = lp.tv_sec - rp.tv_sec;
406 14 : result.tv_nsec = lp.tv_nsec - rp.tv_nsec;
407 14 : break;
408 :
409 : }
410 :
411 33 : if(result.tv_nsec < 0)
412 : {
413 4 : --result.tv_sec;
414 4 : result.tv_nsec += 1'000'000'000L;
415 : }
416 29 : else if(result.tv_nsec >= 1'000'000'000)
417 : {
418 5 : ++result.tv_sec;
419 5 : result.tv_nsec -= 1'000'000'000;
420 : }
421 :
422 33 : if(lneg && rneg)
423 : {
424 2 : result = -result;
425 : }
426 :
427 33 : return result;
428 : }
429 :
430 :
431 : /** \brief Compare two timespec_ex together.
432 : *
433 : * This function compares two timespecs and determine whether they
434 : * are equal (0), 'this' is smaller (-1) or \p rhs is smaller (1).
435 : *
436 : * \param[in] rhs The right handside to compare.
437 : *
438 : * \return -1, 0, or 1 depending on the order between \p lhs and \p rhs.
439 : */
440 97 : int compare(timespec_ex const & rhs) const
441 : {
442 97 : if(tv_sec == rhs.tv_sec)
443 : {
444 85 : return tv_nsec == rhs.tv_nsec
445 112 : ? 0
446 112 : : (tv_nsec < rhs.tv_nsec ? -1 : 1);
447 : }
448 :
449 12 : return tv_sec < rhs.tv_sec ? -1 : 1;
450 : }
451 :
452 :
453 : /** \brief Check whether the timespec_ex is zero.
454 : *
455 : * This function returns true if the timespec_ex represents zero
456 : * (i.e. zero seconds and zero nano-seconds).
457 : *
458 : * \return true if the timespec_ex is zero, false if not zero.
459 : */
460 11 : bool operator ! ()
461 : {
462 11 : return tv_sec == 0 && tv_nsec == 0;
463 : }
464 :
465 :
466 : /** \brief Add the right handside to this timespec_ex.
467 : *
468 : * This operator adds the right handside to this object.
469 : *
470 : * \param[in] rhs Another timespec_ex to add to this one.
471 : *
472 : * \return A reference to this timespec_ex object.
473 : */
474 19 : timespec_ex & operator += (timespec_ex const & rhs)
475 : {
476 19 : *this = add(rhs);
477 19 : return *this;
478 : }
479 :
480 :
481 : /** \brief Add 1 nanosecond to this timespec_ex object.
482 : *
483 : * This function adds exactly one nanonsecond to this timespec_ex
484 : * object.
485 : *
486 : * \return A reference to this timespec_ex object.
487 : */
488 3 : timespec_ex & operator ++ ()
489 : {
490 3 : *this += 1L;
491 3 : return *this;
492 : }
493 :
494 :
495 : /** \brief Add 1 nanosecond to this timespec_ex object.
496 : *
497 : * This function adds exactly one nanonsecond to this timespec_ex
498 : * object and returns the original value.
499 : *
500 : * \return A copy of this timespec_ex object before the add() occurs.
501 : */
502 1 : timespec_ex operator ++ (int)
503 : {
504 1 : timespec_ex result(*this);
505 1 : *this += 1L;
506 1 : return result;
507 : }
508 :
509 :
510 : /** \brief Add two timespec_ex objects and return the result.
511 : *
512 : * This function computes the addition of this timespec_ex object
513 : * and the \p t timespec_ex and returns the result. The inputs
514 : * are not modified.
515 : *
516 : * \param[in] t The right handside to add to this timespex_ex object.
517 : *
518 : * \return The sum of the inputs in a new timespec_ex object.
519 : */
520 7 : timespec_ex operator + (timespec_ex const & t) const
521 : {
522 7 : timespec_ex result(*this);
523 7 : result += t;
524 7 : return result;
525 : }
526 :
527 :
528 : /** \brief Subtract the right handside from this timespec_ex.
529 : *
530 : * This operator subtracts the right handside from this object.
531 : *
532 : * \param[in] rhs Another timespec_ex to subtract from this one.
533 : *
534 : * \return A reference to this timespec_ex object.
535 : */
536 14 : timespec_ex & operator -= (timespec_ex const & rhs)
537 : {
538 14 : *this = add(-rhs);
539 14 : return *this;
540 : }
541 :
542 :
543 : /** \brief Subtract 1 nanosecond from timespec_ex object.
544 : *
545 : * This function subtracts exactly one nanonsecond from this
546 : * timespec_ex object.
547 : *
548 : * \return A reference to this timespec_ex object.
549 : */
550 3 : timespec_ex & operator -- ()
551 : {
552 3 : *this -= 1L;
553 3 : return *this;
554 : }
555 :
556 :
557 : /** \brief Subtract 1 nanosecond from timespec_ex object.
558 : *
559 : * This function subtracts exactly one nanonsecond from this
560 : * timespec_ex object and returns the original value.
561 : *
562 : * \return A copy of this timespec_ex object before the subtract occurs.
563 : */
564 1 : timespec_ex operator -- (int)
565 : {
566 1 : timespec_ex result(*this);
567 1 : *this -= 1L;
568 1 : return result;
569 : }
570 :
571 :
572 : /** \brief Compute the additive opposite of the right handside timespec_ex.
573 : *
574 : * This function computers the opposite of the right handside timespec_ex
575 : * and returns a copy with the result.
576 : *
577 : * This is equivalent to computing `0 - t`.
578 : *
579 : * \param[in] t The right handside time to negate.
580 : *
581 : * \return A timespec_ex representing the additive opposite of the input.
582 : */
583 49 : timespec_ex operator - () const
584 : {
585 49 : timespec_ex result(timespec{ -tv_sec, -tv_nsec });
586 49 : if(result.tv_nsec < 0)
587 : {
588 48 : --result.tv_sec;
589 48 : result.tv_nsec += 1'000'000'000L;
590 : }
591 49 : return result;
592 : }
593 :
594 :
595 : /** \brief Subtract \p t from this timespec_ex object.
596 : *
597 : * This function computes the difference of this timespec_ex object
598 : * and the \p t timespec_ex object and returns the result. The inputs
599 : * are not modified.
600 : *
601 : * \param[in] rhs The right handside to subtract from this timespex_ex
602 : * object.
603 : *
604 : * \return The different of the inputs in a new timespec_ex object.
605 : */
606 6 : timespec_ex operator - (timespec_ex const & rhs) const
607 : {
608 6 : timespec_ex result(*this);
609 6 : result -= rhs;
610 6 : return result;
611 : }
612 :
613 :
614 : /** \brief Compare whether the two timespec_ex are equal.
615 : *
616 : * \param[in] t The time to compare against.
617 : *
618 : * \return true if both timespec_ex objects are equal.
619 : */
620 23 : bool operator == (timespec_ex const & t) const
621 : {
622 23 : return compare(t) == 0;
623 : }
624 :
625 :
626 : /** \brief Compare whether the two timespec_ex are not equal.
627 : *
628 : * \param[in] t The time to compare against.
629 : *
630 : * \return true if both timespec_ex objects are not equal.
631 : */
632 15 : bool operator != (timespec_ex const & t) const
633 : {
634 15 : return compare(t) != 0;
635 : }
636 :
637 :
638 : /** \brief Compare whether the left handside is smaller.
639 : *
640 : * \param[in] t The time to compare against.
641 : *
642 : * \return true if the left handside timespec_ex object is smaller.
643 : */
644 15 : bool operator < (timespec_ex const & t) const
645 : {
646 15 : return compare(t) == -1;
647 : }
648 :
649 :
650 : /** \brief Compare whether the left handside is smaller or equal.
651 : *
652 : * \param[in] t The time to compare against.
653 : *
654 : * \return true if the left handside timespec_ex object is smaller
655 : * or equal.
656 : */
657 15 : bool operator <= (timespec_ex const & t) const
658 : {
659 15 : return compare(t) <= 0;
660 : }
661 :
662 :
663 : /** \brief Compare whether the left handside is larger.
664 : *
665 : * \param[in] t The time to compare against.
666 : *
667 : * \return true if the left handside timespec_ex object is larger.
668 : */
669 14 : bool operator > (timespec_ex const & t) const
670 : {
671 14 : return compare(t) == 1;
672 : }
673 :
674 :
675 : /** \brief Compare whether the left handside is larger or equal.
676 : *
677 : * \param[in] t The time to compare against.
678 : *
679 : * \return true if the left handside timespec_ex object is larger
680 : * or equal.
681 : */
682 15 : bool operator >= (timespec_ex const & t) const
683 : {
684 15 : return compare(t) >= 0;
685 : }
686 : };
687 :
688 :
689 :
690 :
691 :
692 :
693 :
694 : /** \brief Output a timespec to a basic_ostream.
695 : *
696 : * This function allows one to print out a timespec. By default the function
697 : * prints the timespec as a floating point.
698 : *
699 : * \todo
700 : * Look into the possibility to write the data as Y/M/D H:M:S.nanosecs
701 : *
702 : * \param[in] out The output stream where the timespec gets written.
703 : * \param[in] t The actual timespec that is to be printed.
704 : *
705 : * \return A reference to the basic_ostream object.
706 : */
707 : template<typename CharT, typename Traits>
708 3 : std::basic_ostream<CharT, Traits> & operator << (std::basic_ostream<CharT, Traits> & out, timespec const & t)
709 : {
710 : // write to a string buffer first
711 : //
712 6 : std::basic_ostringstream<CharT, Traits, std::allocator<CharT> > s;
713 :
714 : // setup the string output like the out stream
715 : //
716 3 : s.flags(out.flags());
717 3 : s.imbue(out.getloc());
718 3 : s.precision(out.precision());
719 3 : s << t.tv_sec << "." << std::setw(9) << std::setfill('0') << t.tv_nsec;
720 :
721 : // buffer is ready, display in output in one go
722 : //
723 6 : return out << s.str();
724 : }
725 :
726 :
727 : } // namespace snapdev
728 : // vim: ts=4 sw=4 et
|