/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.27.1/src/sys/time.rs
Line | Count | Source |
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 | 0 | 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 | 0 | unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) } |
14 | 0 | } |
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 | 0 | fn zero() -> Self { |
149 | 0 | Self::seconds(0) |
150 | 0 | } |
151 | | |
152 | | #[inline] |
153 | 0 | fn hours(hours: i64) -> Self { |
154 | 0 | let secs = hours |
155 | 0 | .checked_mul(SECS_PER_HOUR) |
156 | 0 | .expect("TimeValLike::hours ouf of bounds"); |
157 | 0 | Self::seconds(secs) |
158 | 0 | } |
159 | | |
160 | | #[inline] |
161 | 0 | fn minutes(minutes: i64) -> Self { |
162 | 0 | let secs = minutes |
163 | 0 | .checked_mul(SECS_PER_MINUTE) |
164 | 0 | .expect("TimeValLike::minutes out of bounds"); |
165 | 0 | Self::seconds(secs) |
166 | 0 | } |
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 | 0 | fn num_hours(&self) -> i64 { |
175 | 0 | self.num_seconds() / 3600 |
176 | 0 | } |
177 | | |
178 | | #[inline] |
179 | 0 | fn num_minutes(&self) -> i64 { |
180 | 0 | self.num_seconds() / 60 |
181 | 0 | } |
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 | 0 | fn from(ts: timespec) -> Self { |
214 | 0 | Self(ts) |
215 | 0 | } |
216 | | } |
217 | | |
218 | | impl From<Duration> for TimeSpec { |
219 | 0 | fn from(duration: Duration) -> Self { |
220 | 0 | Self::from_duration(duration) |
221 | 0 | } |
222 | | } |
223 | | |
224 | | impl From<TimeSpec> for Duration { |
225 | 0 | fn from(timespec: TimeSpec) -> Self { |
226 | 0 | Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32) |
227 | 0 | } |
228 | | } |
229 | | |
230 | | impl AsRef<timespec> for TimeSpec { |
231 | 0 | fn as_ref(&self) -> ×pec { |
232 | 0 | &self.0 |
233 | 0 | } |
234 | | } |
235 | | |
236 | | impl AsMut<timespec> for TimeSpec { |
237 | 0 | fn as_mut(&mut self) -> &mut timespec { |
238 | 0 | &mut self.0 |
239 | 0 | } |
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 | 0 | fn cmp(&self, other: &TimeSpec) -> cmp::Ordering { |
246 | 0 | if self.tv_sec() == other.tv_sec() { |
247 | 0 | self.tv_nsec().cmp(&other.tv_nsec()) |
248 | | } else { |
249 | 0 | self.tv_sec().cmp(&other.tv_sec()) |
250 | | } |
251 | 0 | } |
252 | | } |
253 | | |
254 | | impl PartialOrd for TimeSpec { |
255 | 0 | fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> { |
256 | 0 | Some(self.cmp(other)) |
257 | 0 | } |
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 | 0 | fn seconds(seconds: i64) -> TimeSpec { |
265 | 0 | assert!( |
266 | 0 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds), |
267 | 0 | "TimeSpec out of bounds; seconds={seconds}", |
268 | | ); |
269 | 0 | let mut ts = zero_init_timespec(); |
270 | 0 | ts.tv_sec = seconds as time_t; |
271 | 0 | TimeSpec(ts) |
272 | 0 | } |
273 | | |
274 | | #[inline] |
275 | 0 | fn milliseconds(milliseconds: i64) -> TimeSpec { |
276 | 0 | let nanoseconds = milliseconds |
277 | 0 | .checked_mul(1_000_000) |
278 | 0 | .expect("TimeSpec::milliseconds out of bounds"); |
279 | | |
280 | 0 | TimeSpec::nanoseconds(nanoseconds) |
281 | 0 | } |
282 | | |
283 | | /// Makes a new `TimeSpec` with given number of microseconds. |
284 | | #[inline] |
285 | 0 | fn microseconds(microseconds: i64) -> TimeSpec { |
286 | 0 | let nanoseconds = microseconds |
287 | 0 | .checked_mul(1_000) |
288 | 0 | .expect("TimeSpec::milliseconds out of bounds"); |
289 | | |
290 | 0 | TimeSpec::nanoseconds(nanoseconds) |
291 | 0 | } |
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 | 0 | fn nanoseconds(nanoseconds: i64) -> TimeSpec { |
298 | 0 | let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC); |
299 | 0 | assert!( |
300 | 0 | (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs), |
301 | 0 | "TimeSpec out of bounds" |
302 | | ); |
303 | 0 | let mut ts = zero_init_timespec(); |
304 | 0 | ts.tv_sec = secs as time_t; |
305 | 0 | ts.tv_nsec = nanos as timespec_tv_nsec_t; |
306 | 0 | TimeSpec(ts) |
307 | 0 | } |
308 | | |
309 | | // The cast is not unnecessary on all platforms. |
310 | | #[allow(clippy::unnecessary_cast)] |
311 | 0 | fn num_seconds(&self) -> i64 { |
312 | 0 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
313 | 0 | (self.tv_sec() + 1) as i64 |
314 | | } else { |
315 | 0 | self.tv_sec() as i64 |
316 | | } |
317 | 0 | } |
318 | | |
319 | 0 | fn num_milliseconds(&self) -> i64 { |
320 | 0 | self.num_nanoseconds() / 1_000_000 |
321 | 0 | } |
322 | | |
323 | 0 | fn num_microseconds(&self) -> i64 { |
324 | 0 | self.num_nanoseconds() / 1_000 |
325 | 0 | } |
326 | | |
327 | | // The cast is not unnecessary on all platforms. |
328 | | #[allow(clippy::unnecessary_cast)] |
329 | 0 | fn num_nanoseconds(&self) -> i64 { |
330 | 0 | let secs = self.num_seconds() * 1_000_000_000; |
331 | 0 | let nsec = self.nanos_mod_sec(); |
332 | 0 | secs + nsec as i64 |
333 | 0 | } |
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 | 0 | pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self { |
340 | 0 | let mut ts = zero_init_timespec(); |
341 | 0 | ts.tv_sec = seconds; |
342 | 0 | ts.tv_nsec = nanoseconds; |
343 | 0 | Self(ts) |
344 | 0 | } |
345 | | |
346 | 0 | fn nanos_mod_sec(&self) -> timespec_tv_nsec_t { |
347 | 0 | if self.tv_sec() < 0 && self.tv_nsec() > 0 { |
348 | 0 | self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t |
349 | | } else { |
350 | 0 | self.tv_nsec() |
351 | | } |
352 | 0 | } |
353 | | |
354 | | #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
355 | 0 | pub const fn tv_sec(&self) -> time_t { |
356 | 0 | self.0.tv_sec |
357 | 0 | } |
358 | | |
359 | 0 | pub const fn tv_nsec(&self) -> timespec_tv_nsec_t { |
360 | 0 | self.0.tv_nsec |
361 | 0 | } |
362 | | |
363 | | #[cfg_attr(target_env = "musl", allow(deprecated))] |
364 | | // https://github.com/rust-lang/libc/issues/1848 |
365 | 0 | pub const fn from_duration(duration: Duration) -> Self { |
366 | 0 | let mut ts = zero_init_timespec(); |
367 | 0 | ts.tv_sec = duration.as_secs() as time_t; |
368 | 0 | ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t; |
369 | 0 | TimeSpec(ts) |
370 | 0 | } |
371 | | |
372 | 0 | pub const fn from_timespec(timespec: timespec) -> Self { |
373 | 0 | Self(timespec) |
374 | 0 | } |
375 | | } |
376 | | |
377 | | impl ops::Neg for TimeSpec { |
378 | | type Output = TimeSpec; |
379 | | |
380 | 0 | fn neg(self) -> TimeSpec { |
381 | 0 | TimeSpec::nanoseconds(-self.num_nanoseconds()) |
382 | 0 | } |
383 | | } |
384 | | |
385 | | impl ops::Add for TimeSpec { |
386 | | type Output = TimeSpec; |
387 | | |
388 | 0 | fn add(self, rhs: TimeSpec) -> TimeSpec { |
389 | 0 | TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds()) |
390 | 0 | } |
391 | | } |
392 | | |
393 | | impl ops::Sub for TimeSpec { |
394 | | type Output = TimeSpec; |
395 | | |
396 | 0 | fn sub(self, rhs: TimeSpec) -> TimeSpec { |
397 | 0 | TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds()) |
398 | 0 | } |
399 | | } |
400 | | |
401 | | impl ops::Mul<i32> for TimeSpec { |
402 | | type Output = TimeSpec; |
403 | | |
404 | 0 | fn mul(self, rhs: i32) -> TimeSpec { |
405 | 0 | let usec = self |
406 | 0 | .num_nanoseconds() |
407 | 0 | .checked_mul(i64::from(rhs)) |
408 | 0 | .expect("TimeSpec multiply out of bounds"); |
409 | | |
410 | 0 | TimeSpec::nanoseconds(usec) |
411 | 0 | } |
412 | | } |
413 | | |
414 | | impl ops::Div<i32> for TimeSpec { |
415 | | type Output = TimeSpec; |
416 | | |
417 | 0 | fn div(self, rhs: i32) -> TimeSpec { |
418 | 0 | let usec = self.num_nanoseconds() / i64::from(rhs); |
419 | 0 | TimeSpec::nanoseconds(usec) |
420 | 0 | } |
421 | | } |
422 | | |
423 | | impl fmt::Display for TimeSpec { |
424 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
425 | 0 | let (abs, sign) = if self.tv_sec() < 0 { |
426 | 0 | (-*self, "-") |
427 | | } else { |
428 | 0 | (*self, "") |
429 | | }; |
430 | | |
431 | 0 | let sec = abs.tv_sec(); |
432 | | |
433 | 0 | write!(f, "{sign}")?; |
434 | | |
435 | 0 | if abs.tv_nsec() == 0 { |
436 | 0 | if sec == 1 { |
437 | 0 | write!(f, "1 second")?; |
438 | | } else { |
439 | 0 | write!(f, "{sec} seconds")?; |
440 | | } |
441 | 0 | } else if abs.tv_nsec() % 1_000_000 == 0 { |
442 | 0 | write!(f, "{sec}.{:03} seconds", abs.tv_nsec() / 1_000_000)?; |
443 | 0 | } else if abs.tv_nsec() % 1_000 == 0 { |
444 | 0 | write!(f, "{sec}.{:06} seconds", abs.tv_nsec() / 1_000)?; |
445 | | } else { |
446 | 0 | write!(f, "{sec}.{:09} seconds", abs.tv_nsec())?; |
447 | | } |
448 | | |
449 | 0 | Ok(()) |
450 | 0 | } |
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 | 0 | fn as_ref(&self) -> &timeval { |
469 | 0 | &self.0 |
470 | 0 | } |
471 | | } |
472 | | |
473 | | impl AsMut<timeval> for TimeVal { |
474 | 0 | fn as_mut(&mut self) -> &mut timeval { |
475 | 0 | &mut self.0 |
476 | 0 | } |
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 | 0 | fn cmp(&self, other: &TimeVal) -> cmp::Ordering { |
483 | 0 | if self.tv_sec() == other.tv_sec() { |
484 | 0 | self.tv_usec().cmp(&other.tv_usec()) |
485 | | } else { |
486 | 0 | self.tv_sec().cmp(&other.tv_sec()) |
487 | | } |
488 | 0 | } |
489 | | } |
490 | | |
491 | | impl PartialOrd for TimeVal { |
492 | 0 | fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> { |
493 | 0 | Some(self.cmp(other)) |
494 | 0 | } |
495 | | } |
496 | | |
497 | | impl TimeValLike for TimeVal { |
498 | | #[inline] |
499 | 0 | fn seconds(seconds: i64) -> TimeVal { |
500 | 0 | assert!( |
501 | 0 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds), |
502 | 0 | "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 | 0 | TimeVal(timeval { |
507 | 0 | tv_sec: seconds as time_t, |
508 | 0 | tv_usec: 0, |
509 | 0 | }) |
510 | 0 | } |
511 | | |
512 | | #[inline] |
513 | 0 | fn milliseconds(milliseconds: i64) -> TimeVal { |
514 | 0 | let microseconds = milliseconds |
515 | 0 | .checked_mul(1_000) |
516 | 0 | .expect("TimeVal::milliseconds out of bounds"); |
517 | | |
518 | 0 | TimeVal::microseconds(microseconds) |
519 | 0 | } |
520 | | |
521 | | /// Makes a new `TimeVal` with given number of microseconds. |
522 | | #[inline] |
523 | 0 | fn microseconds(microseconds: i64) -> TimeVal { |
524 | 0 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
525 | 0 | assert!( |
526 | 0 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
527 | 0 | "TimeVal out of bounds" |
528 | | ); |
529 | | #[cfg_attr(target_env = "musl", allow(deprecated))] |
530 | | // https://github.com/rust-lang/libc/issues/1848 |
531 | 0 | TimeVal(timeval { |
532 | 0 | tv_sec: secs as time_t, |
533 | 0 | tv_usec: micros as suseconds_t, |
534 | 0 | }) |
535 | 0 | } |
536 | | |
537 | | /// Makes a new `TimeVal` with given number of nanoseconds. Some precision |
538 | | /// will be lost |
539 | | #[inline] |
540 | 0 | fn nanoseconds(nanoseconds: i64) -> TimeVal { |
541 | 0 | let microseconds = nanoseconds / 1000; |
542 | 0 | let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); |
543 | 0 | assert!( |
544 | 0 | (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs), |
545 | 0 | "TimeVal out of bounds" |
546 | | ); |
547 | | #[cfg_attr(target_env = "musl", allow(deprecated))] |
548 | | // https://github.com/rust-lang/libc/issues/1848 |
549 | 0 | TimeVal(timeval { |
550 | 0 | tv_sec: secs as time_t, |
551 | 0 | tv_usec: micros as suseconds_t, |
552 | 0 | }) |
553 | 0 | } |
554 | | |
555 | | // The cast is not unnecessary on all platforms. |
556 | | #[allow(clippy::unnecessary_cast)] |
557 | 0 | fn num_seconds(&self) -> i64 { |
558 | 0 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
559 | 0 | (self.tv_sec() + 1) as i64 |
560 | | } else { |
561 | 0 | self.tv_sec() as i64 |
562 | | } |
563 | 0 | } |
564 | | |
565 | 0 | fn num_milliseconds(&self) -> i64 { |
566 | 0 | self.num_microseconds() / 1_000 |
567 | 0 | } |
568 | | |
569 | | // The cast is not unnecessary on all platforms. |
570 | | #[allow(clippy::unnecessary_cast)] |
571 | 0 | fn num_microseconds(&self) -> i64 { |
572 | 0 | let secs = self.num_seconds() * 1_000_000; |
573 | 0 | let usec = self.micros_mod_sec(); |
574 | 0 | secs + usec as i64 |
575 | 0 | } |
576 | | |
577 | 0 | fn num_nanoseconds(&self) -> i64 { |
578 | 0 | self.num_microseconds() * 1_000 |
579 | 0 | } |
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 | 0 | pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self { |
586 | 0 | Self(timeval { |
587 | 0 | tv_sec: seconds, |
588 | 0 | tv_usec: microseconds, |
589 | 0 | }) |
590 | 0 | } |
591 | | |
592 | 0 | fn micros_mod_sec(&self) -> suseconds_t { |
593 | 0 | if self.tv_sec() < 0 && self.tv_usec() > 0 { |
594 | 0 | self.tv_usec() - MICROS_PER_SEC as suseconds_t |
595 | | } else { |
596 | 0 | self.tv_usec() |
597 | | } |
598 | 0 | } |
599 | | |
600 | | #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848 |
601 | 0 | pub const fn tv_sec(&self) -> time_t { |
602 | 0 | self.0.tv_sec |
603 | 0 | } |
604 | | |
605 | 0 | pub const fn tv_usec(&self) -> suseconds_t { |
606 | 0 | self.0.tv_usec |
607 | 0 | } |
608 | | } |
609 | | |
610 | | impl ops::Neg for TimeVal { |
611 | | type Output = TimeVal; |
612 | | |
613 | 0 | fn neg(self) -> TimeVal { |
614 | 0 | TimeVal::microseconds(-self.num_microseconds()) |
615 | 0 | } |
616 | | } |
617 | | |
618 | | impl ops::Add for TimeVal { |
619 | | type Output = TimeVal; |
620 | | |
621 | 0 | fn add(self, rhs: TimeVal) -> TimeVal { |
622 | 0 | TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds()) |
623 | 0 | } |
624 | | } |
625 | | |
626 | | impl ops::Sub for TimeVal { |
627 | | type Output = TimeVal; |
628 | | |
629 | 0 | fn sub(self, rhs: TimeVal) -> TimeVal { |
630 | 0 | TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds()) |
631 | 0 | } |
632 | | } |
633 | | |
634 | | impl ops::Mul<i32> for TimeVal { |
635 | | type Output = TimeVal; |
636 | | |
637 | 0 | fn mul(self, rhs: i32) -> TimeVal { |
638 | 0 | let usec = self |
639 | 0 | .num_microseconds() |
640 | 0 | .checked_mul(i64::from(rhs)) |
641 | 0 | .expect("TimeVal multiply out of bounds"); |
642 | | |
643 | 0 | TimeVal::microseconds(usec) |
644 | 0 | } |
645 | | } |
646 | | |
647 | | impl ops::Div<i32> for TimeVal { |
648 | | type Output = TimeVal; |
649 | | |
650 | 0 | fn div(self, rhs: i32) -> TimeVal { |
651 | 0 | let usec = self.num_microseconds() / i64::from(rhs); |
652 | 0 | TimeVal::microseconds(usec) |
653 | 0 | } |
654 | | } |
655 | | |
656 | | impl fmt::Display for TimeVal { |
657 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
658 | 0 | let (abs, sign) = if self.tv_sec() < 0 { |
659 | 0 | (-*self, "-") |
660 | | } else { |
661 | 0 | (*self, "") |
662 | | }; |
663 | | |
664 | 0 | let sec = abs.tv_sec(); |
665 | | |
666 | 0 | write!(f, "{sign}")?; |
667 | | |
668 | 0 | if abs.tv_usec() == 0 { |
669 | 0 | if sec == 1 { |
670 | 0 | write!(f, "1 second")?; |
671 | | } else { |
672 | 0 | write!(f, "{sec} seconds")?; |
673 | | } |
674 | 0 | } else if abs.tv_usec() % 1000 == 0 { |
675 | 0 | write!(f, "{sec}.{:03} seconds", abs.tv_usec() / 1000)?; |
676 | | } else { |
677 | 0 | write!(f, "{sec}.{:06} seconds", abs.tv_usec())?; |
678 | | } |
679 | | |
680 | 0 | Ok(()) |
681 | 0 | } |
682 | | } |
683 | | |
684 | | impl From<timeval> for TimeVal { |
685 | 0 | fn from(tv: timeval) -> Self { |
686 | 0 | TimeVal(tv) |
687 | 0 | } |
688 | | } |
689 | | |
690 | | #[inline] |
691 | 0 | fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { |
692 | 0 | (div_floor_64(this, other), mod_floor_64(this, other)) |
693 | 0 | } |
694 | | |
695 | | #[inline] |
696 | 0 | fn div_floor_64(this: i64, other: i64) -> i64 { |
697 | 0 | match div_rem_64(this, other) { |
698 | 0 | (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1, |
699 | 0 | (d, _) => d, |
700 | | } |
701 | 0 | } |
702 | | |
703 | | #[inline] |
704 | 0 | fn mod_floor_64(this: i64, other: i64) -> i64 { |
705 | 0 | match this % other { |
706 | 0 | r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other, |
707 | 0 | r => r, |
708 | | } |
709 | 0 | } |
710 | | |
711 | | #[inline] |
712 | 0 | fn div_rem_64(this: i64, other: i64) -> (i64, i64) { |
713 | 0 | (this / other, this % other) |
714 | 0 | } |
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 | | } |