1use std::{cmp, fmt, ops};
2use std::time::Duration;
3use std::convert::From;
4use libc::{timespec, timeval};
5#[cfg_attr(target_env = "musl", allow(deprecated))] pub use libc::{time_t, suseconds_t};
7
8pub(crate) const TIMESPEC_ZERO: libc::timespec = unsafe {
9 std::mem::transmute([0u8; std::mem::size_of::<libc::timespec>()])
10};
11
12pub trait TimeValLike: Sized {
13 #[inline]
14 fn zero() -> Self {
15 Self::seconds(0)
16 }
17
18 #[inline]
19 fn hours(hours: i64) -> Self {
20 let secs = hours.checked_mul(SECS_PER_HOUR)
21 .expect("TimeValLike::hours ouf of bounds");
22 Self::seconds(secs)
23 }
24
25 #[inline]
26 fn minutes(minutes: i64) -> Self {
27 let secs = minutes.checked_mul(SECS_PER_MINUTE)
28 .expect("TimeValLike::minutes out of bounds");
29 Self::seconds(secs)
30 }
31
32 fn seconds(seconds: i64) -> Self;
33 fn milliseconds(milliseconds: i64) -> Self;
34 fn microseconds(microseconds: i64) -> Self;
35 fn nanoseconds(nanoseconds: i64) -> Self;
36
37 #[inline]
38 fn num_hours(&self) -> i64 {
39 self.num_seconds() / 3600
40 }
41
42 #[inline]
43 fn num_minutes(&self) -> i64 {
44 self.num_seconds() / 60
45 }
46
47 fn num_seconds(&self) -> i64;
48 fn num_milliseconds(&self) -> i64;
49 fn num_microseconds(&self) -> i64;
50 fn num_nanoseconds(&self) -> i64;
51}
52
53#[repr(C)]
54#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55pub struct TimeSpec(timespec);
56
57const NANOS_PER_SEC: i64 = 1_000_000_000;
58const SECS_PER_MINUTE: i64 = 60;
59const SECS_PER_HOUR: i64 = 3600;
60
61#[cfg(target_pointer_width = "64")]
62const TS_MAX_SECONDS: i64 = (::std::i64::MAX / NANOS_PER_SEC) - 1;
63
64#[cfg(target_pointer_width = "32")]
65const TS_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
66
67const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
68
69#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
72type timespec_tv_nsec_t = i64;
73#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
74type timespec_tv_nsec_t = libc::c_long;
75
76impl From<timespec> for TimeSpec {
77 fn from(ts: timespec) -> Self {
78 Self(ts)
79 }
80}
81
82impl From<Duration> for TimeSpec {
83 fn from(duration: Duration) -> Self {
84 Self::from_duration(duration)
85 }
86}
87
88impl From<TimeSpec> for Duration {
89 fn from(timespec: TimeSpec) -> Self {
90 Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
91 }
92}
93
94impl AsRef<timespec> for TimeSpec {
95 fn as_ref(&self) -> ×pec {
96 &self.0
97 }
98}
99
100impl AsMut<timespec> for TimeSpec {
101 fn as_mut(&mut self) -> &mut timespec {
102 &mut self.0
103 }
104}
105
106impl Ord for TimeSpec {
107 fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
110 if self.tv_sec() == other.tv_sec() {
111 self.tv_nsec().cmp(&other.tv_nsec())
112 } else {
113 self.tv_sec().cmp(&other.tv_sec())
114 }
115 }
116}
117
118impl PartialOrd for TimeSpec {
119 fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
120 Some(self.cmp(other))
121 }
122}
123
124impl TimeValLike for TimeSpec {
125 #[inline]
126 #[cfg_attr(target_env = "musl", allow(deprecated))]
127 fn seconds(seconds: i64) -> TimeSpec {
129 assert!((TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
130 "TimeSpec out of bounds; seconds={}", seconds);
131 let mut ts = TIMESPEC_ZERO;
132 ts.tv_sec = seconds as time_t;
133 ts.tv_nsec = 0;
134 TimeSpec(ts)
135 }
136
137 #[inline]
138 fn milliseconds(milliseconds: i64) -> TimeSpec {
139 let nanoseconds = milliseconds.checked_mul(1_000_000)
140 .expect("TimeSpec::milliseconds out of bounds");
141
142 TimeSpec::nanoseconds(nanoseconds)
143 }
144
145 #[inline]
147 fn microseconds(microseconds: i64) -> TimeSpec {
148 let nanoseconds = microseconds.checked_mul(1_000)
149 .expect("TimeSpec::milliseconds out of bounds");
150
151 TimeSpec::nanoseconds(nanoseconds)
152 }
153
154 #[inline]
156 #[cfg_attr(target_env = "musl", allow(deprecated))]
157 fn nanoseconds(nanoseconds: i64) -> TimeSpec {
159 let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
160 assert!((TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
161 "TimeSpec out of bounds");
162 let mut ts = TIMESPEC_ZERO;
163 ts.tv_sec = secs as time_t;
164 ts.tv_nsec = nanos as timespec_tv_nsec_t;
165 TimeSpec(ts)
166 }
167
168 #[allow(clippy::unnecessary_cast)]
170 fn num_seconds(&self) -> i64 {
171 if self.tv_sec() < 0 && self.tv_nsec() > 0 {
172 (self.tv_sec() + 1) as i64
173 } else {
174 self.tv_sec() as i64
175 }
176 }
177
178 fn num_milliseconds(&self) -> i64 {
179 self.num_nanoseconds() / 1_000_000
180 }
181
182 fn num_microseconds(&self) -> i64 {
183 self.num_nanoseconds() / 1_000
184 }
185
186 #[allow(clippy::unnecessary_cast)]
188 fn num_nanoseconds(&self) -> i64 {
189 let secs = self.num_seconds() * 1_000_000_000;
190 let nsec = self.nanos_mod_sec();
191 secs + nsec as i64
192 }
193}
194
195impl TimeSpec {
196 fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
197 if self.tv_sec() < 0 && self.tv_nsec() > 0 {
198 self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
199 } else {
200 self.tv_nsec()
201 }
202 }
203
204 #[cfg_attr(target_env = "musl", allow(deprecated))] pub const fn tv_sec(&self) -> time_t {
206 self.0.tv_sec
207 }
208
209 pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
210 self.0.tv_nsec
211 }
212
213 #[cfg_attr(target_env = "musl", allow(deprecated))]
214 pub const fn from_duration(duration: Duration) -> Self {
216 let mut ts = TIMESPEC_ZERO;
217 ts.tv_sec = duration.as_secs() as time_t;
218 ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
219 TimeSpec(ts)
220 }
221
222 pub const fn from_timespec(timespec: timespec) -> Self {
223 Self(timespec)
224 }
225}
226
227impl ops::Neg for TimeSpec {
228 type Output = TimeSpec;
229
230 fn neg(self) -> TimeSpec {
231 TimeSpec::nanoseconds(-self.num_nanoseconds())
232 }
233}
234
235impl ops::Add for TimeSpec {
236 type Output = TimeSpec;
237
238 fn add(self, rhs: TimeSpec) -> TimeSpec {
239 TimeSpec::nanoseconds(
240 self.num_nanoseconds() + rhs.num_nanoseconds())
241 }
242}
243
244impl ops::Sub for TimeSpec {
245 type Output = TimeSpec;
246
247 fn sub(self, rhs: TimeSpec) -> TimeSpec {
248 TimeSpec::nanoseconds(
249 self.num_nanoseconds() - rhs.num_nanoseconds())
250 }
251}
252
253impl ops::Mul<i32> for TimeSpec {
254 type Output = TimeSpec;
255
256 fn mul(self, rhs: i32) -> TimeSpec {
257 let usec = self.num_nanoseconds().checked_mul(i64::from(rhs))
258 .expect("TimeSpec multiply out of bounds");
259
260 TimeSpec::nanoseconds(usec)
261 }
262}
263
264impl ops::Div<i32> for TimeSpec {
265 type Output = TimeSpec;
266
267 fn div(self, rhs: i32) -> TimeSpec {
268 let usec = self.num_nanoseconds() / i64::from(rhs);
269 TimeSpec::nanoseconds(usec)
270 }
271}
272
273impl fmt::Display for TimeSpec {
274 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
275 let (abs, sign) = if self.tv_sec() < 0 {
276 (-*self, "-")
277 } else {
278 (*self, "")
279 };
280
281 let sec = abs.tv_sec();
282
283 write!(f, "{}", sign)?;
284
285 if abs.tv_nsec() == 0 {
286 if abs.tv_sec() == 1 {
287 write!(f, "{} second", sec)?;
288 } else {
289 write!(f, "{} seconds", sec)?;
290 }
291 } else if abs.tv_nsec() % 1_000_000 == 0 {
292 write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
293 } else if abs.tv_nsec() % 1_000 == 0 {
294 write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
295 } else {
296 write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
297 }
298
299 Ok(())
300 }
301}
302
303
304
305#[repr(transparent)]
306#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
307pub struct TimeVal(timeval);
308
309const MICROS_PER_SEC: i64 = 1_000_000;
310
311#[cfg(target_pointer_width = "64")]
312const TV_MAX_SECONDS: i64 = (::std::i64::MAX / MICROS_PER_SEC) - 1;
313
314#[cfg(target_pointer_width = "32")]
315const TV_MAX_SECONDS: i64 = ::std::isize::MAX as i64;
316
317const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
318
319impl AsRef<timeval> for TimeVal {
320 fn as_ref(&self) -> &timeval {
321 &self.0
322 }
323}
324
325impl AsMut<timeval> for TimeVal {
326 fn as_mut(&mut self) -> &mut timeval {
327 &mut self.0
328 }
329}
330
331impl Ord for TimeVal {
332 fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
335 if self.tv_sec() == other.tv_sec() {
336 self.tv_usec().cmp(&other.tv_usec())
337 } else {
338 self.tv_sec().cmp(&other.tv_sec())
339 }
340 }
341}
342
343impl PartialOrd for TimeVal {
344 fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
345 Some(self.cmp(other))
346 }
347}
348
349impl TimeValLike for TimeVal {
350 #[inline]
351 fn seconds(seconds: i64) -> TimeVal {
352 assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
353 "TimeVal out of bounds; seconds={}", seconds);
354 #[cfg_attr(target_env = "musl", allow(deprecated))] TimeVal(timeval {tv_sec: seconds as time_t, tv_usec: 0 })
356 }
357
358 #[inline]
359 fn milliseconds(milliseconds: i64) -> TimeVal {
360 let microseconds = milliseconds.checked_mul(1_000)
361 .expect("TimeVal::milliseconds out of bounds");
362
363 TimeVal::microseconds(microseconds)
364 }
365
366 #[inline]
368 fn microseconds(microseconds: i64) -> TimeVal {
369 let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
370 assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
371 "TimeVal out of bounds");
372 #[cfg_attr(target_env = "musl", allow(deprecated))] TimeVal(timeval {tv_sec: secs as time_t,
374 tv_usec: micros as suseconds_t })
375 }
376
377 #[inline]
380 fn nanoseconds(nanoseconds: i64) -> TimeVal {
381 let microseconds = nanoseconds / 1000;
382 let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
383 assert!((TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
384 "TimeVal out of bounds");
385 #[cfg_attr(target_env = "musl", allow(deprecated))] TimeVal(timeval {tv_sec: secs as time_t,
387 tv_usec: micros as suseconds_t })
388 }
389
390 #[allow(clippy::unnecessary_cast)]
392 fn num_seconds(&self) -> i64 {
393 if self.tv_sec() < 0 && self.tv_usec() > 0 {
394 (self.tv_sec() + 1) as i64
395 } else {
396 self.tv_sec() as i64
397 }
398 }
399
400 fn num_milliseconds(&self) -> i64 {
401 self.num_microseconds() / 1_000
402 }
403
404 #[allow(clippy::unnecessary_cast)]
406 fn num_microseconds(&self) -> i64 {
407 let secs = self.num_seconds() * 1_000_000;
408 let usec = self.micros_mod_sec();
409 secs + usec as i64
410 }
411
412 fn num_nanoseconds(&self) -> i64 {
413 self.num_microseconds() * 1_000
414 }
415}
416
417impl TimeVal {
418 fn micros_mod_sec(&self) -> suseconds_t {
419 if self.tv_sec() < 0 && self.tv_usec() > 0 {
420 self.tv_usec() - MICROS_PER_SEC as suseconds_t
421 } else {
422 self.tv_usec()
423 }
424 }
425
426 #[cfg_attr(target_env = "musl", allow(deprecated))] pub const fn tv_sec(&self) -> time_t {
428 self.0.tv_sec
429 }
430
431 pub const fn tv_usec(&self) -> suseconds_t {
432 self.0.tv_usec
433 }
434}
435
436impl ops::Neg for TimeVal {
437 type Output = TimeVal;
438
439 fn neg(self) -> TimeVal {
440 TimeVal::microseconds(-self.num_microseconds())
441 }
442}
443
444impl ops::Add for TimeVal {
445 type Output = TimeVal;
446
447 fn add(self, rhs: TimeVal) -> TimeVal {
448 TimeVal::microseconds(
449 self.num_microseconds() + rhs.num_microseconds())
450 }
451}
452
453impl ops::Sub for TimeVal {
454 type Output = TimeVal;
455
456 fn sub(self, rhs: TimeVal) -> TimeVal {
457 TimeVal::microseconds(
458 self.num_microseconds() - rhs.num_microseconds())
459 }
460}
461
462impl ops::Mul<i32> for TimeVal {
463 type Output = TimeVal;
464
465 fn mul(self, rhs: i32) -> TimeVal {
466 let usec = self.num_microseconds().checked_mul(i64::from(rhs))
467 .expect("TimeVal multiply out of bounds");
468
469 TimeVal::microseconds(usec)
470 }
471}
472
473impl ops::Div<i32> for TimeVal {
474 type Output = TimeVal;
475
476 fn div(self, rhs: i32) -> TimeVal {
477 let usec = self.num_microseconds() / i64::from(rhs);
478 TimeVal::microseconds(usec)
479 }
480}
481
482impl fmt::Display for TimeVal {
483 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
484 let (abs, sign) = if self.tv_sec() < 0 {
485 (-*self, "-")
486 } else {
487 (*self, "")
488 };
489
490 let sec = abs.tv_sec();
491
492 write!(f, "{}", sign)?;
493
494 if abs.tv_usec() == 0 {
495 if abs.tv_sec() == 1 {
496 write!(f, "{} second", sec)?;
497 } else {
498 write!(f, "{} seconds", sec)?;
499 }
500 } else if abs.tv_usec() % 1000 == 0 {
501 write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
502 } else {
503 write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
504 }
505
506 Ok(())
507 }
508}
509
510impl From<timeval> for TimeVal {
511 fn from(tv: timeval) -> Self {
512 TimeVal(tv)
513 }
514}
515
516#[inline]
517fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
518 (div_floor_64(this, other), mod_floor_64(this, other))
519}
520
521#[inline]
522fn div_floor_64(this: i64, other: i64) -> i64 {
523 match div_rem_64(this, other) {
524 (d, r) if (r > 0 && other < 0)
525 || (r < 0 && other > 0) => d - 1,
526 (d, _) => d,
527 }
528}
529
530#[inline]
531fn mod_floor_64(this: i64, other: i64) -> i64 {
532 match this % other {
533 r if (r > 0 && other < 0)
534 || (r < 0 && other > 0) => r + other,
535 r => r,
536 }
537}
538
539#[inline]
540fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
541 (this / other, this % other)
542}
543
544#[cfg(test)]
545mod test {
546 use super::{TimeSpec, TimeVal, TimeValLike};
547 use std::time::Duration;
548
549 #[test]
550 pub fn test_timespec() {
551 assert!(TimeSpec::seconds(1) != TimeSpec::zero());
552 assert_eq!(TimeSpec::seconds(1) + TimeSpec::seconds(2),
553 TimeSpec::seconds(3));
554 assert_eq!(TimeSpec::minutes(3) + TimeSpec::seconds(2),
555 TimeSpec::seconds(182));
556 }
557
558 #[test]
559 pub fn test_timespec_from() {
560 let duration = Duration::new(123, 123_456_789);
561 let timespec = TimeSpec::nanoseconds(123_123_456_789);
562
563 assert_eq!(TimeSpec::from(duration), timespec);
564 assert_eq!(Duration::from(timespec), duration);
565 }
566
567 #[test]
568 pub fn test_timespec_neg() {
569 let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
570 let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
571
572 assert_eq!(a, -b);
573 }
574
575 #[test]
576 pub fn test_timespec_ord() {
577 assert!(TimeSpec::seconds(1) == TimeSpec::nanoseconds(1_000_000_000));
578 assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
579 assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
580 assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
581 assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
582 }
583
584 #[test]
585 pub fn test_timespec_fmt() {
586 assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
587 assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
588 assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
589 assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
590 assert_eq!(TimeSpec::nanoseconds(42).to_string(), "0.000000042 seconds");
591 assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
592 }
593
594 #[test]
595 pub fn test_timeval() {
596 assert!(TimeVal::seconds(1) != TimeVal::zero());
597 assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2),
598 TimeVal::seconds(3));
599 assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2),
600 TimeVal::seconds(182));
601 }
602
603 #[test]
604 pub fn test_timeval_ord() {
605 assert!(TimeVal::seconds(1) == TimeVal::microseconds(1_000_000));
606 assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
607 assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
608 assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
609 assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
610 }
611
612 #[test]
613 pub fn test_timeval_neg() {
614 let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
615 let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
616
617 assert_eq!(a, -b);
618 }
619
620 #[test]
621 pub fn test_timeval_fmt() {
622 assert_eq!(TimeVal::zero().to_string(), "0 seconds");
623 assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
624 assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
625 assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
626 assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
627 assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
628 }
629}