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) -> ×pec228 fn as_ref(&self) -> ×pec {
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