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 : * \todo
296 : * Look into whether we want to return an error if clock_gettime()
297 : * fails.
298 : *
299 : * \param[in] clk_id The type of clock you want to query.
300 : *
301 : * \return A timespec_ex representing the specified \p clk_id.
302 : */
303 1 : static timespec_ex gettime(clockid_t clk_id = CLOCK_REALTIME)
304 : {
305 1 : timespec_ex result;
306 1 : clock_gettime(clk_id, &result);
307 1 : return result;
308 : }
309 :
310 :
311 : /** \brief Extract the timespec_ex as an int64_t value.
312 : *
313 : * This function transforms a timespec_ex structure in an int64_t
314 : * in nanoseconds.
315 : *
316 : * \return This timespec_ex converted to nanoseconds.
317 : */
318 1 : std::int64_t to_nsec() const
319 : {
320 1 : return tv_nsec
321 1 : + tv_sec * 1'000'000'000LL;
322 : }
323 :
324 :
325 : /** \brief Extract the timespec_ex as a double value.
326 : *
327 : * This function transforms a timespec_ex structure into a double
328 : * in seconds. The nanoseconds are added as one billionth of a
329 : * second.
330 : *
331 : * \return The timespec_ex converted the seconds.
332 : */
333 6 : double to_sec() const
334 : {
335 6 : return static_cast<double>(tv_sec)
336 6 : + static_cast<double>(tv_nsec) / 1.0e9;
337 : }
338 :
339 :
340 : /** \brief Validate this timespec_ex structure.
341 : *
342 : * This function returns true if this timespec_ex structure is considered
343 : * valid.
344 : *
345 : * At this time, the validation consists of verifying that the
346 : * nanoseconds is a number between 0 and 1 billion (maximum excluded).
347 : *
348 : * \note
349 : * Negative timespec_ex are represented by a negative tv_sec. The
350 : * tv_nsec can never be negative after a valid operation.
351 : *
352 : * \return true if the timespec_ex is considered valid.
353 : */
354 7 : bool valid() const
355 : {
356 7 : return tv_nsec < 1'000'000'000LL;
357 : }
358 :
359 :
360 : /** \brief Check whether this timespec_ex is negative.
361 : *
362 : * This function checks whether the number represents a negative
363 : * timespec_ex. This is true if the number of seconds is negative.
364 : *
365 : * \note
366 : * The first negative timespec_ex is { -1, 999,999,999 }.
367 : *
368 : * \return true if the timespec_ex is considered negative.
369 : */
370 72 : bool negative() const
371 : {
372 72 : return tv_sec < 0LL;
373 : }
374 :
375 :
376 : /** \brief Add two timespec_ex together.
377 : *
378 : * This function adds \p rhs to this timespec_ex value and returns a
379 : * new timespec_ex with the result. This timespec_ex is not modified.
380 : *
381 : * \param[in] rhs The right handside to add to this number.
382 : *
383 : * \return A new timespec_ex representing the sum of 'this' and rhs.
384 : */
385 33 : timespec_ex add(timespec_ex const & rhs) const
386 : {
387 33 : bool const lneg(negative());
388 33 : bool const rneg(rhs.negative());
389 :
390 33 : timespec_ex lp(lneg ? -*this : *this);
391 33 : timespec_ex rp(rneg ? -rhs : rhs);
392 :
393 33 : timespec_ex result;
394 :
395 33 : switch((lneg ? 1 : 0) + (rneg ? 2 : 0))
396 : {
397 16 : case 0: // positive + positive
398 : case 3: // negative + negative
399 16 : result.tv_sec = lp.tv_sec + rp.tv_sec;
400 16 : result.tv_nsec = lp.tv_nsec + rp.tv_nsec;
401 16 : break;
402 :
403 3 : case 1: // negative + positive
404 3 : result.tv_sec = rp.tv_sec - lp.tv_sec;
405 3 : result.tv_nsec = rp.tv_nsec - lp.tv_nsec;
406 3 : break;
407 :
408 14 : case 2: // positive + negative
409 14 : result.tv_sec = lp.tv_sec - rp.tv_sec;
410 14 : result.tv_nsec = lp.tv_nsec - rp.tv_nsec;
411 14 : break;
412 :
413 : }
414 :
415 33 : if(result.tv_nsec < 0)
416 : {
417 4 : --result.tv_sec;
418 4 : result.tv_nsec += 1'000'000'000L;
419 : }
420 29 : else if(result.tv_nsec >= 1'000'000'000)
421 : {
422 5 : ++result.tv_sec;
423 5 : result.tv_nsec -= 1'000'000'000;
424 : }
425 :
426 33 : if(lneg && rneg)
427 : {
428 2 : result = -result;
429 : }
430 :
431 33 : return result;
432 : }
433 :
434 :
435 : /** \brief Compare two timespec_ex together.
436 : *
437 : * This function compares two timespecs and determine whether they
438 : * are equal (0), 'this' is smaller (-1) or \p rhs is smaller (1).
439 : *
440 : * \param[in] rhs The right handside to compare.
441 : *
442 : * \return -1, 0, or 1 depending on the order between \p lhs and \p rhs.
443 : */
444 97 : int compare(timespec_ex const & rhs) const
445 : {
446 97 : if(tv_sec == rhs.tv_sec)
447 : {
448 85 : return tv_nsec == rhs.tv_nsec
449 112 : ? 0
450 112 : : (tv_nsec < rhs.tv_nsec ? -1 : 1);
451 : }
452 :
453 12 : return tv_sec < rhs.tv_sec ? -1 : 1;
454 : }
455 :
456 :
457 : /** \brief Check whether the timespec_ex is zero.
458 : *
459 : * This function returns true if the timespec_ex represents zero
460 : * (i.e. zero seconds and zero nano-seconds).
461 : *
462 : * \return true if the timespec_ex is zero, false if not zero.
463 : */
464 11 : bool operator ! ()
465 : {
466 11 : return tv_sec == 0 && tv_nsec == 0;
467 : }
468 :
469 :
470 : /** \brief Add the right handside to this timespec_ex.
471 : *
472 : * This operator adds the right handside to this object.
473 : *
474 : * \param[in] rhs Another timespec_ex to add to this one.
475 : *
476 : * \return A reference to this timespec_ex object.
477 : */
478 19 : timespec_ex & operator += (timespec_ex const & rhs)
479 : {
480 19 : *this = add(rhs);
481 19 : return *this;
482 : }
483 :
484 :
485 : /** \brief Add 1 nanosecond to this timespec_ex object.
486 : *
487 : * This function adds exactly one nanonsecond to this timespec_ex
488 : * object.
489 : *
490 : * \return A reference to this timespec_ex object.
491 : */
492 3 : timespec_ex & operator ++ ()
493 : {
494 3 : *this += 1L;
495 3 : return *this;
496 : }
497 :
498 :
499 : /** \brief Add 1 nanosecond to this timespec_ex object.
500 : *
501 : * This function adds exactly one nanonsecond to this timespec_ex
502 : * object and returns the original value.
503 : *
504 : * \return A copy of this timespec_ex object before the add() occurs.
505 : */
506 1 : timespec_ex operator ++ (int)
507 : {
508 1 : timespec_ex result(*this);
509 1 : *this += 1L;
510 1 : return result;
511 : }
512 :
513 :
514 : /** \brief Add two timespec_ex objects and return the result.
515 : *
516 : * This function computes the addition of this timespec_ex object
517 : * and the \p t timespec_ex and returns the result. The inputs
518 : * are not modified.
519 : *
520 : * \param[in] t The right handside to add to this timespex_ex object.
521 : *
522 : * \return The sum of the inputs in a new timespec_ex object.
523 : */
524 7 : timespec_ex operator + (timespec_ex const & t) const
525 : {
526 7 : timespec_ex result(*this);
527 7 : result += t;
528 7 : return result;
529 : }
530 :
531 :
532 : /** \brief Subtract the right handside from this timespec_ex.
533 : *
534 : * This operator subtracts the right handside from this object.
535 : *
536 : * \param[in] rhs Another timespec_ex to subtract from this one.
537 : *
538 : * \return A reference to this timespec_ex object.
539 : */
540 14 : timespec_ex & operator -= (timespec_ex const & rhs)
541 : {
542 14 : *this = add(-rhs);
543 14 : return *this;
544 : }
545 :
546 :
547 : /** \brief Subtract 1 nanosecond from timespec_ex object.
548 : *
549 : * This function subtracts exactly one nanonsecond from this
550 : * timespec_ex object.
551 : *
552 : * \return A reference to this timespec_ex object.
553 : */
554 3 : timespec_ex & operator -- ()
555 : {
556 3 : *this -= 1L;
557 3 : return *this;
558 : }
559 :
560 :
561 : /** \brief Subtract 1 nanosecond from timespec_ex object.
562 : *
563 : * This function subtracts exactly one nanonsecond from this
564 : * timespec_ex object and returns the original value.
565 : *
566 : * \return A copy of this timespec_ex object before the subtract occurs.
567 : */
568 1 : timespec_ex operator -- (int)
569 : {
570 1 : timespec_ex result(*this);
571 1 : *this -= 1L;
572 1 : return result;
573 : }
574 :
575 :
576 : /** \brief Compute the additive opposite of the right handside timespec_ex.
577 : *
578 : * This function computers the opposite of the right handside timespec_ex
579 : * and returns a copy with the result.
580 : *
581 : * This is equivalent to computing `0 - t`.
582 : *
583 : * \param[in] t The right handside time to negate.
584 : *
585 : * \return A timespec_ex representing the additive opposite of the input.
586 : */
587 49 : timespec_ex operator - () const
588 : {
589 49 : timespec_ex result(timespec{ -tv_sec, -tv_nsec });
590 49 : if(result.tv_nsec < 0)
591 : {
592 48 : --result.tv_sec;
593 48 : result.tv_nsec += 1'000'000'000L;
594 : }
595 49 : return result;
596 : }
597 :
598 :
599 : /** \brief Subtract \p t from this timespec_ex object.
600 : *
601 : * This function computes the difference of this timespec_ex object
602 : * and the \p t timespec_ex object and returns the result. The inputs
603 : * are not modified.
604 : *
605 : * \param[in] rhs The right handside to subtract from this timespex_ex
606 : * object.
607 : *
608 : * \return The different of the inputs in a new timespec_ex object.
609 : */
610 6 : timespec_ex operator - (timespec_ex const & rhs) const
611 : {
612 6 : timespec_ex result(*this);
613 6 : result -= rhs;
614 6 : return result;
615 : }
616 :
617 :
618 : /** \brief Compare whether the two timespec_ex are equal.
619 : *
620 : * \param[in] t The time to compare against.
621 : *
622 : * \return true if both timespec_ex objects are equal.
623 : */
624 23 : bool operator == (timespec_ex const & t) const
625 : {
626 23 : return compare(t) == 0;
627 : }
628 :
629 :
630 : /** \brief Compare whether the two timespec_ex are not equal.
631 : *
632 : * \param[in] t The time to compare against.
633 : *
634 : * \return true if both timespec_ex objects are not equal.
635 : */
636 15 : bool operator != (timespec_ex const & t) const
637 : {
638 15 : return compare(t) != 0;
639 : }
640 :
641 :
642 : /** \brief Compare whether the left handside is smaller.
643 : *
644 : * \param[in] t The time to compare against.
645 : *
646 : * \return true if the left handside timespec_ex object is smaller.
647 : */
648 15 : bool operator < (timespec_ex const & t) const
649 : {
650 15 : return compare(t) == -1;
651 : }
652 :
653 :
654 : /** \brief Compare whether the left handside is smaller or equal.
655 : *
656 : * \param[in] t The time to compare against.
657 : *
658 : * \return true if the left handside timespec_ex object is smaller
659 : * or equal.
660 : */
661 15 : bool operator <= (timespec_ex const & t) const
662 : {
663 15 : return compare(t) <= 0;
664 : }
665 :
666 :
667 : /** \brief Compare whether the left handside is larger.
668 : *
669 : * \param[in] t The time to compare against.
670 : *
671 : * \return true if the left handside timespec_ex object is larger.
672 : */
673 14 : bool operator > (timespec_ex const & t) const
674 : {
675 14 : return compare(t) == 1;
676 : }
677 :
678 :
679 : /** \brief Compare whether the left handside is larger or equal.
680 : *
681 : * \param[in] t The time to compare against.
682 : *
683 : * \return true if the left handside timespec_ex object is larger
684 : * or equal.
685 : */
686 15 : bool operator >= (timespec_ex const & t) const
687 : {
688 15 : return compare(t) >= 0;
689 : }
690 : };
691 :
692 :
693 :
694 :
695 :
696 :
697 :
698 : /** \brief Output a timespec to a basic_ostream.
699 : *
700 : * This function allows one to print out a timespec. By default the function
701 : * prints the timespec as a floating point.
702 : *
703 : * \todo
704 : * Look into the possibility to write the data as Y/M/D H:M:S.nanosecs
705 : *
706 : * \param[in] out The output stream where the timespec gets written.
707 : * \param[in] t The actual timespec that is to be printed.
708 : *
709 : * \return A reference to the basic_ostream object.
710 : */
711 : template<typename CharT, typename Traits>
712 3 : std::basic_ostream<CharT, Traits> & operator << (std::basic_ostream<CharT, Traits> & out, timespec const & t)
713 : {
714 : // write to a string buffer first
715 : //
716 6 : std::basic_ostringstream<CharT, Traits, std::allocator<CharT> > s;
717 :
718 : // setup the string output like the out stream
719 : //
720 3 : s.flags(out.flags());
721 3 : s.imbue(out.getloc());
722 3 : s.precision(out.precision());
723 3 : s << t.tv_sec << "." << std::setw(9) << std::setfill('0') << t.tv_nsec;
724 :
725 : // buffer is ready, display in output in one go
726 : //
727 6 : return out << s.str();
728 : }
729 :
730 :
731 : } // namespace snapdev
732 : // vim: ts=4 sw=4 et
|