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 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
95 | pub struct TimerSetTimeFlags: libc::c_int { |
96 | const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME; |
97 | const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET; |
98 | } |
99 | } |
100 | #[cfg (any( |
101 | target_os = "freebsd" , |
102 | target_os = "netbsd" , |
103 | target_os = "dragonfly" , |
104 | target_os = "illumos" |
105 | ))] |
106 | bitflags! { |
107 | /// Flags that are used for arming the timer. |
108 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
109 | pub struct TimerSetTimeFlags: libc::c_int { |
110 | const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME; |
111 | } |
112 | } |
113 | |
114 | impl From<TimerSpec> for Expiration { |
115 | fn from(timerspec: TimerSpec) -> Expiration { |
116 | match timerspec { |
117 | TimerSpec(libc::itimerspec { |
118 | it_interval: |
119 | libc::timespec { |
120 | tv_sec: 0, |
121 | tv_nsec: 0, |
122 | .. |
123 | }, |
124 | it_value: ts, |
125 | }) => Expiration::OneShot(ts.into()), |
126 | TimerSpec(libc::itimerspec { |
127 | it_interval: int_ts, |
128 | it_value: val_ts, |
129 | }) => { |
130 | if (int_ts.tv_sec == val_ts.tv_sec) |
131 | && (int_ts.tv_nsec == val_ts.tv_nsec) |
132 | { |
133 | Expiration::Interval(int_ts.into()) |
134 | } else { |
135 | Expiration::IntervalDelayed( |
136 | val_ts.into(), |
137 | int_ts.into(), |
138 | ) |
139 | } |
140 | } |
141 | } |
142 | } |
143 | } |
144 | } |
145 | |
146 | pub trait TimeValLike: Sized { |
147 | #[inline ] |
148 | fn zero() -> Self { |
149 | Self::seconds(0) |
150 | } |
151 | |
152 | #[inline ] |
153 | fn hours(hours: i64) -> Self { |
154 | let secs = hours |
155 | .checked_mul(SECS_PER_HOUR) |
156 | .expect("TimeValLike::hours ouf of bounds" ); |
157 | Self::seconds(secs) |
158 | } |
159 | |
160 | #[inline ] |
161 | fn minutes(minutes: i64) -> Self { |
162 | let secs = minutes |
163 | .checked_mul(SECS_PER_MINUTE) |
164 | .expect("TimeValLike::minutes out of bounds" ); |
165 | Self::seconds(secs) |
166 | } |
167 | |
168 | fn seconds(seconds: i64) -> Self; |
169 | fn milliseconds(milliseconds: i64) -> Self; |
170 | fn microseconds(microseconds: i64) -> Self; |
171 | fn nanoseconds(nanoseconds: i64) -> Self; |
172 | |
173 | #[inline ] |
174 | fn num_hours(&self) -> i64 { |
175 | self.num_seconds() / 3600 |
176 | } |
177 | |
178 | #[inline ] |
179 | fn num_minutes(&self) -> i64 { |
180 | self.num_seconds() / 60 |
181 | } |
182 | |
183 | fn num_seconds(&self) -> i64; |
184 | fn num_milliseconds(&self) -> i64; |
185 | fn num_microseconds(&self) -> i64; |
186 | fn num_nanoseconds(&self) -> i64; |
187 | } |
188 | |
189 | #[repr (C)] |
190 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
191 | pub struct TimeSpec(timespec); |
192 | |
193 | const NANOS_PER_SEC: i64 = 1_000_000_000; |
194 | const SECS_PER_MINUTE: i64 = 60; |
195 | const SECS_PER_HOUR: i64 = 3600; |
196 | |
197 | #[cfg (target_pointer_width = "64" )] |
198 | const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1; |
199 | |
200 | #[cfg (target_pointer_width = "32" )] |
201 | const TS_MAX_SECONDS: i64 = isize::MAX as i64; |
202 | |
203 | const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; |
204 | |
205 | // x32 compatibility |
206 | // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 |
207 | #[cfg (all(target_arch = "x86_64" , target_pointer_width = "32" ))] |
208 | type timespec_tv_nsec_t = i64; |
209 | #[cfg (not(all(target_arch = "x86_64" , target_pointer_width = "32" )))] |
210 | type timespec_tv_nsec_t = libc::c_long; |
211 | |
212 | impl From<timespec> for TimeSpec { |
213 | fn from(ts: timespec) -> Self { |
214 | Self(ts) |
215 | } |
216 | } |
217 | |
218 | impl From<Duration> for TimeSpec { |
219 | fn from(duration: Duration) -> Self { |
220 | Self::from_duration(duration) |
221 | } |
222 | } |
223 | |
224 | impl From<TimeSpec> for Duration { |
225 | fn from(timespec: TimeSpec) -> Self { |
226 | Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32) |
227 | } |
228 | } |
229 | |
230 | impl AsRef<timespec> for TimeSpec { |
231 | fn as_ref(&self) -> ×pec { |
232 | &self.0 |
233 | } |
234 | } |
235 | |
236 | impl AsMut<timespec> for TimeSpec { |
237 | fn as_mut(&mut self) -> &mut timespec { |
238 | &mut self.0 |
239 | } |
240 | } |
241 | |
242 | impl Ord for TimeSpec { |
243 | // The implementation of cmp is simplified by assuming that the struct is |
244 | // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) |
245 | fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { |
246 | if self.tv_sec() == other.tv_sec() { |
247 | self.tv_nsec().cmp(&other.tv_nsec()) |
248 | } else { |
249 | self.tv_sec().cmp(&other.tv_sec()) |
250 | } |
251 | } |
252 | } |
253 | |
254 | impl PartialOrd for TimeSpec { |
255 | fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> { |
256 | Some(self.cmp(other)) |
257 | } |
258 | } |
259 | |
260 | impl TimeValLike for TimeSpec { |
261 | #[inline ] |
262 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
263 | // https://github.com/rust-lang/libc/issues/1848 |
264 | fn seconds(seconds: i64) -> TimeSpec { |
265 | assert!( |
266 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds), |
267 | "TimeSpec out of bounds; seconds={seconds}" , |
268 | ); |
269 | let mut ts = zero_init_timespec(); |
270 | ts.tv_sec = seconds as time_t; |
271 | TimeSpec(ts) |
272 | } |
273 | |
274 | #[inline ] |
275 | fn milliseconds(milliseconds: i64) -> TimeSpec { |
276 | let nanoseconds = milliseconds |
277 | .checked_mul(1_000_000) |
278 | .expect("TimeSpec::milliseconds out of bounds" ); |
279 | |
280 | TimeSpec::nanoseconds(nanoseconds) |
281 | } |
282 | |
283 | /// Makes a new `TimeSpec` with given number of microseconds. |
284 | #[inline ] |
285 | fn microseconds(microseconds: i64) -> TimeSpec { |
286 | let nanoseconds = microseconds |
287 | .checked_mul(1_000) |
288 | .expect("TimeSpec::milliseconds out of bounds" ); |
289 | |
290 | TimeSpec::nanoseconds(nanoseconds) |
291 | } |
292 | |
293 | /// Makes a new `TimeSpec` with given number of nanoseconds. |
294 | #[inline ] |
295 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
296 | // https://github.com/rust-lang/libc/issues/1848 |
297 | fn nanoseconds(nanoseconds: i64) -> TimeSpec { |
298 | let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); |
299 | assert!( |
300 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs), |
301 | "TimeSpec out of bounds" |
302 | ); |
303 | let mut ts = zero_init_timespec(); |
304 | ts.tv_sec = secs as time_t; |
305 | ts.tv_nsec = nanos as timespec_tv_nsec_t; |
306 | TimeSpec(ts) |
307 | } |
308 | |
309 | // The cast is not unnecessary on all platforms. |
310 | #[allow (clippy::unnecessary_cast)] |
311 | fn num_seconds(&self) -> i64 { |
312 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
313 | (self.tv_sec() + 1) as i64 |
314 | } else { |
315 | self.tv_sec() as i64 |
316 | } |
317 | } |
318 | |
319 | fn num_milliseconds(&self) -> i64 { |
320 | self.num_nanoseconds() / 1_000_000 |
321 | } |
322 | |
323 | fn num_microseconds(&self) -> i64 { |
324 | self.num_nanoseconds() / 1_000 |
325 | } |
326 | |
327 | // The cast is not unnecessary on all platforms. |
328 | #[allow (clippy::unnecessary_cast)] |
329 | fn num_nanoseconds(&self) -> i64 { |
330 | let secs = self.num_seconds() * 1_000_000_000; |
331 | let nsec = self.nanos_mod_sec(); |
332 | secs + nsec as i64 |
333 | } |
334 | } |
335 | |
336 | impl TimeSpec { |
337 | /// Construct a new `TimeSpec` from its components |
338 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
339 | pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self { |
340 | let mut ts = zero_init_timespec(); |
341 | ts.tv_sec = seconds; |
342 | ts.tv_nsec = nanoseconds; |
343 | Self(ts) |
344 | } |
345 | |
346 | fn nanos_mod_sec(&self) -> timespec_tv_nsec_t { |
347 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
348 | self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t |
349 | } else { |
350 | self.tv_nsec() |
351 | } |
352 | } |
353 | |
354 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
355 | pub const fn tv_sec(&self) -> time_t { |
356 | self.0.tv_sec |
357 | } |
358 | |
359 | pub const fn tv_nsec(&self) -> timespec_tv_nsec_t { |
360 | self.0.tv_nsec |
361 | } |
362 | |
363 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
364 | // https://github.com/rust-lang/libc/issues/1848 |
365 | pub const fn from_duration(duration: Duration) -> Self { |
366 | let mut ts = zero_init_timespec(); |
367 | ts.tv_sec = duration.as_secs() as time_t; |
368 | ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t; |
369 | TimeSpec(ts) |
370 | } |
371 | |
372 | pub const fn from_timespec(timespec: timespec) -> Self { |
373 | Self(timespec) |
374 | } |
375 | } |
376 | |
377 | impl ops::Neg for TimeSpec { |
378 | type Output = TimeSpec; |
379 | |
380 | fn neg(self) -> TimeSpec { |
381 | TimeSpec::nanoseconds(-self.num_nanoseconds()) |
382 | } |
383 | } |
384 | |
385 | impl ops::Add for TimeSpec { |
386 | type Output = TimeSpec; |
387 | |
388 | fn add(self, rhs: TimeSpec) -> TimeSpec { |
389 | TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds()) |
390 | } |
391 | } |
392 | |
393 | impl ops::Sub for TimeSpec { |
394 | type Output = TimeSpec; |
395 | |
396 | fn sub(self, rhs: TimeSpec) -> TimeSpec { |
397 | TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds()) |
398 | } |
399 | } |
400 | |
401 | impl ops::Mul<i32> for TimeSpec { |
402 | type Output = TimeSpec; |
403 | |
404 | fn mul(self, rhs: i32) -> TimeSpec { |
405 | let usec = self |
406 | .num_nanoseconds() |
407 | .checked_mul(i64::from(rhs)) |
408 | .expect("TimeSpec multiply out of bounds" ); |
409 | |
410 | TimeSpec::nanoseconds(usec) |
411 | } |
412 | } |
413 | |
414 | impl ops::Div<i32> for TimeSpec { |
415 | type Output = TimeSpec; |
416 | |
417 | fn div(self, rhs: i32) -> TimeSpec { |
418 | let usec = self.num_nanoseconds() / i64::from(rhs); |
419 | TimeSpec::nanoseconds(usec) |
420 | } |
421 | } |
422 | |
423 | impl fmt::Display for TimeSpec { |
424 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
425 | let (abs, sign) = if self.tv_sec() < 0 { |
426 | (-*self, "-" ) |
427 | } else { |
428 | (*self, "" ) |
429 | }; |
430 | |
431 | let sec = abs.tv_sec(); |
432 | |
433 | write!(f, "{sign}" )?; |
434 | |
435 | if abs.tv_nsec() == 0 { |
436 | if sec == 1 { |
437 | write!(f, "1 second" )?; |
438 | } else { |
439 | write!(f, "{sec} seconds" )?; |
440 | } |
441 | } else if abs.tv_nsec() % 1_000_000 == 0 { |
442 | write!(f, "{sec}.{:03} seconds" , abs.tv_nsec() / 1_000_000)?; |
443 | } else if abs.tv_nsec() % 1_000 == 0 { |
444 | write!(f, "{sec}.{:06} seconds" , abs.tv_nsec() / 1_000)?; |
445 | } else { |
446 | write!(f, "{sec}.{:09} seconds" , abs.tv_nsec())?; |
447 | } |
448 | |
449 | Ok(()) |
450 | } |
451 | } |
452 | |
453 | #[repr (transparent)] |
454 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
455 | pub struct TimeVal(timeval); |
456 | |
457 | const MICROS_PER_SEC: i64 = 1_000_000; |
458 | |
459 | #[cfg (target_pointer_width = "64" )] |
460 | const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1; |
461 | |
462 | #[cfg (target_pointer_width = "32" )] |
463 | const TV_MAX_SECONDS: i64 = isize::MAX as i64; |
464 | |
465 | const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS; |
466 | |
467 | impl AsRef<timeval> for TimeVal { |
468 | fn as_ref(&self) -> &timeval { |
469 | &self.0 |
470 | } |
471 | } |
472 | |
473 | impl AsMut<timeval> for TimeVal { |
474 | fn as_mut(&mut self) -> &mut timeval { |
475 | &mut self.0 |
476 | } |
477 | } |
478 | |
479 | impl Ord for TimeVal { |
480 | // The implementation of cmp is simplified by assuming that the struct is |
481 | // normalized. That is, tv_usec must always be within [0, 1_000_000) |
482 | fn cmp(&self, other: &TimeVal) -> cmp::Ordering { |
483 | if self.tv_sec() == other.tv_sec() { |
484 | self.tv_usec().cmp(&other.tv_usec()) |
485 | } else { |
486 | self.tv_sec().cmp(&other.tv_sec()) |
487 | } |
488 | } |
489 | } |
490 | |
491 | impl PartialOrd for TimeVal { |
492 | fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> { |
493 | Some(self.cmp(other)) |
494 | } |
495 | } |
496 | |
497 | impl TimeValLike for TimeVal { |
498 | #[inline ] |
499 | fn seconds(seconds: i64) -> TimeVal { |
500 | assert!( |
501 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds), |
502 | "TimeVal out of bounds; seconds={seconds}" |
503 | ); |
504 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
505 | // https://github.com/rust-lang/libc/issues/1848 |
506 | TimeVal(timeval { |
507 | tv_sec: seconds as time_t, |
508 | tv_usec: 0, |
509 | }) |
510 | } |
511 | |
512 | #[inline ] |
513 | fn milliseconds(milliseconds: i64) -> TimeVal { |
514 | let microseconds = milliseconds |
515 | .checked_mul(1_000) |
516 | .expect("TimeVal::milliseconds out of bounds" ); |
517 | |
518 | TimeVal::microseconds(microseconds) |
519 | } |
520 | |
521 | /// Makes a new `TimeVal` with given number of microseconds. |
522 | #[inline ] |
523 | fn microseconds(microseconds: i64) -> TimeVal { |
524 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
525 | assert!( |
526 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
527 | "TimeVal out of bounds" |
528 | ); |
529 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
530 | // https://github.com/rust-lang/libc/issues/1848 |
531 | TimeVal(timeval { |
532 | tv_sec: secs as time_t, |
533 | tv_usec: micros as suseconds_t, |
534 | }) |
535 | } |
536 | |
537 | /// Makes a new `TimeVal` with given number of nanoseconds. Some precision |
538 | /// will be lost |
539 | #[inline ] |
540 | fn nanoseconds(nanoseconds: i64) -> TimeVal { |
541 | let microseconds = nanoseconds / 1000; |
542 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
543 | assert!( |
544 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
545 | "TimeVal out of bounds" |
546 | ); |
547 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
548 | // https://github.com/rust-lang/libc/issues/1848 |
549 | TimeVal(timeval { |
550 | tv_sec: secs as time_t, |
551 | tv_usec: micros as suseconds_t, |
552 | }) |
553 | } |
554 | |
555 | // The cast is not unnecessary on all platforms. |
556 | #[allow (clippy::unnecessary_cast)] |
557 | fn num_seconds(&self) -> i64 { |
558 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
559 | (self.tv_sec() + 1) as i64 |
560 | } else { |
561 | self.tv_sec() as i64 |
562 | } |
563 | } |
564 | |
565 | fn num_milliseconds(&self) -> i64 { |
566 | self.num_microseconds() / 1_000 |
567 | } |
568 | |
569 | // The cast is not unnecessary on all platforms. |
570 | #[allow (clippy::unnecessary_cast)] |
571 | fn num_microseconds(&self) -> i64 { |
572 | let secs = self.num_seconds() * 1_000_000; |
573 | let usec = self.micros_mod_sec(); |
574 | secs + usec as i64 |
575 | } |
576 | |
577 | fn num_nanoseconds(&self) -> i64 { |
578 | self.num_microseconds() * 1_000 |
579 | } |
580 | } |
581 | |
582 | impl TimeVal { |
583 | /// Construct a new `TimeVal` from its components |
584 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
585 | pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self { |
586 | Self(timeval { |
587 | tv_sec: seconds, |
588 | tv_usec: microseconds, |
589 | }) |
590 | } |
591 | |
592 | fn micros_mod_sec(&self) -> suseconds_t { |
593 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
594 | self.tv_usec() - MICROS_PER_SEC as suseconds_t |
595 | } else { |
596 | self.tv_usec() |
597 | } |
598 | } |
599 | |
600 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
601 | pub const fn tv_sec(&self) -> time_t { |
602 | self.0.tv_sec |
603 | } |
604 | |
605 | pub const fn tv_usec(&self) -> suseconds_t { |
606 | self.0.tv_usec |
607 | } |
608 | } |
609 | |
610 | impl ops::Neg for TimeVal { |
611 | type Output = TimeVal; |
612 | |
613 | fn neg(self) -> TimeVal { |
614 | TimeVal::microseconds(-self.num_microseconds()) |
615 | } |
616 | } |
617 | |
618 | impl ops::Add for TimeVal { |
619 | type Output = TimeVal; |
620 | |
621 | fn add(self, rhs: TimeVal) -> TimeVal { |
622 | TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds()) |
623 | } |
624 | } |
625 | |
626 | impl ops::Sub for TimeVal { |
627 | type Output = TimeVal; |
628 | |
629 | fn sub(self, rhs: TimeVal) -> TimeVal { |
630 | TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds()) |
631 | } |
632 | } |
633 | |
634 | impl ops::Mul<i32> for TimeVal { |
635 | type Output = TimeVal; |
636 | |
637 | fn mul(self, rhs: i32) -> TimeVal { |
638 | let usec = self |
639 | .num_microseconds() |
640 | .checked_mul(i64::from(rhs)) |
641 | .expect("TimeVal multiply out of bounds" ); |
642 | |
643 | TimeVal::microseconds(usec) |
644 | } |
645 | } |
646 | |
647 | impl ops::Div<i32> for TimeVal { |
648 | type Output = TimeVal; |
649 | |
650 | fn div(self, rhs: i32) -> TimeVal { |
651 | let usec = self.num_microseconds() / i64::from(rhs); |
652 | TimeVal::microseconds(usec) |
653 | } |
654 | } |
655 | |
656 | impl fmt::Display for TimeVal { |
657 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
658 | let (abs, sign) = if self.tv_sec() < 0 { |
659 | (-*self, "-" ) |
660 | } else { |
661 | (*self, "" ) |
662 | }; |
663 | |
664 | let sec = abs.tv_sec(); |
665 | |
666 | write!(f, "{sign}" )?; |
667 | |
668 | if abs.tv_usec() == 0 { |
669 | if sec == 1 { |
670 | write!(f, "1 second" )?; |
671 | } else { |
672 | write!(f, "{sec} seconds" )?; |
673 | } |
674 | } else if abs.tv_usec() % 1000 == 0 { |
675 | write!(f, "{sec}.{:03} seconds" , abs.tv_usec() / 1000)?; |
676 | } else { |
677 | write!(f, "{sec}.{:06} seconds" , abs.tv_usec())?; |
678 | } |
679 | |
680 | Ok(()) |
681 | } |
682 | } |
683 | |
684 | impl From<timeval> for TimeVal { |
685 | fn from(tv: timeval) -> Self { |
686 | TimeVal(tv) |
687 | } |
688 | } |
689 | |
690 | #[inline ] |
691 | fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { |
692 | (div_floor_64(this, other), mod_floor_64(this, other)) |
693 | } |
694 | |
695 | #[inline ] |
696 | fn div_floor_64(this: i64, other: i64) -> i64 { |
697 | match div_rem_64(this, other) { |
698 | (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, |
699 | (d, _) => d, |
700 | } |
701 | } |
702 | |
703 | #[inline ] |
704 | fn mod_floor_64(this: i64, other: i64) -> i64 { |
705 | match this % other { |
706 | r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, |
707 | r => r, |
708 | } |
709 | } |
710 | |
711 | #[inline ] |
712 | fn div_rem_64(this: i64, other: i64) -> (i64, i64) { |
713 | (this / other, this % other) |
714 | } |
715 | |
716 | #[cfg (test)] |
717 | mod test { |
718 | use super::{TimeSpec, TimeVal, TimeValLike}; |
719 | use std::time::Duration; |
720 | |
721 | #[test] |
722 | pub fn test_timespec() { |
723 | assert_ne!(TimeSpec::seconds(1), TimeSpec::zero()); |
724 | assert_eq!( |
725 | TimeSpec::seconds(1) + TimeSpec::seconds(2), |
726 | TimeSpec::seconds(3) |
727 | ); |
728 | assert_eq!( |
729 | TimeSpec::minutes(3) + TimeSpec::seconds(2), |
730 | TimeSpec::seconds(182) |
731 | ); |
732 | } |
733 | |
734 | #[test] |
735 | pub fn test_timespec_from() { |
736 | let duration = Duration::new(123, 123_456_789); |
737 | let timespec = TimeSpec::nanoseconds(123_123_456_789); |
738 | |
739 | assert_eq!(TimeSpec::from(duration), timespec); |
740 | assert_eq!(Duration::from(timespec), duration); |
741 | } |
742 | |
743 | #[test] |
744 | pub fn test_timespec_neg() { |
745 | let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123); |
746 | let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123); |
747 | |
748 | assert_eq!(a, -b); |
749 | } |
750 | |
751 | #[test] |
752 | pub fn test_timespec_ord() { |
753 | assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000)); |
754 | assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001)); |
755 | assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999)); |
756 | assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999)); |
757 | assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001)); |
758 | } |
759 | |
760 | #[test] |
761 | pub fn test_timespec_fmt() { |
762 | assert_eq!(TimeSpec::zero().to_string(), "0 seconds" ); |
763 | assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds" ); |
764 | assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds" ); |
765 | assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds" ); |
766 | assert_eq!( |
767 | TimeSpec::nanoseconds(42).to_string(), |
768 | "0.000000042 seconds" |
769 | ); |
770 | assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds" ); |
771 | } |
772 | |
773 | #[test] |
774 | pub fn test_timeval() { |
775 | assert_ne!(TimeVal::seconds(1), TimeVal::zero()); |
776 | assert_eq!( |
777 | TimeVal::seconds(1) + TimeVal::seconds(2), |
778 | TimeVal::seconds(3) |
779 | ); |
780 | assert_eq!( |
781 | TimeVal::minutes(3) + TimeVal::seconds(2), |
782 | TimeVal::seconds(182) |
783 | ); |
784 | } |
785 | |
786 | #[test] |
787 | pub fn test_timeval_ord() { |
788 | assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000)); |
789 | assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001)); |
790 | assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999)); |
791 | assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999)); |
792 | assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001)); |
793 | } |
794 | |
795 | #[test] |
796 | pub fn test_timeval_neg() { |
797 | let a = TimeVal::seconds(1) + TimeVal::microseconds(123); |
798 | let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); |
799 | |
800 | assert_eq!(a, -b); |
801 | } |
802 | |
803 | #[test] |
804 | pub fn test_timeval_fmt() { |
805 | assert_eq!(TimeVal::zero().to_string(), "0 seconds" ); |
806 | assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds" ); |
807 | assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds" ); |
808 | assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds" ); |
809 | assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds" ); |
810 | assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds" ); |
811 | } |
812 | } |
813 | |