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