• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #[cfg_attr(target_env = "musl", allow(deprecated))]
2 // https://github.com/rust-lang/libc/issues/1848
3 pub use libc::{suseconds_t, time_t};
4 use libc::{timespec, timeval};
5 use std::convert::From;
6 use std::time::Duration;
7 use std::{cmp, fmt, ops};
8 
zero_init_timespec() -> timespec9 const fn zero_init_timespec() -> timespec {
10     // `std::mem::MaybeUninit::zeroed()` is not yet a const fn
11     // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
12     // the appropriate size to zero and then transmute it to a timespec value.
13     unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
14 }
15 
16 #[cfg(any(
17     all(feature = "time", any(target_os = "android", target_os = "linux")),
18     all(
19         any(
20             target_os = "freebsd",
21             target_os = "illumos",
22             target_os = "linux",
23             target_os = "netbsd"
24         ),
25         feature = "time",
26         feature = "signal"
27     )
28 ))]
29 pub(crate) mod timer {
30     use crate::sys::time::{zero_init_timespec, TimeSpec};
31     use bitflags::bitflags;
32 
33     #[derive(Debug, Clone, Copy)]
34     pub(crate) struct TimerSpec(libc::itimerspec);
35 
36     impl TimerSpec {
none() -> Self37         pub const fn none() -> Self {
38             Self(libc::itimerspec {
39                 it_interval: zero_init_timespec(),
40                 it_value: zero_init_timespec(),
41             })
42         }
43     }
44 
45     impl AsMut<libc::itimerspec> for TimerSpec {
as_mut(&mut self) -> &mut libc::itimerspec46         fn as_mut(&mut self) -> &mut libc::itimerspec {
47             &mut self.0
48         }
49     }
50 
51     impl AsRef<libc::itimerspec> for TimerSpec {
as_ref(&self) -> &libc::itimerspec52         fn as_ref(&self) -> &libc::itimerspec {
53             &self.0
54         }
55     }
56 
57     impl From<Expiration> for TimerSpec {
from(expiration: Expiration) -> TimerSpec58         fn from(expiration: Expiration) -> TimerSpec {
59             match expiration {
60                 Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
61                     it_interval: zero_init_timespec(),
62                     it_value: *t.as_ref(),
63                 }),
64                 Expiration::IntervalDelayed(start, interval) => {
65                     TimerSpec(libc::itimerspec {
66                         it_interval: *interval.as_ref(),
67                         it_value: *start.as_ref(),
68                     })
69                 }
70                 Expiration::Interval(t) => TimerSpec(libc::itimerspec {
71                     it_interval: *t.as_ref(),
72                     it_value: *t.as_ref(),
73                 }),
74             }
75         }
76     }
77 
78     /// An enumeration allowing the definition of the expiration time of an alarm,
79     /// recurring or not.
80     #[derive(Debug, Clone, Copy, Eq, PartialEq)]
81     pub enum Expiration {
82         /// Alarm will trigger once after the time given in `TimeSpec`
83         OneShot(TimeSpec),
84         /// Alarm will trigger after a specified delay and then every interval of
85         /// time.
86         IntervalDelayed(TimeSpec, TimeSpec),
87         /// Alarm will trigger every specified interval of time.
88         Interval(TimeSpec),
89     }
90 
91     #[cfg(any(target_os = "android", target_os = "linux"))]
92     bitflags! {
93         /// Flags that are used for arming the timer.
94         pub struct TimerSetTimeFlags: libc::c_int {
95             const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
96         }
97     }
98     #[cfg(any(
99         target_os = "freebsd",
100         target_os = "netbsd",
101         target_os = "dragonfly",
102         target_os = "illumos"
103     ))]
104     bitflags! {
105         /// Flags that are used for arming the timer.
106         pub struct TimerSetTimeFlags: libc::c_int {
107             const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
108         }
109     }
110 
111     impl From<TimerSpec> for Expiration {
from(timerspec: TimerSpec) -> Expiration112         fn from(timerspec: TimerSpec) -> Expiration {
113             match timerspec {
114                 TimerSpec(libc::itimerspec {
115                     it_interval:
116                         libc::timespec {
117                             tv_sec: 0,
118                             tv_nsec: 0,
119                             ..
120                         },
121                     it_value: ts,
122                 }) => Expiration::OneShot(ts.into()),
123                 TimerSpec(libc::itimerspec {
124                     it_interval: int_ts,
125                     it_value: val_ts,
126                 }) => {
127                     if (int_ts.tv_sec == val_ts.tv_sec)
128                         && (int_ts.tv_nsec == val_ts.tv_nsec)
129                     {
130                         Expiration::Interval(int_ts.into())
131                     } else {
132                         Expiration::IntervalDelayed(
133                             val_ts.into(),
134                             int_ts.into(),
135                         )
136                     }
137                 }
138             }
139         }
140     }
141 }
142 
143 pub trait TimeValLike: Sized {
144     #[inline]
zero() -> Self145     fn zero() -> Self {
146         Self::seconds(0)
147     }
148 
149     #[inline]
hours(hours: i64) -> Self150     fn hours(hours: i64) -> Self {
151         let secs = hours
152             .checked_mul(SECS_PER_HOUR)
153             .expect("TimeValLike::hours ouf of bounds");
154         Self::seconds(secs)
155     }
156 
157     #[inline]
minutes(minutes: i64) -> Self158     fn minutes(minutes: i64) -> Self {
159         let secs = minutes
160             .checked_mul(SECS_PER_MINUTE)
161             .expect("TimeValLike::minutes out of bounds");
162         Self::seconds(secs)
163     }
164 
seconds(seconds: i64) -> Self165     fn seconds(seconds: i64) -> Self;
milliseconds(milliseconds: i64) -> Self166     fn milliseconds(milliseconds: i64) -> Self;
microseconds(microseconds: i64) -> Self167     fn microseconds(microseconds: i64) -> Self;
nanoseconds(nanoseconds: i64) -> Self168     fn nanoseconds(nanoseconds: i64) -> Self;
169 
170     #[inline]
num_hours(&self) -> i64171     fn num_hours(&self) -> i64 {
172         self.num_seconds() / 3600
173     }
174 
175     #[inline]
num_minutes(&self) -> i64176     fn num_minutes(&self) -> i64 {
177         self.num_seconds() / 60
178     }
179 
num_seconds(&self) -> i64180     fn num_seconds(&self) -> i64;
num_milliseconds(&self) -> i64181     fn num_milliseconds(&self) -> i64;
num_microseconds(&self) -> i64182     fn num_microseconds(&self) -> i64;
num_nanoseconds(&self) -> i64183     fn num_nanoseconds(&self) -> i64;
184 }
185 
186 #[repr(C)]
187 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
188 pub struct TimeSpec(timespec);
189 
190 const NANOS_PER_SEC: i64 = 1_000_000_000;
191 const SECS_PER_MINUTE: i64 = 60;
192 const SECS_PER_HOUR: i64 = 3600;
193 
194 #[cfg(target_pointer_width = "64")]
195 const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
196 
197 #[cfg(target_pointer_width = "32")]
198 const TS_MAX_SECONDS: i64 = isize::MAX as i64;
199 
200 const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
201 
202 // x32 compatibility
203 // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
204 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
205 type timespec_tv_nsec_t = i64;
206 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
207 type timespec_tv_nsec_t = libc::c_long;
208 
209 impl From<timespec> for TimeSpec {
from(ts: timespec) -> Self210     fn from(ts: timespec) -> Self {
211         Self(ts)
212     }
213 }
214 
215 impl From<Duration> for TimeSpec {
from(duration: Duration) -> Self216     fn from(duration: Duration) -> Self {
217         Self::from_duration(duration)
218     }
219 }
220 
221 impl From<TimeSpec> for Duration {
from(timespec: TimeSpec) -> Self222     fn from(timespec: TimeSpec) -> Self {
223         Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
224     }
225 }
226 
227 impl AsRef<timespec> for TimeSpec {
as_ref(&self) -> &timespec228     fn as_ref(&self) -> &timespec {
229         &self.0
230     }
231 }
232 
233 impl AsMut<timespec> for TimeSpec {
as_mut(&mut self) -> &mut timespec234     fn as_mut(&mut self) -> &mut timespec {
235         &mut self.0
236     }
237 }
238 
239 impl Ord for TimeSpec {
240     // The implementation of cmp is simplified by assuming that the struct is
241     // normalized.  That is, tv_nsec must always be within [0, 1_000_000_000)
cmp(&self, other: &TimeSpec) -> cmp::Ordering242     fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
243         if self.tv_sec() == other.tv_sec() {
244             self.tv_nsec().cmp(&other.tv_nsec())
245         } else {
246             self.tv_sec().cmp(&other.tv_sec())
247         }
248     }
249 }
250 
251 impl PartialOrd for TimeSpec {
partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering>252     fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
253         Some(self.cmp(other))
254     }
255 }
256 
257 impl TimeValLike for TimeSpec {
258     #[inline]
259     #[cfg_attr(target_env = "musl", allow(deprecated))]
260     // https://github.com/rust-lang/libc/issues/1848
seconds(seconds: i64) -> TimeSpec261     fn seconds(seconds: i64) -> TimeSpec {
262         assert!(
263             (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
264             "TimeSpec out of bounds; seconds={}",
265             seconds
266         );
267         let mut ts = zero_init_timespec();
268         ts.tv_sec = seconds as time_t;
269         TimeSpec(ts)
270     }
271 
272     #[inline]
milliseconds(milliseconds: i64) -> TimeSpec273     fn milliseconds(milliseconds: i64) -> TimeSpec {
274         let nanoseconds = milliseconds
275             .checked_mul(1_000_000)
276             .expect("TimeSpec::milliseconds out of bounds");
277 
278         TimeSpec::nanoseconds(nanoseconds)
279     }
280 
281     /// Makes a new `TimeSpec` with given number of microseconds.
282     #[inline]
microseconds(microseconds: i64) -> TimeSpec283     fn microseconds(microseconds: i64) -> TimeSpec {
284         let nanoseconds = microseconds
285             .checked_mul(1_000)
286             .expect("TimeSpec::milliseconds out of bounds");
287 
288         TimeSpec::nanoseconds(nanoseconds)
289     }
290 
291     /// Makes a new `TimeSpec` with given number of nanoseconds.
292     #[inline]
293     #[cfg_attr(target_env = "musl", allow(deprecated))]
294     // https://github.com/rust-lang/libc/issues/1848
nanoseconds(nanoseconds: i64) -> TimeSpec295     fn nanoseconds(nanoseconds: i64) -> TimeSpec {
296         let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
297         assert!(
298             (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
299             "TimeSpec out of bounds"
300         );
301         let mut ts = zero_init_timespec();
302         ts.tv_sec = secs as time_t;
303         ts.tv_nsec = nanos as timespec_tv_nsec_t;
304         TimeSpec(ts)
305     }
306 
307     // The cast is not unnecessary on all platforms.
308     #[allow(clippy::unnecessary_cast)]
num_seconds(&self) -> i64309     fn num_seconds(&self) -> i64 {
310         if self.tv_sec() < 0 && self.tv_nsec() > 0 {
311             (self.tv_sec() + 1) as i64
312         } else {
313             self.tv_sec() as i64
314         }
315     }
316 
num_milliseconds(&self) -> i64317     fn num_milliseconds(&self) -> i64 {
318         self.num_nanoseconds() / 1_000_000
319     }
320 
num_microseconds(&self) -> i64321     fn num_microseconds(&self) -> i64 {
322         self.num_nanoseconds() / 1_000
323     }
324 
325     // The cast is not unnecessary on all platforms.
326     #[allow(clippy::unnecessary_cast)]
num_nanoseconds(&self) -> i64327     fn num_nanoseconds(&self) -> i64 {
328         let secs = self.num_seconds() * 1_000_000_000;
329         let nsec = self.nanos_mod_sec();
330         secs + nsec as i64
331     }
332 }
333 
334 impl TimeSpec {
335     /// Construct a new `TimeSpec` from its components
336     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self337     pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
338         let mut ts = zero_init_timespec();
339         ts.tv_sec = seconds;
340         ts.tv_nsec = nanoseconds;
341         Self(ts)
342     }
343 
nanos_mod_sec(&self) -> timespec_tv_nsec_t344     fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
345         if self.tv_sec() < 0 && self.tv_nsec() > 0 {
346             self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
347         } else {
348             self.tv_nsec()
349         }
350     }
351 
352     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
tv_sec(&self) -> time_t353     pub const fn tv_sec(&self) -> time_t {
354         self.0.tv_sec
355     }
356 
tv_nsec(&self) -> timespec_tv_nsec_t357     pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
358         self.0.tv_nsec
359     }
360 
361     #[cfg_attr(target_env = "musl", allow(deprecated))]
362     // https://github.com/rust-lang/libc/issues/1848
from_duration(duration: Duration) -> Self363     pub const fn from_duration(duration: Duration) -> Self {
364         let mut ts = zero_init_timespec();
365         ts.tv_sec = duration.as_secs() as time_t;
366         ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
367         TimeSpec(ts)
368     }
369 
from_timespec(timespec: timespec) -> Self370     pub const fn from_timespec(timespec: timespec) -> Self {
371         Self(timespec)
372     }
373 }
374 
375 impl ops::Neg for TimeSpec {
376     type Output = TimeSpec;
377 
neg(self) -> TimeSpec378     fn neg(self) -> TimeSpec {
379         TimeSpec::nanoseconds(-self.num_nanoseconds())
380     }
381 }
382 
383 impl ops::Add for TimeSpec {
384     type Output = TimeSpec;
385 
add(self, rhs: TimeSpec) -> TimeSpec386     fn add(self, rhs: TimeSpec) -> TimeSpec {
387         TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
388     }
389 }
390 
391 impl ops::Sub for TimeSpec {
392     type Output = TimeSpec;
393 
sub(self, rhs: TimeSpec) -> TimeSpec394     fn sub(self, rhs: TimeSpec) -> TimeSpec {
395         TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
396     }
397 }
398 
399 impl ops::Mul<i32> for TimeSpec {
400     type Output = TimeSpec;
401 
mul(self, rhs: i32) -> TimeSpec402     fn mul(self, rhs: i32) -> TimeSpec {
403         let usec = self
404             .num_nanoseconds()
405             .checked_mul(i64::from(rhs))
406             .expect("TimeSpec multiply out of bounds");
407 
408         TimeSpec::nanoseconds(usec)
409     }
410 }
411 
412 impl ops::Div<i32> for TimeSpec {
413     type Output = TimeSpec;
414 
div(self, rhs: i32) -> TimeSpec415     fn div(self, rhs: i32) -> TimeSpec {
416         let usec = self.num_nanoseconds() / i64::from(rhs);
417         TimeSpec::nanoseconds(usec)
418     }
419 }
420 
421 impl fmt::Display for TimeSpec {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result422     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423         let (abs, sign) = if self.tv_sec() < 0 {
424             (-*self, "-")
425         } else {
426             (*self, "")
427         };
428 
429         let sec = abs.tv_sec();
430 
431         write!(f, "{}", sign)?;
432 
433         if abs.tv_nsec() == 0 {
434             if abs.tv_sec() == 1 {
435                 write!(f, "{} second", sec)?;
436             } else {
437                 write!(f, "{} seconds", sec)?;
438             }
439         } else if abs.tv_nsec() % 1_000_000 == 0 {
440             write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
441         } else if abs.tv_nsec() % 1_000 == 0 {
442             write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
443         } else {
444             write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
445         }
446 
447         Ok(())
448     }
449 }
450 
451 #[repr(transparent)]
452 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
453 pub struct TimeVal(timeval);
454 
455 const MICROS_PER_SEC: i64 = 1_000_000;
456 
457 #[cfg(target_pointer_width = "64")]
458 const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
459 
460 #[cfg(target_pointer_width = "32")]
461 const TV_MAX_SECONDS: i64 = isize::MAX as i64;
462 
463 const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
464 
465 impl AsRef<timeval> for TimeVal {
as_ref(&self) -> &timeval466     fn as_ref(&self) -> &timeval {
467         &self.0
468     }
469 }
470 
471 impl AsMut<timeval> for TimeVal {
as_mut(&mut self) -> &mut timeval472     fn as_mut(&mut self) -> &mut timeval {
473         &mut self.0
474     }
475 }
476 
477 impl Ord for TimeVal {
478     // The implementation of cmp is simplified by assuming that the struct is
479     // normalized.  That is, tv_usec must always be within [0, 1_000_000)
cmp(&self, other: &TimeVal) -> cmp::Ordering480     fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
481         if self.tv_sec() == other.tv_sec() {
482             self.tv_usec().cmp(&other.tv_usec())
483         } else {
484             self.tv_sec().cmp(&other.tv_sec())
485         }
486     }
487 }
488 
489 impl PartialOrd for TimeVal {
partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering>490     fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
491         Some(self.cmp(other))
492     }
493 }
494 
495 impl TimeValLike for TimeVal {
496     #[inline]
seconds(seconds: i64) -> TimeVal497     fn seconds(seconds: i64) -> TimeVal {
498         assert!(
499             (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
500             "TimeVal out of bounds; seconds={}",
501             seconds
502         );
503         #[cfg_attr(target_env = "musl", allow(deprecated))]
504         // https://github.com/rust-lang/libc/issues/1848
505         TimeVal(timeval {
506             tv_sec: seconds as time_t,
507             tv_usec: 0,
508         })
509     }
510 
511     #[inline]
milliseconds(milliseconds: i64) -> TimeVal512     fn milliseconds(milliseconds: i64) -> TimeVal {
513         let microseconds = milliseconds
514             .checked_mul(1_000)
515             .expect("TimeVal::milliseconds out of bounds");
516 
517         TimeVal::microseconds(microseconds)
518     }
519 
520     /// Makes a new `TimeVal` with given number of microseconds.
521     #[inline]
microseconds(microseconds: i64) -> TimeVal522     fn microseconds(microseconds: i64) -> TimeVal {
523         let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
524         assert!(
525             (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
526             "TimeVal out of bounds"
527         );
528         #[cfg_attr(target_env = "musl", allow(deprecated))]
529         // https://github.com/rust-lang/libc/issues/1848
530         TimeVal(timeval {
531             tv_sec: secs as time_t,
532             tv_usec: micros as suseconds_t,
533         })
534     }
535 
536     /// Makes a new `TimeVal` with given number of nanoseconds.  Some precision
537     /// will be lost
538     #[inline]
nanoseconds(nanoseconds: i64) -> TimeVal539     fn nanoseconds(nanoseconds: i64) -> TimeVal {
540         let microseconds = nanoseconds / 1000;
541         let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
542         assert!(
543             (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
544             "TimeVal out of bounds"
545         );
546         #[cfg_attr(target_env = "musl", allow(deprecated))]
547         // https://github.com/rust-lang/libc/issues/1848
548         TimeVal(timeval {
549             tv_sec: secs as time_t,
550             tv_usec: micros as suseconds_t,
551         })
552     }
553 
554     // The cast is not unnecessary on all platforms.
555     #[allow(clippy::unnecessary_cast)]
num_seconds(&self) -> i64556     fn num_seconds(&self) -> i64 {
557         if self.tv_sec() < 0 && self.tv_usec() > 0 {
558             (self.tv_sec() + 1) as i64
559         } else {
560             self.tv_sec() as i64
561         }
562     }
563 
num_milliseconds(&self) -> i64564     fn num_milliseconds(&self) -> i64 {
565         self.num_microseconds() / 1_000
566     }
567 
568     // The cast is not unnecessary on all platforms.
569     #[allow(clippy::unnecessary_cast)]
num_microseconds(&self) -> i64570     fn num_microseconds(&self) -> i64 {
571         let secs = self.num_seconds() * 1_000_000;
572         let usec = self.micros_mod_sec();
573         secs + usec as i64
574     }
575 
num_nanoseconds(&self) -> i64576     fn num_nanoseconds(&self) -> i64 {
577         self.num_microseconds() * 1_000
578     }
579 }
580 
581 impl TimeVal {
582     /// Construct a new `TimeVal` from its components
583     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
new(seconds: time_t, microseconds: suseconds_t) -> Self584     pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
585         Self(timeval {
586             tv_sec: seconds,
587             tv_usec: microseconds,
588         })
589     }
590 
micros_mod_sec(&self) -> suseconds_t591     fn micros_mod_sec(&self) -> suseconds_t {
592         if self.tv_sec() < 0 && self.tv_usec() > 0 {
593             self.tv_usec() - MICROS_PER_SEC as suseconds_t
594         } else {
595             self.tv_usec()
596         }
597     }
598 
599     #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
tv_sec(&self) -> time_t600     pub const fn tv_sec(&self) -> time_t {
601         self.0.tv_sec
602     }
603 
tv_usec(&self) -> suseconds_t604     pub const fn tv_usec(&self) -> suseconds_t {
605         self.0.tv_usec
606     }
607 }
608 
609 impl ops::Neg for TimeVal {
610     type Output = TimeVal;
611 
neg(self) -> TimeVal612     fn neg(self) -> TimeVal {
613         TimeVal::microseconds(-self.num_microseconds())
614     }
615 }
616 
617 impl ops::Add for TimeVal {
618     type Output = TimeVal;
619 
add(self, rhs: TimeVal) -> TimeVal620     fn add(self, rhs: TimeVal) -> TimeVal {
621         TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
622     }
623 }
624 
625 impl ops::Sub for TimeVal {
626     type Output = TimeVal;
627 
sub(self, rhs: TimeVal) -> TimeVal628     fn sub(self, rhs: TimeVal) -> TimeVal {
629         TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
630     }
631 }
632 
633 impl ops::Mul<i32> for TimeVal {
634     type Output = TimeVal;
635 
mul(self, rhs: i32) -> TimeVal636     fn mul(self, rhs: i32) -> TimeVal {
637         let usec = self
638             .num_microseconds()
639             .checked_mul(i64::from(rhs))
640             .expect("TimeVal multiply out of bounds");
641 
642         TimeVal::microseconds(usec)
643     }
644 }
645 
646 impl ops::Div<i32> for TimeVal {
647     type Output = TimeVal;
648 
div(self, rhs: i32) -> TimeVal649     fn div(self, rhs: i32) -> TimeVal {
650         let usec = self.num_microseconds() / i64::from(rhs);
651         TimeVal::microseconds(usec)
652     }
653 }
654 
655 impl fmt::Display for TimeVal {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result656     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
657         let (abs, sign) = if self.tv_sec() < 0 {
658             (-*self, "-")
659         } else {
660             (*self, "")
661         };
662 
663         let sec = abs.tv_sec();
664 
665         write!(f, "{}", sign)?;
666 
667         if abs.tv_usec() == 0 {
668             if abs.tv_sec() == 1 {
669                 write!(f, "{} second", sec)?;
670             } else {
671                 write!(f, "{} seconds", sec)?;
672             }
673         } else if abs.tv_usec() % 1000 == 0 {
674             write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
675         } else {
676             write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
677         }
678 
679         Ok(())
680     }
681 }
682 
683 impl From<timeval> for TimeVal {
from(tv: timeval) -> Self684     fn from(tv: timeval) -> Self {
685         TimeVal(tv)
686     }
687 }
688 
689 #[inline]
div_mod_floor_64(this: i64, other: i64) -> (i64, i64)690 fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
691     (div_floor_64(this, other), mod_floor_64(this, other))
692 }
693 
694 #[inline]
div_floor_64(this: i64, other: i64) -> i64695 fn div_floor_64(this: i64, other: i64) -> i64 {
696     match div_rem_64(this, other) {
697         (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
698         (d, _) => d,
699     }
700 }
701 
702 #[inline]
mod_floor_64(this: i64, other: i64) -> i64703 fn mod_floor_64(this: i64, other: i64) -> i64 {
704     match this % other {
705         r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
706         r => r,
707     }
708 }
709 
710 #[inline]
div_rem_64(this: i64, other: i64) -> (i64, i64)711 fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
712     (this / other, this % other)
713 }
714 
715 #[cfg(test)]
716 mod test {
717     use super::{TimeSpec, TimeVal, TimeValLike};
718     use std::time::Duration;
719 
720     #[test]
test_timespec()721     pub fn test_timespec() {
722         assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
723         assert_eq!(
724             TimeSpec::seconds(1) + TimeSpec::seconds(2),
725             TimeSpec::seconds(3)
726         );
727         assert_eq!(
728             TimeSpec::minutes(3) + TimeSpec::seconds(2),
729             TimeSpec::seconds(182)
730         );
731     }
732 
733     #[test]
test_timespec_from()734     pub fn test_timespec_from() {
735         let duration = Duration::new(123, 123_456_789);
736         let timespec = TimeSpec::nanoseconds(123_123_456_789);
737 
738         assert_eq!(TimeSpec::from(duration), timespec);
739         assert_eq!(Duration::from(timespec), duration);
740     }
741 
742     #[test]
test_timespec_neg()743     pub fn test_timespec_neg() {
744         let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
745         let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
746 
747         assert_eq!(a, -b);
748     }
749 
750     #[test]
test_timespec_ord()751     pub fn test_timespec_ord() {
752         assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
753         assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
754         assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
755         assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
756         assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
757     }
758 
759     #[test]
test_timespec_fmt()760     pub fn test_timespec_fmt() {
761         assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
762         assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
763         assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
764         assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
765         assert_eq!(
766             TimeSpec::nanoseconds(42).to_string(),
767             "0.000000042 seconds"
768         );
769         assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
770     }
771 
772     #[test]
test_timeval()773     pub fn test_timeval() {
774         assert_ne!(TimeVal::seconds(1), TimeVal::zero());
775         assert_eq!(
776             TimeVal::seconds(1) + TimeVal::seconds(2),
777             TimeVal::seconds(3)
778         );
779         assert_eq!(
780             TimeVal::minutes(3) + TimeVal::seconds(2),
781             TimeVal::seconds(182)
782         );
783     }
784 
785     #[test]
test_timeval_ord()786     pub fn test_timeval_ord() {
787         assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
788         assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
789         assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
790         assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
791         assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
792     }
793 
794     #[test]
test_timeval_neg()795     pub fn test_timeval_neg() {
796         let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
797         let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
798 
799         assert_eq!(a, -b);
800     }
801 
802     #[test]
test_timeval_fmt()803     pub fn test_timeval_fmt() {
804         assert_eq!(TimeVal::zero().to_string(), "0 seconds");
805         assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
806         assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
807         assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
808         assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
809         assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
810     }
811 }
812