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::time::Duration; |
6 | use std::{cmp, fmt, ops}; |
7 | |
8 | const fn zero_init_timespec() -> timespec { |
9 | // `std::mem::MaybeUninit::zeroed()` is not yet a const fn |
10 | // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of |
11 | // the appropriate size to zero and then transmute it to a timespec value. |
12 | unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) } |
13 | } |
14 | |
15 | #[cfg (any( |
16 | all(feature = "time" , any(target_os = "android" , target_os = "linux" )), |
17 | all( |
18 | any( |
19 | target_os = "freebsd" , |
20 | solarish, |
21 | target_os = "linux" , |
22 | target_os = "netbsd" |
23 | ), |
24 | feature = "time" , |
25 | feature = "signal" |
26 | ) |
27 | ))] |
28 | pub(crate) mod timer { |
29 | use crate::sys::time::{zero_init_timespec, TimeSpec}; |
30 | use bitflags::bitflags; |
31 | |
32 | #[derive (Debug, Clone, Copy)] |
33 | pub(crate) struct TimerSpec(libc::itimerspec); |
34 | |
35 | impl TimerSpec { |
36 | pub const fn none() -> Self { |
37 | Self(libc::itimerspec { |
38 | it_interval: zero_init_timespec(), |
39 | it_value: zero_init_timespec(), |
40 | }) |
41 | } |
42 | } |
43 | |
44 | impl AsMut<libc::itimerspec> for TimerSpec { |
45 | fn as_mut(&mut self) -> &mut libc::itimerspec { |
46 | &mut self.0 |
47 | } |
48 | } |
49 | |
50 | impl AsRef<libc::itimerspec> for TimerSpec { |
51 | fn as_ref(&self) -> &libc::itimerspec { |
52 | &self.0 |
53 | } |
54 | } |
55 | |
56 | impl From<Expiration> for TimerSpec { |
57 | fn from(expiration: Expiration) -> TimerSpec { |
58 | match expiration { |
59 | Expiration::OneShot(t) => TimerSpec(libc::itimerspec { |
60 | it_interval: zero_init_timespec(), |
61 | it_value: *t.as_ref(), |
62 | }), |
63 | Expiration::IntervalDelayed(start, interval) => { |
64 | TimerSpec(libc::itimerspec { |
65 | it_interval: *interval.as_ref(), |
66 | it_value: *start.as_ref(), |
67 | }) |
68 | } |
69 | Expiration::Interval(t) => TimerSpec(libc::itimerspec { |
70 | it_interval: *t.as_ref(), |
71 | it_value: *t.as_ref(), |
72 | }), |
73 | } |
74 | } |
75 | } |
76 | |
77 | /// An enumeration allowing the definition of the expiration time of an alarm, |
78 | /// recurring or not. |
79 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
80 | pub enum Expiration { |
81 | /// Alarm will trigger once after the time given in `TimeSpec` |
82 | OneShot(TimeSpec), |
83 | /// Alarm will trigger after a specified delay and then every interval of |
84 | /// time. |
85 | IntervalDelayed(TimeSpec, TimeSpec), |
86 | /// Alarm will trigger every specified interval of time. |
87 | Interval(TimeSpec), |
88 | } |
89 | |
90 | #[cfg (linux_android)] |
91 | bitflags! { |
92 | /// Flags that are used for arming the timer. |
93 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
94 | pub struct TimerSetTimeFlags: libc::c_int { |
95 | const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME; |
96 | const TFD_TIMER_CANCEL_ON_SET = libc::TFD_TIMER_CANCEL_ON_SET; |
97 | } |
98 | } |
99 | #[cfg (any(freebsdlike, target_os = "netbsd" , solarish))] |
100 | bitflags! { |
101 | /// Flags that are used for arming the timer. |
102 | #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
103 | pub struct TimerSetTimeFlags: libc::c_int { |
104 | const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME; |
105 | } |
106 | } |
107 | |
108 | impl From<TimerSpec> for Expiration { |
109 | fn from(timerspec: TimerSpec) -> Expiration { |
110 | match timerspec { |
111 | TimerSpec(libc::itimerspec { |
112 | it_interval: |
113 | libc::timespec { |
114 | tv_sec: 0, |
115 | tv_nsec: 0, |
116 | .. |
117 | }, |
118 | it_value: ts, |
119 | }) => Expiration::OneShot(ts.into()), |
120 | TimerSpec(libc::itimerspec { |
121 | it_interval: int_ts, |
122 | it_value: val_ts, |
123 | }) => { |
124 | if (int_ts.tv_sec == val_ts.tv_sec) |
125 | && (int_ts.tv_nsec == val_ts.tv_nsec) |
126 | { |
127 | Expiration::Interval(int_ts.into()) |
128 | } else { |
129 | Expiration::IntervalDelayed( |
130 | val_ts.into(), |
131 | int_ts.into(), |
132 | ) |
133 | } |
134 | } |
135 | } |
136 | } |
137 | } |
138 | } |
139 | |
140 | pub trait TimeValLike: Sized { |
141 | #[inline ] |
142 | fn zero() -> Self { |
143 | Self::seconds(0) |
144 | } |
145 | |
146 | #[inline ] |
147 | fn hours(hours: i64) -> Self { |
148 | let secs = hours |
149 | .checked_mul(SECS_PER_HOUR) |
150 | .expect("TimeValLike::hours ouf of bounds" ); |
151 | Self::seconds(secs) |
152 | } |
153 | |
154 | #[inline ] |
155 | fn minutes(minutes: i64) -> Self { |
156 | let secs = minutes |
157 | .checked_mul(SECS_PER_MINUTE) |
158 | .expect("TimeValLike::minutes out of bounds" ); |
159 | Self::seconds(secs) |
160 | } |
161 | |
162 | fn seconds(seconds: i64) -> Self; |
163 | fn milliseconds(milliseconds: i64) -> Self; |
164 | fn microseconds(microseconds: i64) -> Self; |
165 | fn nanoseconds(nanoseconds: i64) -> Self; |
166 | |
167 | #[inline ] |
168 | fn num_hours(&self) -> i64 { |
169 | self.num_seconds() / 3600 |
170 | } |
171 | |
172 | #[inline ] |
173 | fn num_minutes(&self) -> i64 { |
174 | self.num_seconds() / 60 |
175 | } |
176 | |
177 | fn num_seconds(&self) -> i64; |
178 | fn num_milliseconds(&self) -> i64; |
179 | fn num_microseconds(&self) -> i64; |
180 | fn num_nanoseconds(&self) -> i64; |
181 | } |
182 | |
183 | #[repr (C)] |
184 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
185 | pub struct TimeSpec(timespec); |
186 | |
187 | const NANOS_PER_SEC: i64 = 1_000_000_000; |
188 | const SECS_PER_MINUTE: i64 = 60; |
189 | const SECS_PER_HOUR: i64 = 3600; |
190 | |
191 | #[cfg (target_pointer_width = "64" )] |
192 | const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1; |
193 | |
194 | #[cfg (target_pointer_width = "32" )] |
195 | const TS_MAX_SECONDS: i64 = isize::MAX as i64; |
196 | |
197 | const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS; |
198 | |
199 | // x32 compatibility |
200 | // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 |
201 | #[cfg (all(target_arch = "x86_64" , target_pointer_width = "32" ))] |
202 | type timespec_tv_nsec_t = i64; |
203 | #[cfg (not(all(target_arch = "x86_64" , target_pointer_width = "32" )))] |
204 | type timespec_tv_nsec_t = libc::c_long; |
205 | |
206 | impl From<timespec> for TimeSpec { |
207 | fn from(ts: timespec) -> Self { |
208 | Self(ts) |
209 | } |
210 | } |
211 | |
212 | impl From<Duration> for TimeSpec { |
213 | fn from(duration: Duration) -> Self { |
214 | Self::from_duration(duration) |
215 | } |
216 | } |
217 | |
218 | impl From<TimeSpec> for Duration { |
219 | fn from(timespec: TimeSpec) -> Self { |
220 | Duration::new(secs:timespec.0.tv_sec as u64, nanos:timespec.0.tv_nsec as u32) |
221 | } |
222 | } |
223 | |
224 | impl AsRef<timespec> for TimeSpec { |
225 | fn as_ref(&self) -> ×pec { |
226 | &self.0 |
227 | } |
228 | } |
229 | |
230 | impl AsMut<timespec> for TimeSpec { |
231 | fn as_mut(&mut self) -> &mut timespec { |
232 | &mut self.0 |
233 | } |
234 | } |
235 | |
236 | impl Ord for TimeSpec { |
237 | // The implementation of cmp is simplified by assuming that the struct is |
238 | // normalized. That is, tv_nsec must always be within [0, 1_000_000_000) |
239 | fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { |
240 | if self.tv_sec() == other.tv_sec() { |
241 | self.tv_nsec().cmp(&other.tv_nsec()) |
242 | } else { |
243 | self.tv_sec().cmp(&other.tv_sec()) |
244 | } |
245 | } |
246 | } |
247 | |
248 | impl PartialOrd for TimeSpec { |
249 | fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> { |
250 | Some(self.cmp(other)) |
251 | } |
252 | } |
253 | |
254 | impl TimeValLike for TimeSpec { |
255 | #[inline ] |
256 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
257 | // https://github.com/rust-lang/libc/issues/1848 |
258 | fn seconds(seconds: i64) -> TimeSpec { |
259 | assert!( |
260 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds), |
261 | "TimeSpec out of bounds; seconds= {seconds}" , |
262 | ); |
263 | let mut ts = zero_init_timespec(); |
264 | ts.tv_sec = seconds as time_t; |
265 | TimeSpec(ts) |
266 | } |
267 | |
268 | #[inline ] |
269 | fn milliseconds(milliseconds: i64) -> TimeSpec { |
270 | let nanoseconds = milliseconds |
271 | .checked_mul(1_000_000) |
272 | .expect("TimeSpec::milliseconds out of bounds" ); |
273 | |
274 | TimeSpec::nanoseconds(nanoseconds) |
275 | } |
276 | |
277 | /// Makes a new `TimeSpec` with given number of microseconds. |
278 | #[inline ] |
279 | fn microseconds(microseconds: i64) -> TimeSpec { |
280 | let nanoseconds = microseconds |
281 | .checked_mul(1_000) |
282 | .expect("TimeSpec::milliseconds out of bounds" ); |
283 | |
284 | TimeSpec::nanoseconds(nanoseconds) |
285 | } |
286 | |
287 | /// Makes a new `TimeSpec` with given number of nanoseconds. |
288 | #[inline ] |
289 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
290 | // https://github.com/rust-lang/libc/issues/1848 |
291 | fn nanoseconds(nanoseconds: i64) -> TimeSpec { |
292 | let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); |
293 | assert!( |
294 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs), |
295 | "TimeSpec out of bounds" |
296 | ); |
297 | let mut ts = zero_init_timespec(); |
298 | ts.tv_sec = secs as time_t; |
299 | ts.tv_nsec = nanos as timespec_tv_nsec_t; |
300 | TimeSpec(ts) |
301 | } |
302 | |
303 | // The cast is not unnecessary on all platforms. |
304 | #[allow (clippy::unnecessary_cast)] |
305 | fn num_seconds(&self) -> i64 { |
306 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
307 | (self.tv_sec() + 1) as i64 |
308 | } else { |
309 | self.tv_sec() as i64 |
310 | } |
311 | } |
312 | |
313 | fn num_milliseconds(&self) -> i64 { |
314 | self.num_nanoseconds() / 1_000_000 |
315 | } |
316 | |
317 | fn num_microseconds(&self) -> i64 { |
318 | self.num_nanoseconds() / 1_000 |
319 | } |
320 | |
321 | // The cast is not unnecessary on all platforms. |
322 | #[allow (clippy::unnecessary_cast)] |
323 | fn num_nanoseconds(&self) -> i64 { |
324 | let secs = self.num_seconds() * 1_000_000_000; |
325 | let nsec = self.nanos_mod_sec(); |
326 | secs + nsec as i64 |
327 | } |
328 | } |
329 | |
330 | impl TimeSpec { |
331 | /// Leave the timestamp unchanged. |
332 | #[cfg (not(target_os = "redox" ))] |
333 | // At the time of writing this PR, redox does not support this feature |
334 | pub const UTIME_OMIT: TimeSpec = |
335 | TimeSpec::new(0, libc::UTIME_OMIT as timespec_tv_nsec_t); |
336 | /// Update the timestamp to `Now` |
337 | // At the time of writing this PR, redox does not support this feature |
338 | #[cfg (not(target_os = "redox" ))] |
339 | pub const UTIME_NOW: TimeSpec = |
340 | TimeSpec::new(0, libc::UTIME_NOW as timespec_tv_nsec_t); |
341 | |
342 | /// Construct a new `TimeSpec` from its components |
343 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
344 | pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self { |
345 | let mut ts = zero_init_timespec(); |
346 | ts.tv_sec = seconds; |
347 | ts.tv_nsec = nanoseconds; |
348 | Self(ts) |
349 | } |
350 | |
351 | fn nanos_mod_sec(&self) -> timespec_tv_nsec_t { |
352 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
353 | self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t |
354 | } else { |
355 | self.tv_nsec() |
356 | } |
357 | } |
358 | |
359 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
360 | pub const fn tv_sec(&self) -> time_t { |
361 | self.0.tv_sec |
362 | } |
363 | |
364 | pub const fn tv_nsec(&self) -> timespec_tv_nsec_t { |
365 | self.0.tv_nsec |
366 | } |
367 | |
368 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
369 | // https://github.com/rust-lang/libc/issues/1848 |
370 | pub const fn from_duration(duration: Duration) -> Self { |
371 | let mut ts = zero_init_timespec(); |
372 | ts.tv_sec = duration.as_secs() as time_t; |
373 | ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t; |
374 | TimeSpec(ts) |
375 | } |
376 | |
377 | pub const fn from_timespec(timespec: timespec) -> Self { |
378 | Self(timespec) |
379 | } |
380 | } |
381 | |
382 | impl ops::Neg for TimeSpec { |
383 | type Output = TimeSpec; |
384 | |
385 | fn neg(self) -> TimeSpec { |
386 | TimeSpec::nanoseconds(-self.num_nanoseconds()) |
387 | } |
388 | } |
389 | |
390 | impl ops::Add for TimeSpec { |
391 | type Output = TimeSpec; |
392 | |
393 | fn add(self, rhs: TimeSpec) -> TimeSpec { |
394 | TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds()) |
395 | } |
396 | } |
397 | |
398 | impl ops::Sub for TimeSpec { |
399 | type Output = TimeSpec; |
400 | |
401 | fn sub(self, rhs: TimeSpec) -> TimeSpec { |
402 | TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds()) |
403 | } |
404 | } |
405 | |
406 | impl ops::Mul<i32> for TimeSpec { |
407 | type Output = TimeSpec; |
408 | |
409 | fn mul(self, rhs: i32) -> TimeSpec { |
410 | let usec: i64 = self |
411 | .num_nanoseconds() |
412 | .checked_mul(i64::from(rhs)) |
413 | .expect(msg:"TimeSpec multiply out of bounds" ); |
414 | |
415 | TimeSpec::nanoseconds(usec) |
416 | } |
417 | } |
418 | |
419 | impl ops::Div<i32> for TimeSpec { |
420 | type Output = TimeSpec; |
421 | |
422 | fn div(self, rhs: i32) -> TimeSpec { |
423 | let usec: i64 = self.num_nanoseconds() / i64::from(rhs); |
424 | TimeSpec::nanoseconds(usec) |
425 | } |
426 | } |
427 | |
428 | impl fmt::Display for TimeSpec { |
429 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
430 | let (abs, sign) = if self.tv_sec() < 0 { |
431 | (-*self, "-" ) |
432 | } else { |
433 | (*self, "" ) |
434 | }; |
435 | |
436 | let sec = abs.tv_sec(); |
437 | |
438 | write!(f, " {sign}" )?; |
439 | |
440 | if abs.tv_nsec() == 0 { |
441 | if sec == 1 { |
442 | write!(f, "1 second" )?; |
443 | } else { |
444 | write!(f, " {sec} seconds" )?; |
445 | } |
446 | } else if abs.tv_nsec() % 1_000_000 == 0 { |
447 | write!(f, " {sec}. {:03} seconds" , abs.tv_nsec() / 1_000_000)?; |
448 | } else if abs.tv_nsec() % 1_000 == 0 { |
449 | write!(f, " {sec}. {:06} seconds" , abs.tv_nsec() / 1_000)?; |
450 | } else { |
451 | write!(f, " {sec}. {:09} seconds" , abs.tv_nsec())?; |
452 | } |
453 | |
454 | Ok(()) |
455 | } |
456 | } |
457 | |
458 | #[repr (transparent)] |
459 | #[derive (Clone, Copy, Debug, Eq, Hash, PartialEq)] |
460 | pub struct TimeVal(timeval); |
461 | |
462 | const MICROS_PER_SEC: i64 = 1_000_000; |
463 | |
464 | #[cfg (target_pointer_width = "64" )] |
465 | const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1; |
466 | |
467 | #[cfg (target_pointer_width = "32" )] |
468 | const TV_MAX_SECONDS: i64 = isize::MAX as i64; |
469 | |
470 | const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS; |
471 | |
472 | impl AsRef<timeval> for TimeVal { |
473 | fn as_ref(&self) -> &timeval { |
474 | &self.0 |
475 | } |
476 | } |
477 | |
478 | impl AsMut<timeval> for TimeVal { |
479 | fn as_mut(&mut self) -> &mut timeval { |
480 | &mut self.0 |
481 | } |
482 | } |
483 | |
484 | impl Ord for TimeVal { |
485 | // The implementation of cmp is simplified by assuming that the struct is |
486 | // normalized. That is, tv_usec must always be within [0, 1_000_000) |
487 | fn cmp(&self, other: &TimeVal) -> cmp::Ordering { |
488 | if self.tv_sec() == other.tv_sec() { |
489 | self.tv_usec().cmp(&other.tv_usec()) |
490 | } else { |
491 | self.tv_sec().cmp(&other.tv_sec()) |
492 | } |
493 | } |
494 | } |
495 | |
496 | impl PartialOrd for TimeVal { |
497 | fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> { |
498 | Some(self.cmp(other)) |
499 | } |
500 | } |
501 | |
502 | impl TimeValLike for TimeVal { |
503 | #[inline ] |
504 | fn seconds(seconds: i64) -> TimeVal { |
505 | assert!( |
506 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds), |
507 | "TimeVal out of bounds; seconds= {seconds}" |
508 | ); |
509 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
510 | // https://github.com/rust-lang/libc/issues/1848 |
511 | TimeVal(timeval { |
512 | tv_sec: seconds as time_t, |
513 | tv_usec: 0, |
514 | }) |
515 | } |
516 | |
517 | #[inline ] |
518 | fn milliseconds(milliseconds: i64) -> TimeVal { |
519 | let microseconds = milliseconds |
520 | .checked_mul(1_000) |
521 | .expect("TimeVal::milliseconds out of bounds" ); |
522 | |
523 | TimeVal::microseconds(microseconds) |
524 | } |
525 | |
526 | /// Makes a new `TimeVal` with given number of microseconds. |
527 | #[inline ] |
528 | fn microseconds(microseconds: i64) -> TimeVal { |
529 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
530 | assert!( |
531 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
532 | "TimeVal out of bounds" |
533 | ); |
534 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
535 | // https://github.com/rust-lang/libc/issues/1848 |
536 | TimeVal(timeval { |
537 | tv_sec: secs as time_t, |
538 | tv_usec: micros as suseconds_t, |
539 | }) |
540 | } |
541 | |
542 | /// Makes a new `TimeVal` with given number of nanoseconds. Some precision |
543 | /// will be lost |
544 | #[inline ] |
545 | fn nanoseconds(nanoseconds: i64) -> TimeVal { |
546 | let microseconds = nanoseconds / 1000; |
547 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
548 | assert!( |
549 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
550 | "TimeVal out of bounds" |
551 | ); |
552 | #[cfg_attr (target_env = "musl" , allow(deprecated))] |
553 | // https://github.com/rust-lang/libc/issues/1848 |
554 | TimeVal(timeval { |
555 | tv_sec: secs as time_t, |
556 | tv_usec: micros as suseconds_t, |
557 | }) |
558 | } |
559 | |
560 | // The cast is not unnecessary on all platforms. |
561 | #[allow (clippy::unnecessary_cast)] |
562 | fn num_seconds(&self) -> i64 { |
563 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
564 | (self.tv_sec() + 1) as i64 |
565 | } else { |
566 | self.tv_sec() as i64 |
567 | } |
568 | } |
569 | |
570 | fn num_milliseconds(&self) -> i64 { |
571 | self.num_microseconds() / 1_000 |
572 | } |
573 | |
574 | // The cast is not unnecessary on all platforms. |
575 | #[allow (clippy::unnecessary_cast)] |
576 | fn num_microseconds(&self) -> i64 { |
577 | let secs = self.num_seconds() * 1_000_000; |
578 | let usec = self.micros_mod_sec(); |
579 | secs + usec as i64 |
580 | } |
581 | |
582 | fn num_nanoseconds(&self) -> i64 { |
583 | self.num_microseconds() * 1_000 |
584 | } |
585 | } |
586 | |
587 | impl TimeVal { |
588 | /// Construct a new `TimeVal` from its components |
589 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
590 | pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self { |
591 | Self(timeval { |
592 | tv_sec: seconds, |
593 | tv_usec: microseconds, |
594 | }) |
595 | } |
596 | |
597 | fn micros_mod_sec(&self) -> suseconds_t { |
598 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
599 | self.tv_usec() - MICROS_PER_SEC as suseconds_t |
600 | } else { |
601 | self.tv_usec() |
602 | } |
603 | } |
604 | |
605 | #[cfg_attr (target_env = "musl" , allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
606 | pub const fn tv_sec(&self) -> time_t { |
607 | self.0.tv_sec |
608 | } |
609 | |
610 | pub const fn tv_usec(&self) -> suseconds_t { |
611 | self.0.tv_usec |
612 | } |
613 | } |
614 | |
615 | impl ops::Neg for TimeVal { |
616 | type Output = TimeVal; |
617 | |
618 | fn neg(self) -> TimeVal { |
619 | TimeVal::microseconds(-self.num_microseconds()) |
620 | } |
621 | } |
622 | |
623 | impl ops::Add for TimeVal { |
624 | type Output = TimeVal; |
625 | |
626 | fn add(self, rhs: TimeVal) -> TimeVal { |
627 | TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds()) |
628 | } |
629 | } |
630 | |
631 | impl ops::Sub for TimeVal { |
632 | type Output = TimeVal; |
633 | |
634 | fn sub(self, rhs: TimeVal) -> TimeVal { |
635 | TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds()) |
636 | } |
637 | } |
638 | |
639 | impl ops::Mul<i32> for TimeVal { |
640 | type Output = TimeVal; |
641 | |
642 | fn mul(self, rhs: i32) -> TimeVal { |
643 | let usec: i64 = self |
644 | .num_microseconds() |
645 | .checked_mul(i64::from(rhs)) |
646 | .expect(msg:"TimeVal multiply out of bounds" ); |
647 | |
648 | TimeVal::microseconds(usec) |
649 | } |
650 | } |
651 | |
652 | impl ops::Div<i32> for TimeVal { |
653 | type Output = TimeVal; |
654 | |
655 | fn div(self, rhs: i32) -> TimeVal { |
656 | let usec: i64 = self.num_microseconds() / i64::from(rhs); |
657 | TimeVal::microseconds(usec) |
658 | } |
659 | } |
660 | |
661 | impl fmt::Display for TimeVal { |
662 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
663 | let (abs, sign) = if self.tv_sec() < 0 { |
664 | (-*self, "-" ) |
665 | } else { |
666 | (*self, "" ) |
667 | }; |
668 | |
669 | let sec = abs.tv_sec(); |
670 | |
671 | write!(f, " {sign}" )?; |
672 | |
673 | if abs.tv_usec() == 0 { |
674 | if sec == 1 { |
675 | write!(f, "1 second" )?; |
676 | } else { |
677 | write!(f, " {sec} seconds" )?; |
678 | } |
679 | } else if abs.tv_usec() % 1000 == 0 { |
680 | write!(f, " {sec}. {:03} seconds" , abs.tv_usec() / 1000)?; |
681 | } else { |
682 | write!(f, " {sec}. {:06} seconds" , abs.tv_usec())?; |
683 | } |
684 | |
685 | Ok(()) |
686 | } |
687 | } |
688 | |
689 | impl From<timeval> for TimeVal { |
690 | fn from(tv: timeval) -> Self { |
691 | TimeVal(tv) |
692 | } |
693 | } |
694 | |
695 | #[inline ] |
696 | fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { |
697 | (div_floor_64(this, other), mod_floor_64(this, other)) |
698 | } |
699 | |
700 | #[inline ] |
701 | fn div_floor_64(this: i64, other: i64) -> i64 { |
702 | match div_rem_64(this, other) { |
703 | (d: i64, r: i64) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, |
704 | (d: i64, _) => d, |
705 | } |
706 | } |
707 | |
708 | #[inline ] |
709 | fn mod_floor_64(this: i64, other: i64) -> i64 { |
710 | match this % other { |
711 | r: i64 if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, |
712 | r: i64 => r, |
713 | } |
714 | } |
715 | |
716 | #[inline ] |
717 | fn div_rem_64(this: i64, other: i64) -> (i64, i64) { |
718 | (this / other, this % other) |
719 | } |
720 | |