nix/sys/
time.rs

1#[cfg_attr(target_env = "musl", allow(deprecated))]
2// https://github.com/rust-lang/libc/issues/1848
3pub use libc::{suseconds_t, time_t};
4use libc::{timespec, timeval};
5use std::time::Duration;
6use std::{cmp, fmt, ops};
7
8const 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))]
28pub(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
140pub 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)]
185pub struct TimeSpec(timespec);
186
187const NANOS_PER_SEC: i64 = 1_000_000_000;
188const SECS_PER_MINUTE: i64 = 60;
189const SECS_PER_HOUR: i64 = 3600;
190
191#[cfg(target_pointer_width = "64")]
192const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
193
194#[cfg(target_pointer_width = "32")]
195const TS_MAX_SECONDS: i64 = isize::MAX as i64;
196
197const 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"))]
202type timespec_tv_nsec_t = i64;
203#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
204type timespec_tv_nsec_t = libc::c_long;
205
206impl From<timespec> for TimeSpec {
207    fn from(ts: timespec) -> Self {
208        Self(ts)
209    }
210}
211
212impl From<Duration> for TimeSpec {
213    fn from(duration: Duration) -> Self {
214        Self::from_duration(duration)
215    }
216}
217
218impl From<TimeSpec> for Duration {
219    fn from(timespec: TimeSpec) -> Self {
220        Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
221    }
222}
223
224impl AsRef<timespec> for TimeSpec {
225    fn as_ref(&self) -> &timespec {
226        &self.0
227    }
228}
229
230impl AsMut<timespec> for TimeSpec {
231    fn as_mut(&mut self) -> &mut timespec {
232        &mut self.0
233    }
234}
235
236impl 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
248impl PartialOrd for TimeSpec {
249    fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
250        Some(self.cmp(other))
251    }
252}
253
254impl 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
330impl 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
382impl ops::Neg for TimeSpec {
383    type Output = TimeSpec;
384
385    fn neg(self) -> TimeSpec {
386        TimeSpec::nanoseconds(-self.num_nanoseconds())
387    }
388}
389
390impl 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
398impl 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
406impl ops::Mul<i32> for TimeSpec {
407    type Output = TimeSpec;
408
409    fn mul(self, rhs: i32) -> TimeSpec {
410        let usec = self
411            .num_nanoseconds()
412            .checked_mul(i64::from(rhs))
413            .expect("TimeSpec multiply out of bounds");
414
415        TimeSpec::nanoseconds(usec)
416    }
417}
418
419impl ops::Div<i32> for TimeSpec {
420    type Output = TimeSpec;
421
422    fn div(self, rhs: i32) -> TimeSpec {
423        let usec = self.num_nanoseconds() / i64::from(rhs);
424        TimeSpec::nanoseconds(usec)
425    }
426}
427
428impl 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)]
460pub struct TimeVal(timeval);
461
462const MICROS_PER_SEC: i64 = 1_000_000;
463
464#[cfg(target_pointer_width = "64")]
465const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
466
467#[cfg(target_pointer_width = "32")]
468const TV_MAX_SECONDS: i64 = isize::MAX as i64;
469
470const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
471
472impl AsRef<timeval> for TimeVal {
473    fn as_ref(&self) -> &timeval {
474        &self.0
475    }
476}
477
478impl AsMut<timeval> for TimeVal {
479    fn as_mut(&mut self) -> &mut timeval {
480        &mut self.0
481    }
482}
483
484impl 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
496impl PartialOrd for TimeVal {
497    fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
498        Some(self.cmp(other))
499    }
500}
501
502impl 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
587impl 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
615impl ops::Neg for TimeVal {
616    type Output = TimeVal;
617
618    fn neg(self) -> TimeVal {
619        TimeVal::microseconds(-self.num_microseconds())
620    }
621}
622
623impl 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
631impl 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
639impl ops::Mul<i32> for TimeVal {
640    type Output = TimeVal;
641
642    fn mul(self, rhs: i32) -> TimeVal {
643        let usec = self
644            .num_microseconds()
645            .checked_mul(i64::from(rhs))
646            .expect("TimeVal multiply out of bounds");
647
648        TimeVal::microseconds(usec)
649    }
650}
651
652impl ops::Div<i32> for TimeVal {
653    type Output = TimeVal;
654
655    fn div(self, rhs: i32) -> TimeVal {
656        let usec = self.num_microseconds() / i64::from(rhs);
657        TimeVal::microseconds(usec)
658    }
659}
660
661impl 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
689impl From<timeval> for TimeVal {
690    fn from(tv: timeval) -> Self {
691        TimeVal(tv)
692    }
693}
694
695#[inline]
696fn 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]
701fn div_floor_64(this: i64, other: i64) -> i64 {
702    match div_rem_64(this, other) {
703        (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
704        (d, _) => d,
705    }
706}
707
708#[inline]
709fn mod_floor_64(this: i64, other: i64) -> i64 {
710    match this % other {
711        r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
712        r => r,
713    }
714}
715
716#[inline]
717fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
718    (this / other, this % other)
719}