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 | |
9 | 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 { |
37 | 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 { |
46 | fn as_mut(&mut self) -> &mut libc::itimerspec { |
47 | &mut self.0 |
48 | } |
49 | } |
50 | |
51 | impl AsRef<libc::itimerspec> for TimerSpec { |
52 | fn as_ref(&self) -> &libc::itimerspec { |
53 | &self.0 |
54 | } |
55 | } |
56 | |
57 | impl From<Expiration> for TimerSpec { |
58 | 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 { |
112 | 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 ] |
145 | fn zero() -> Self { |
146 | Self::seconds(0) |
147 | } |
148 | |
149 | #[inline ] |
150 | 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 ] |
158 | 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 | |
165 | fn seconds(seconds: i64) -> Self; |
166 | fn milliseconds(milliseconds: i64) -> Self; |
167 | fn microseconds(microseconds: i64) -> Self; |
168 | fn nanoseconds(nanoseconds: i64) -> Self; |
169 | |
170 | #[inline ] |
171 | fn num_hours(&self) -> i64 { |
172 | self.num_seconds() / 3600 |
173 | } |
174 | |
175 | #[inline ] |
176 | fn num_minutes(&self) -> i64 { |
177 | self.num_seconds() / 60 |
178 | } |
179 | |
180 | fn num_seconds(&self) -> i64; |
181 | fn num_milliseconds(&self) -> i64; |
182 | fn num_microseconds(&self) -> i64; |
183 | 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 { |
210 | fn from(ts: timespec) -> Self { |
211 | Self(ts) |
212 | } |
213 | } |
214 | |
215 | impl From<Duration> for TimeSpec { |
216 | fn from(duration: Duration) -> Self { |
217 | Self::from_duration(duration) |
218 | } |
219 | } |
220 | |
221 | impl From<TimeSpec> for Duration { |
222 | fn from(timespec: TimeSpec) -> Self { |
223 | Duration::new(secs:timespec.0.tv_sec as u64, nanos:timespec.0.tv_nsec as u32) |
224 | } |
225 | } |
226 | |
227 | impl AsRef<timespec> for TimeSpec { |
228 | fn as_ref(&self) -> ×pec { |
229 | &self.0 |
230 | } |
231 | } |
232 | |
233 | impl AsMut<timespec> for TimeSpec { |
234 | 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) |
242 | 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 { |
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 |
261 | 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 ] |
273 | 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 ] |
283 | 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 |
295 | 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)] |
309 | 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 | |
317 | fn num_milliseconds(&self) -> i64 { |
318 | self.num_nanoseconds() / 1_000_000 |
319 | } |
320 | |
321 | 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)] |
327 | 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 |
337 | 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 | |
344 | 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 |
353 | pub const fn tv_sec(&self) -> time_t { |
354 | self.0.tv_sec |
355 | } |
356 | |
357 | 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 |
363 | 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 | |
370 | 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 | |
378 | fn neg(self) -> TimeSpec { |
379 | TimeSpec::nanoseconds(-self.num_nanoseconds()) |
380 | } |
381 | } |
382 | |
383 | impl ops::Add for TimeSpec { |
384 | type Output = TimeSpec; |
385 | |
386 | 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 | |
394 | 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 | |
402 | fn mul(self, rhs: i32) -> TimeSpec { |
403 | let usec: i64 = self |
404 | .num_nanoseconds() |
405 | .checked_mul(i64::from(rhs)) |
406 | .expect(msg:"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 | |
415 | fn div(self, rhs: i32) -> TimeSpec { |
416 | let usec: i64 = self.num_nanoseconds() / i64::from(rhs); |
417 | TimeSpec::nanoseconds(usec) |
418 | } |
419 | } |
420 | |
421 | impl fmt::Display for TimeSpec { |
422 | 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 { |
466 | fn as_ref(&self) -> &timeval { |
467 | &self.0 |
468 | } |
469 | } |
470 | |
471 | impl AsMut<timeval> for TimeVal { |
472 | 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) |
480 | 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 { |
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 ] |
497 | 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 ] |
512 | 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 ] |
522 | 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 ] |
539 | 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)] |
556 | 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 | |
564 | 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)] |
570 | 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 | |
576 | 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 |
584 | 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 | |
591 | 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 |
600 | pub const fn tv_sec(&self) -> time_t { |
601 | self.0.tv_sec |
602 | } |
603 | |
604 | 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 | |
612 | fn neg(self) -> TimeVal { |
613 | TimeVal::microseconds(-self.num_microseconds()) |
614 | } |
615 | } |
616 | |
617 | impl ops::Add for TimeVal { |
618 | type Output = TimeVal; |
619 | |
620 | 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 | |
628 | 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 | |
636 | fn mul(self, rhs: i32) -> TimeVal { |
637 | let usec: i64 = self |
638 | .num_microseconds() |
639 | .checked_mul(i64::from(rhs)) |
640 | .expect(msg:"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 | |
649 | fn div(self, rhs: i32) -> TimeVal { |
650 | let usec: i64 = self.num_microseconds() / i64::from(rhs); |
651 | TimeVal::microseconds(usec) |
652 | } |
653 | } |
654 | |
655 | impl fmt::Display for TimeVal { |
656 | 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 { |
684 | fn from(tv: timeval) -> Self { |
685 | TimeVal(tv) |
686 | } |
687 | } |
688 | |
689 | #[inline ] |
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 ] |
695 | fn div_floor_64(this: i64, other: i64) -> i64 { |
696 | match div_rem_64(this, other) { |
697 | (d: i64, r: i64) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, |
698 | (d: i64, _) => d, |
699 | } |
700 | } |
701 | |
702 | #[inline ] |
703 | fn mod_floor_64(this: i64, other: i64) -> i64 { |
704 | match this % other { |
705 | r: i64 if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, |
706 | r: i64 => r, |
707 | } |
708 | } |
709 | |
710 | #[inline ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 | |