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