shadow_shim_helper_rs/
simulation_time.rs

1/*!
2Values for working with a simulated duration. Use `EmulatedTime` to represent an instant in time.
3
4In Rust, use `EmulatedTime` to represent an instant in time, or
5`SimulationTime` to represent a time interval. `SimulationTime` is meant to
6replace [`SimulationTime`] from the C APIs.
7
8This module contains some identically-named constants defined as C macros in
9`main/core/definitions.h`.
10*/
11
12use core::time::Duration;
13
14use vasi::VirtualAddressSpaceIndependent;
15
16use super::emulated_time;
17
18#[derive(
19    Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord, Hash, VirtualAddressSpaceIndependent,
20)]
21#[repr(C)]
22pub struct SimulationTime(CSimulationTime);
23
24/// The same as the type alias in the 'main/cshadow.rs' bindings.
25pub type CSimulationTime = u64;
26
27impl SimulationTime {
28    /// Maximum value. Currently equivalent to SIMTIME_MAX to avoid surprises
29    /// when interoperating with C, but could use Duration::MAX when the C types
30    /// go away.
31    pub const MAX: SimulationTime = SimulationTime(SIMTIME_MAX);
32    pub const ZERO: SimulationTime = SimulationTime(0);
33    pub const SECOND: SimulationTime = SimulationTime(SIMTIME_ONE_SECOND);
34    pub const MILLISECOND: SimulationTime = SimulationTime(SIMTIME_ONE_MILLISECOND);
35    pub const MICROSECOND: SimulationTime = SimulationTime(SIMTIME_ONE_MICROSECOND);
36    pub const NANOSECOND: SimulationTime = SimulationTime(SIMTIME_ONE_NANOSECOND);
37
38    pub fn from_c_simtime(val: CSimulationTime) -> Option<Self> {
39        if val == SIMTIME_INVALID {
40            return None;
41        }
42
43        if val > SIMTIME_MAX {
44            return None;
45        }
46
47        Some(Self(val / SIMTIME_ONE_NANOSECOND))
48    }
49
50    pub fn to_c_simtime(val: Option<Self>) -> CSimulationTime {
51        val.map_or(SIMTIME_INVALID, |val| val.0)
52    }
53
54    /// Convert a [`Duration`] to a [`SimulationTime`]. This function exists as a `const`
55    /// alternative to `SimulationTime::try_from(duration)`. May panic if the duration is too large.
56    pub const fn from_duration(val: core::time::Duration) -> Self {
57        if SIMTIME_ONE_NANOSECOND != 1 {
58            unreachable!();
59        }
60
61        let val = val.as_nanos();
62        if val > SIMTIME_MAX as u128 {
63            panic!("Duration is larger than SIMTIME_MAX");
64        }
65
66        Self(val as u64)
67    }
68
69    pub fn is_zero(&self) -> bool {
70        self.0 == 0
71    }
72
73    pub fn is_positive(&self) -> bool {
74        self.0 > 0
75    }
76
77    pub fn as_secs(&self) -> u64 {
78        self.0 / SIMTIME_ONE_SECOND
79    }
80
81    pub fn as_millis(&self) -> u64 {
82        self.0 / SIMTIME_ONE_MILLISECOND
83    }
84
85    pub fn as_micros(&self) -> u64 {
86        self.0 / SIMTIME_ONE_MICROSECOND
87    }
88
89    pub fn as_nanos(&self) -> u128 {
90        (self.0 / SIMTIME_ONE_NANOSECOND).into()
91    }
92
93    pub fn as_nanos_f64(&self) -> f64 {
94        self.as_nanos() as f64
95    }
96
97    pub fn checked_add(self, other: Self) -> Option<Self> {
98        match self.0.checked_add(other.0) {
99            Some(sum) => SimulationTime::from_c_simtime(sum),
100            None => None,
101        }
102    }
103
104    pub fn checked_sub(self, other: Self) -> Option<Self> {
105        match self.0.checked_sub(other.0) {
106            Some(difference) => SimulationTime::from_c_simtime(difference),
107            None => None,
108        }
109    }
110
111    pub fn checked_mul(self, other: u64) -> Option<Self> {
112        match self.0.checked_mul(other) {
113            Some(product) => SimulationTime::from_c_simtime(product),
114            None => None,
115        }
116    }
117
118    pub fn checked_div(self, other: u64) -> Option<Self> {
119        match self.0.checked_div(other) {
120            Some(quotient) => SimulationTime::from_c_simtime(quotient),
121            None => None,
122        }
123    }
124
125    pub fn checked_rem(self, other: Self) -> Option<Self> {
126        match self.0.checked_rem(other.0) {
127            Some(rem) => SimulationTime::from_c_simtime(rem),
128            None => None,
129        }
130    }
131
132    pub fn saturating_add(self, other: Self) -> Self {
133        let sum = self.0.checked_add(other.0).unwrap_or(SIMTIME_MAX);
134        SimulationTime::from_c_simtime(sum).unwrap()
135    }
136
137    pub fn saturating_sub(self, other: Self) -> Self {
138        let difference = self.0.checked_sub(other.0).unwrap_or(SIMTIME_MIN);
139        SimulationTime::from_c_simtime(difference).unwrap()
140    }
141
142    pub fn saturating_mul(self, other: u64) -> Self {
143        let product = self.0.checked_mul(other).unwrap_or(SIMTIME_MAX);
144        SimulationTime::from_c_simtime(product).unwrap()
145    }
146
147    pub fn try_from_secs(s: u64) -> Option<Self> {
148        Self::SECOND.checked_mul(s)
149    }
150
151    pub fn from_secs(s: u64) -> Self {
152        Self::try_from_secs(s).unwrap()
153    }
154
155    pub fn try_from_millis(s: u64) -> Option<Self> {
156        Self::MILLISECOND.checked_mul(s)
157    }
158
159    pub fn from_millis(s: u64) -> Self {
160        Self::try_from_millis(s).unwrap()
161    }
162
163    pub fn try_from_micros(s: u64) -> Option<Self> {
164        Self::MICROSECOND.checked_mul(s)
165    }
166
167    pub fn from_micros(s: u64) -> Self {
168        Self::try_from_micros(s).unwrap()
169    }
170
171    pub fn try_from_nanos(s: u64) -> Option<Self> {
172        Self::NANOSECOND.checked_mul(s)
173    }
174
175    pub fn from_nanos(s: u64) -> Self {
176        Self::try_from_nanos(s).unwrap()
177    }
178
179    pub fn subsec_millis(&self) -> u32 {
180        (self.as_millis() % 1_000).try_into().unwrap()
181    }
182
183    pub fn subsec_micros(&self) -> u32 {
184        (self.as_micros() % 1_000_000).try_into().unwrap()
185    }
186
187    pub fn subsec_nanos(&self) -> u32 {
188        (self.as_nanos() % 1_000_000_000).try_into().unwrap()
189    }
190}
191
192impl core::ops::Add<SimulationTime> for SimulationTime {
193    type Output = SimulationTime;
194
195    fn add(self, other: Self) -> Self::Output {
196        self.checked_add(other).unwrap()
197    }
198}
199
200impl core::ops::AddAssign<SimulationTime> for SimulationTime {
201    fn add_assign(&mut self, rhs: SimulationTime) {
202        *self = *self + rhs;
203    }
204}
205
206impl core::ops::Sub<SimulationTime> for SimulationTime {
207    type Output = SimulationTime;
208
209    fn sub(self, other: Self) -> Self::Output {
210        self.checked_sub(other).unwrap()
211    }
212}
213
214impl core::ops::SubAssign<SimulationTime> for SimulationTime {
215    fn sub_assign(&mut self, rhs: SimulationTime) {
216        *self = *self - rhs;
217    }
218}
219
220impl core::ops::Mul<u32> for SimulationTime {
221    type Output = SimulationTime;
222
223    fn mul(self, other: u32) -> Self::Output {
224        self.checked_mul(other.into()).unwrap()
225    }
226}
227
228impl core::ops::MulAssign<u32> for SimulationTime {
229    fn mul_assign(&mut self, rhs: u32) {
230        *self = self.checked_mul(rhs.into()).unwrap();
231    }
232}
233
234impl core::ops::Div<u32> for SimulationTime {
235    type Output = SimulationTime;
236
237    fn div(self, other: u32) -> Self::Output {
238        self.checked_div(other.into()).unwrap()
239    }
240}
241
242impl core::ops::DivAssign<u32> for SimulationTime {
243    fn div_assign(&mut self, rhs: u32) {
244        *self = self.checked_div(rhs.into()).unwrap();
245    }
246}
247
248impl core::ops::Rem<SimulationTime> for SimulationTime {
249    type Output = SimulationTime;
250
251    fn rem(self, other: SimulationTime) -> Self::Output {
252        self.checked_rem(other).unwrap()
253    }
254}
255
256impl core::convert::TryFrom<core::time::Duration> for SimulationTime {
257    type Error = ();
258
259    fn try_from(val: core::time::Duration) -> Result<Self, Self::Error> {
260        debug_assert_eq!(SIMTIME_ONE_NANOSECOND, 1);
261        let val = val.as_nanos();
262        if val > SIMTIME_MAX.into() {
263            Err(())
264        } else {
265            Ok(Self(val.try_into().unwrap()))
266        }
267    }
268}
269
270impl core::convert::From<SimulationTime> for core::time::Duration {
271    fn from(val: SimulationTime) -> core::time::Duration {
272        debug_assert_eq!(SIMTIME_ONE_NANOSECOND, 1);
273        Duration::from_nanos(val.0)
274    }
275}
276
277impl core::convert::From<SimulationTime> for CSimulationTime {
278    fn from(val: SimulationTime) -> CSimulationTime {
279        val.0
280    }
281}
282
283impl core::convert::TryFrom<libc::timespec> for SimulationTime {
284    type Error = ();
285
286    fn try_from(value: libc::timespec) -> Result<Self, Self::Error> {
287        if value.tv_sec < 0 || value.tv_nsec < 0 || value.tv_nsec > 999_999_999 {
288            return Err(());
289        }
290        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
291        let nanos = Duration::from_nanos(value.tv_nsec.try_into().unwrap());
292        Self::try_from(secs + nanos)
293    }
294}
295
296impl core::convert::TryFrom<SimulationTime> for libc::timespec {
297    type Error = ();
298
299    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
300        let value = Duration::from(value);
301        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
302        let tv_nsec = value.subsec_nanos().into();
303        Ok(libc::timespec { tv_sec, tv_nsec })
304    }
305}
306
307impl core::convert::TryFrom<linux_api::time::timespec> for SimulationTime {
308    type Error = ();
309
310    fn try_from(value: linux_api::time::timespec) -> Result<Self, Self::Error> {
311        if value.tv_sec < 0 || value.tv_nsec < 0 || value.tv_nsec > 999_999_999 {
312            return Err(());
313        }
314        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
315        let nanos = Duration::from_nanos(value.tv_nsec.try_into().unwrap());
316        Self::try_from(secs + nanos)
317    }
318}
319
320impl core::convert::TryFrom<SimulationTime> for linux_api::time::timespec {
321    type Error = ();
322
323    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
324        let value = Duration::from(value);
325        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
326        let tv_nsec = value.subsec_nanos().into();
327        Ok(linux_api::time::timespec { tv_sec, tv_nsec })
328    }
329}
330
331impl core::convert::TryFrom<linux_api::time::kernel_timespec> for SimulationTime {
332    type Error = ();
333
334    fn try_from(value: linux_api::time::kernel_timespec) -> Result<Self, Self::Error> {
335        if value.tv_sec < 0 || value.tv_nsec < 0 || value.tv_nsec > 999_999_999 {
336            return Err(());
337        }
338        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
339        let nanos = Duration::from_nanos(value.tv_nsec.try_into().unwrap());
340        Self::try_from(secs + nanos)
341    }
342}
343
344impl core::convert::TryFrom<SimulationTime> for linux_api::time::kernel_timespec {
345    type Error = ();
346
347    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
348        let value = Duration::from(value);
349        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
350        let tv_nsec = value.subsec_nanos().into();
351        Ok(linux_api::time::kernel_timespec { tv_sec, tv_nsec })
352    }
353}
354
355impl core::convert::TryFrom<libc::timeval> for SimulationTime {
356    type Error = ();
357
358    fn try_from(value: libc::timeval) -> Result<Self, Self::Error> {
359        if value.tv_sec < 0 || value.tv_usec < 0 || value.tv_usec > 999_999 {
360            return Err(());
361        }
362        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
363        let micros = Duration::from_micros(value.tv_usec.try_into().unwrap());
364        Self::try_from(secs + micros)
365    }
366}
367
368impl core::convert::TryFrom<SimulationTime> for libc::timeval {
369    type Error = ();
370
371    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
372        let value = Duration::from(value);
373        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
374        let tv_usec = value.subsec_micros().into();
375        Ok(libc::timeval { tv_sec, tv_usec })
376    }
377}
378
379impl core::convert::TryFrom<linux_api::time::timeval> for SimulationTime {
380    type Error = ();
381
382    fn try_from(value: linux_api::time::timeval) -> Result<Self, Self::Error> {
383        if value.tv_sec < 0 || value.tv_usec < 0 || value.tv_usec > 999_999 {
384            return Err(());
385        }
386        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
387        let micros = Duration::from_micros(value.tv_usec.try_into().unwrap());
388        Self::try_from(secs + micros)
389    }
390}
391
392impl core::convert::TryFrom<linux_api::time::kernel_old_timeval> for SimulationTime {
393    type Error = ();
394
395    fn try_from(value: linux_api::time::kernel_old_timeval) -> Result<Self, Self::Error> {
396        if value.tv_sec < 0 || value.tv_usec < 0 || value.tv_usec > 999_999 {
397            return Err(());
398        }
399        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
400        let micros = Duration::from_micros(value.tv_usec.try_into().unwrap());
401        Self::try_from(secs + micros)
402    }
403}
404
405impl core::convert::TryFrom<SimulationTime> for linux_api::time::timeval {
406    type Error = ();
407
408    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
409        let value = Duration::from(value);
410        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
411        let tv_usec = value.subsec_micros().into();
412        Ok(linux_api::time::timeval { tv_sec, tv_usec })
413    }
414}
415
416impl core::convert::TryFrom<SimulationTime> for linux_api::time::kernel_old_timeval {
417    type Error = ();
418
419    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
420        let value = Duration::from(value);
421        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
422        let tv_usec = value.subsec_micros().into();
423        Ok(linux_api::time::kernel_old_timeval { tv_sec, tv_usec })
424    }
425}
426
427impl tcp::util::time::Duration for SimulationTime {
428    const MAX: Self = Self::MAX;
429    const NANOSECOND: Self = Self::NANOSECOND;
430    const MICROSECOND: Self = Self::MICROSECOND;
431    const MILLISECOND: Self = Self::MILLISECOND;
432    const SECOND: Self = Self::SECOND;
433    const ZERO: Self = Self::ZERO;
434
435    #[inline]
436    fn as_micros(&self) -> u128 {
437        self.as_micros().into()
438    }
439
440    #[inline]
441    fn as_millis(&self) -> u128 {
442        self.as_millis().into()
443    }
444
445    #[inline]
446    fn as_nanos(&self) -> u128 {
447        self.as_nanos()
448    }
449
450    #[inline]
451    fn as_secs(&self) -> u64 {
452        self.as_secs()
453    }
454
455    #[inline]
456    fn checked_add(self, rhs: Self) -> Option<Self> {
457        self.checked_add(rhs)
458    }
459
460    #[inline]
461    fn checked_div(self, rhs: u32) -> Option<Self> {
462        self.checked_div(rhs.into())
463    }
464
465    #[inline]
466    fn checked_mul(self, rhs: u32) -> Option<Self> {
467        self.checked_mul(rhs.into())
468    }
469
470    #[inline]
471    fn checked_sub(self, rhs: Self) -> Option<Self> {
472        self.checked_sub(rhs)
473    }
474
475    #[inline]
476    fn from_micros(micros: u64) -> Self {
477        Self::from_micros(micros)
478    }
479
480    #[inline]
481    fn from_millis(millis: u64) -> Self {
482        Self::from_millis(millis)
483    }
484
485    #[inline]
486    fn from_nanos(nanos: u64) -> Self {
487        Self::from_nanos(nanos)
488    }
489
490    #[inline]
491    fn from_secs(secs: u64) -> Self {
492        Self::from_secs(secs)
493    }
494
495    #[inline]
496    fn is_zero(&self) -> bool {
497        self.is_zero()
498    }
499
500    #[inline]
501    fn saturating_add(self, rhs: Self) -> Self {
502        self.saturating_add(rhs)
503    }
504
505    #[inline]
506    fn saturating_mul(self, rhs: u32) -> Self {
507        self.saturating_mul(rhs.into())
508    }
509
510    #[inline]
511    fn saturating_sub(self, rhs: Self) -> Self {
512        self.saturating_sub(rhs)
513    }
514
515    #[inline]
516    fn subsec_micros(&self) -> u32 {
517        self.subsec_micros()
518    }
519
520    #[inline]
521    fn subsec_millis(&self) -> u32 {
522        self.subsec_millis()
523    }
524
525    #[inline]
526    fn subsec_nanos(&self) -> u32 {
527        self.subsec_nanos()
528    }
529}
530
531/// Invalid simulation time.
532pub const SIMTIME_INVALID: CSimulationTime = u64::MAX;
533
534/// Maximum and minimum valid values.
535//
536// cbindgen refuses to do the arithmetic here, so we we pre-compute,
537// and validate in the static assertion below.
538pub const SIMTIME_MAX: CSimulationTime = 17500059273709551614u64;
539const _: () =
540    assert!(SIMTIME_MAX == emulated_time::EMUTIME_MAX - emulated_time::EMUTIME_SIMULATION_START);
541
542pub const SIMTIME_MIN: CSimulationTime = 0u64;
543
544/// Represents one nanosecond in simulation time.
545pub const SIMTIME_ONE_NANOSECOND: CSimulationTime = 1u64;
546
547/// Represents one microsecond in simulation time.
548pub const SIMTIME_ONE_MICROSECOND: CSimulationTime = 1000u64;
549
550/// Represents one millisecond in simulation time.
551pub const SIMTIME_ONE_MILLISECOND: CSimulationTime = 1000000u64;
552
553/// Represents one second in simulation time.
554pub const SIMTIME_ONE_SECOND: CSimulationTime = 1000000000u64;
555
556/// Represents one minute in simulation time.
557pub const SIMTIME_ONE_MINUTE: CSimulationTime = 60000000000u64;
558
559/// Represents one hour in simulation time.
560pub const SIMTIME_ONE_HOUR: CSimulationTime = 3600000000000u64;
561
562pub mod export {
563    use super::*;
564    use crate::notnull::*;
565
566    #[unsafe(no_mangle)]
567    pub extern "C-unwind" fn simtime_from_timeval(val: libc::timeval) -> CSimulationTime {
568        SimulationTime::to_c_simtime(SimulationTime::try_from(val).ok())
569    }
570
571    #[unsafe(no_mangle)]
572    pub extern "C-unwind" fn simtime_from_timespec(val: libc::timespec) -> CSimulationTime {
573        SimulationTime::to_c_simtime(SimulationTime::try_from(val).ok())
574    }
575
576    /// # Safety
577    ///
578    /// Pointer args must be safe to write to.
579    #[must_use]
580    #[unsafe(no_mangle)]
581    pub unsafe extern "C-unwind" fn simtime_to_timeval(
582        val: CSimulationTime,
583        out: *mut libc::timeval,
584    ) -> bool {
585        let Some(simtime) = SimulationTime::from_c_simtime(val) else {
586            return false;
587        };
588        let Ok(tv) = libc::timeval::try_from(simtime) else {
589            return false;
590        };
591        unsafe { core::ptr::write(notnull_mut(out), tv) };
592        true
593    }
594
595    /// # Safety
596    ///
597    /// Pointer args must be safe to write to.
598    #[must_use]
599    #[unsafe(no_mangle)]
600    pub unsafe extern "C-unwind" fn simtime_to_timespec(
601        val: CSimulationTime,
602        out: *mut libc::timespec,
603    ) -> bool {
604        let Some(simtime) = SimulationTime::from_c_simtime(val) else {
605            return false;
606        };
607        let Ok(ts) = libc::timespec::try_from(simtime) else {
608            return false;
609        };
610        unsafe { core::ptr::write(out, ts) };
611        true
612    }
613}
614
615#[cfg(test)]
616mod tests {
617    use super::*;
618
619    #[test]
620    fn test_from_csimtime() {
621        let sim_time = 5 * SIMTIME_ONE_MINUTE + 7 * SIMTIME_ONE_MILLISECOND;
622        let rust_time = SimulationTime::from_c_simtime(sim_time).unwrap();
623
624        assert_eq!(Duration::from(rust_time).as_secs(), 5 * 60);
625        assert_eq!(Duration::from(rust_time).as_millis(), 5 * 60 * 1_000 + 7);
626
627        assert_eq!(
628            SimulationTime::from_c_simtime(SIMTIME_MAX).unwrap(),
629            SimulationTime::try_from(Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND))
630                .unwrap()
631        );
632        assert_eq!(SimulationTime::from_c_simtime(SIMTIME_MAX + 1), None);
633    }
634
635    #[test]
636    fn test_to_csimtime() {
637        let rust_time = SimulationTime::from_secs(5 * 60) + SimulationTime::from_millis(7);
638        let sim_time = 5 * SIMTIME_ONE_MINUTE + 7 * SIMTIME_ONE_MILLISECOND;
639
640        assert_eq!(SimulationTime::to_c_simtime(Some(rust_time)), sim_time);
641        assert_eq!(SimulationTime::to_c_simtime(None), SIMTIME_INVALID);
642        assert_eq!(
643            SimulationTime::to_c_simtime(Some(SimulationTime::MAX)),
644            SIMTIME_MAX
645        );
646    }
647
648    #[test]
649    fn test_from_timeval() {
650        use libc::timeval;
651
652        assert_eq!(
653            SimulationTime::try_from(timeval {
654                tv_sec: 0,
655                tv_usec: 0
656            }),
657            Ok(SimulationTime::ZERO)
658        );
659        assert_eq!(
660            SimulationTime::try_from(timeval {
661                tv_sec: 1,
662                tv_usec: 2
663            }),
664            Ok(
665                SimulationTime::try_from(Duration::from_secs(1) + Duration::from_micros(2))
666                    .unwrap()
667            )
668        );
669        assert_eq!(
670            SimulationTime::try_from(timeval {
671                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
672                tv_usec: SimulationTime::MAX.subsec_micros().into(),
673            }),
674            Ok(SimulationTime::from_micros(SimulationTime::MAX.as_micros()))
675        );
676
677        // Out of range
678        assert_eq!(
679            SimulationTime::try_from(timeval {
680                tv_sec: libc::time_t::MAX,
681                tv_usec: 999_999
682            }),
683            Err(())
684        );
685
686        assert_eq!(
687            SimulationTime::try_from(timeval {
688                tv_sec: 0,
689                tv_usec: 1_000_000
690            }),
691            Err(())
692        );
693        assert_eq!(
694            SimulationTime::try_from(timeval {
695                tv_sec: 0,
696                tv_usec: -1
697            }),
698            Err(())
699        );
700        assert_eq!(
701            SimulationTime::try_from(timeval {
702                tv_sec: -1,
703                tv_usec: 0
704            }),
705            Err(())
706        );
707        assert_eq!(
708            SimulationTime::try_from(timeval {
709                tv_sec: -1,
710                tv_usec: -1
711            }),
712            Err(())
713        );
714    }
715
716    #[test]
717    fn test_c_from_timeval() {
718        use export::simtime_from_timeval;
719        use libc::timeval;
720
721        assert_eq!(
722            simtime_from_timeval(timeval {
723                tv_sec: 0,
724                tv_usec: 0,
725            }),
726            0
727        );
728        assert_eq!(
729            simtime_from_timeval(timeval {
730                tv_sec: 1,
731                tv_usec: 2,
732            }),
733            SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_MICROSECOND
734        );
735
736        // Out of range
737        assert_eq!(
738            simtime_from_timeval(timeval {
739                tv_sec: libc::time_t::MAX,
740                tv_usec: 999_999,
741            }),
742            SIMTIME_INVALID
743        );
744
745        assert_eq!(
746            simtime_from_timeval(timeval {
747                tv_sec: 0,
748                tv_usec: 1_000_000,
749            }),
750            SIMTIME_INVALID
751        );
752        assert_eq!(
753            simtime_from_timeval(timeval {
754                tv_sec: 0,
755                tv_usec: -1,
756            }),
757            SIMTIME_INVALID
758        );
759        assert_eq!(
760            simtime_from_timeval(timeval {
761                tv_sec: -1,
762                tv_usec: 0,
763            }),
764            SIMTIME_INVALID
765        );
766        assert_eq!(
767            simtime_from_timeval(timeval {
768                tv_sec: -1,
769                tv_usec: -1,
770            }),
771            SIMTIME_INVALID
772        );
773    }
774
775    #[test]
776    fn test_to_timeval() {
777        use libc::timeval;
778
779        assert_eq!(
780            timeval::try_from(SimulationTime::ZERO),
781            Ok(timeval {
782                tv_sec: 0,
783                tv_usec: 0
784            })
785        );
786        assert_eq!(
787            timeval::try_from(
788                SimulationTime::try_from(Duration::from_secs(1) + Duration::from_micros(2))
789                    .unwrap()
790            ),
791            Ok(timeval {
792                tv_sec: 1,
793                tv_usec: 2
794            })
795        );
796        assert_eq!(
797            timeval::try_from(SimulationTime::MAX),
798            Ok(timeval {
799                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
800                tv_usec: SimulationTime::MAX.subsec_micros().into(),
801            })
802        );
803    }
804
805    #[test]
806    fn test_c_to_timeval() {
807        use export::simtime_to_timeval;
808        use libc::timeval;
809
810        let mut tv = unsafe { std::mem::zeroed() };
811
812        assert!(unsafe { simtime_to_timeval(0, &mut tv) });
813        assert_eq!(
814            tv,
815            timeval {
816                tv_sec: 0,
817                tv_usec: 0
818            }
819        );
820
821        assert!(unsafe {
822            simtime_to_timeval(SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_MICROSECOND, &mut tv)
823        });
824        assert_eq!(
825            tv,
826            timeval {
827                tv_sec: 1,
828                tv_usec: 2
829            }
830        );
831
832        {
833            assert!(unsafe { simtime_to_timeval(SIMTIME_MAX, &mut tv) });
834            let d = Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND);
835            assert_eq!(
836                tv,
837                timeval {
838                    tv_sec: d.as_secs().try_into().unwrap(),
839                    tv_usec: d.subsec_micros().into(),
840                }
841            );
842        }
843    }
844
845    #[test]
846    fn test_from_timespec() {
847        use libc::timespec;
848
849        assert_eq!(
850            SimulationTime::try_from(timespec {
851                tv_sec: 0,
852                tv_nsec: 0
853            }),
854            Ok(SimulationTime::ZERO)
855        );
856        assert_eq!(
857            SimulationTime::try_from(timespec {
858                tv_sec: 1,
859                tv_nsec: 2
860            }),
861            Ok(SimulationTime::try_from(Duration::from_secs(1) + Duration::from_nanos(2)).unwrap())
862        );
863        assert_eq!(
864            SimulationTime::try_from(timespec {
865                tv_sec: (SIMTIME_MAX / SIMTIME_ONE_SECOND).try_into().unwrap(),
866                tv_nsec: 0,
867            }),
868            Ok(
869                SimulationTime::try_from(Duration::from_secs(SIMTIME_MAX / SIMTIME_ONE_SECOND))
870                    .unwrap()
871            )
872        );
873
874        // The C SimulatedTime type is too small to represent this value.
875        // The Rust SimulationTime *could* represent it if we widen it.
876        assert_eq!(
877            SimulationTime::try_from(timespec {
878                tv_sec: libc::time_t::MAX,
879                tv_nsec: 999_999_999
880            }),
881            Err(())
882        );
883
884        assert_eq!(
885            SimulationTime::try_from(timespec {
886                tv_sec: 0,
887                tv_nsec: 1_000_000_000
888            }),
889            Err(())
890        );
891        assert_eq!(
892            SimulationTime::try_from(timespec {
893                tv_sec: 0,
894                tv_nsec: -1
895            }),
896            Err(())
897        );
898        assert_eq!(
899            SimulationTime::try_from(timespec {
900                tv_sec: -1,
901                tv_nsec: 0
902            }),
903            Err(())
904        );
905        assert_eq!(
906            SimulationTime::try_from(timespec {
907                tv_sec: -1,
908                tv_nsec: -1
909            }),
910            Err(())
911        );
912    }
913
914    #[test]
915    fn test_c_from_timespec() {
916        use export::simtime_from_timespec;
917        use libc::timespec;
918
919        assert_eq!(
920            simtime_from_timespec(timespec {
921                tv_sec: 0,
922                tv_nsec: 0,
923            }),
924            0
925        );
926        assert_eq!(
927            simtime_from_timespec(timespec {
928                tv_sec: 1,
929                tv_nsec: 2,
930            }),
931            SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_NANOSECOND
932        );
933
934        // The C SimulatedTime type is too small to represent this value.
935        assert_eq!(
936            simtime_from_timespec(timespec {
937                tv_sec: libc::time_t::MAX,
938                tv_nsec: 999_999_999,
939            }),
940            SIMTIME_INVALID
941        );
942
943        assert_eq!(
944            simtime_from_timespec(timespec {
945                tv_sec: 0,
946                tv_nsec: 1_000_000_000,
947            }),
948            SIMTIME_INVALID
949        );
950        assert_eq!(
951            simtime_from_timespec(timespec {
952                tv_sec: 0,
953                tv_nsec: -1,
954            }),
955            SIMTIME_INVALID
956        );
957        assert_eq!(
958            simtime_from_timespec(timespec {
959                tv_sec: -1,
960                tv_nsec: 0,
961            }),
962            SIMTIME_INVALID
963        );
964        assert_eq!(
965            simtime_from_timespec(timespec {
966                tv_sec: -1,
967                tv_nsec: -1,
968            }),
969            SIMTIME_INVALID
970        );
971    }
972
973    #[test]
974    fn test_to_timespec() {
975        use libc::timespec;
976
977        assert_eq!(
978            timespec::try_from(SimulationTime::ZERO),
979            Ok(timespec {
980                tv_sec: 0,
981                tv_nsec: 0
982            })
983        );
984        assert_eq!(
985            timespec::try_from(SimulationTime::from_secs(1) + SimulationTime::from_nanos(2)),
986            Ok(timespec {
987                tv_sec: 1,
988                tv_nsec: 2
989            })
990        );
991
992        assert_eq!(
993            timespec::try_from(SimulationTime::MAX),
994            Ok(timespec {
995                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
996                tv_nsec: SimulationTime::MAX.subsec_nanos().into(),
997            })
998        );
999    }
1000
1001    #[test]
1002    fn test_c_to_timespec() {
1003        use export::simtime_to_timespec;
1004        use libc::timespec;
1005
1006        let mut ts = unsafe { std::mem::zeroed() };
1007
1008        assert!(unsafe { simtime_to_timespec(0, &mut ts) });
1009        assert_eq!(
1010            ts,
1011            timespec {
1012                tv_sec: 0,
1013                tv_nsec: 0
1014            }
1015        );
1016
1017        assert!(unsafe {
1018            simtime_to_timespec(SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_NANOSECOND, &mut ts)
1019        });
1020        assert_eq!(
1021            ts,
1022            timespec {
1023                tv_sec: 1,
1024                tv_nsec: 2
1025            }
1026        );
1027
1028        {
1029            assert!(unsafe { simtime_to_timespec(SIMTIME_MAX, &mut ts) });
1030            let d = Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND);
1031            assert_eq!(
1032                ts,
1033                timespec {
1034                    tv_sec: d.as_secs().try_into().unwrap(),
1035                    tv_nsec: d.subsec_nanos().into(),
1036                }
1037            );
1038        }
1039    }
1040}