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 std::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: std::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 std::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 std::ops::AddAssign<SimulationTime> for SimulationTime {
201    fn add_assign(&mut self, rhs: SimulationTime) {
202        *self = *self + rhs;
203    }
204}
205
206impl std::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 std::ops::SubAssign<SimulationTime> for SimulationTime {
215    fn sub_assign(&mut self, rhs: SimulationTime) {
216        *self = *self - rhs;
217    }
218}
219
220impl std::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 std::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 std::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 std::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 std::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 std::convert::TryFrom<std::time::Duration> for SimulationTime {
257    type Error = ();
258
259    fn try_from(val: std::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 std::convert::From<SimulationTime> for std::time::Duration {
271    fn from(val: SimulationTime) -> std::time::Duration {
272        debug_assert_eq!(SIMTIME_ONE_NANOSECOND, 1);
273        Duration::from_nanos(val.0)
274    }
275}
276
277impl std::convert::From<SimulationTime> for CSimulationTime {
278    fn from(val: SimulationTime) -> CSimulationTime {
279        val.0
280    }
281}
282
283impl std::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 std::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 std::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 std::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 std::convert::TryFrom<libc::timeval> for SimulationTime {
332    type Error = ();
333
334    fn try_from(value: libc::timeval) -> Result<Self, Self::Error> {
335        if value.tv_sec < 0 || value.tv_usec < 0 || value.tv_usec > 999_999 {
336            return Err(());
337        }
338        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
339        let micros = Duration::from_micros(value.tv_usec.try_into().unwrap());
340        Self::try_from(secs + micros)
341    }
342}
343
344impl std::convert::TryFrom<SimulationTime> for libc::timeval {
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_usec = value.subsec_micros().into();
351        Ok(libc::timeval { tv_sec, tv_usec })
352    }
353}
354
355impl std::convert::TryFrom<linux_api::time::timeval> for SimulationTime {
356    type Error = ();
357
358    fn try_from(value: linux_api::time::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 std::convert::TryFrom<linux_api::time::kernel_old_timeval> for SimulationTime {
369    type Error = ();
370
371    fn try_from(value: linux_api::time::kernel_old_timeval) -> Result<Self, Self::Error> {
372        if value.tv_sec < 0 || value.tv_usec < 0 || value.tv_usec > 999_999 {
373            return Err(());
374        }
375        let secs = Duration::from_secs(value.tv_sec.try_into().unwrap());
376        let micros = Duration::from_micros(value.tv_usec.try_into().unwrap());
377        Self::try_from(secs + micros)
378    }
379}
380
381impl std::convert::TryFrom<SimulationTime> for linux_api::time::timeval {
382    type Error = ();
383
384    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
385        let value = Duration::from(value);
386        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
387        let tv_usec = value.subsec_micros().into();
388        Ok(linux_api::time::timeval { tv_sec, tv_usec })
389    }
390}
391
392impl std::convert::TryFrom<SimulationTime> for linux_api::time::kernel_old_timeval {
393    type Error = ();
394
395    fn try_from(value: SimulationTime) -> Result<Self, Self::Error> {
396        let value = Duration::from(value);
397        let tv_sec = value.as_secs().try_into().map_err(|_| ())?;
398        let tv_usec = value.subsec_micros().into();
399        Ok(linux_api::time::kernel_old_timeval { tv_sec, tv_usec })
400    }
401}
402
403impl tcp::util::time::Duration for SimulationTime {
404    const MAX: Self = Self::MAX;
405    const NANOSECOND: Self = Self::NANOSECOND;
406    const MICROSECOND: Self = Self::MICROSECOND;
407    const MILLISECOND: Self = Self::MILLISECOND;
408    const SECOND: Self = Self::SECOND;
409    const ZERO: Self = Self::ZERO;
410
411    #[inline]
412    fn as_micros(&self) -> u128 {
413        self.as_micros().into()
414    }
415
416    #[inline]
417    fn as_millis(&self) -> u128 {
418        self.as_millis().into()
419    }
420
421    #[inline]
422    fn as_nanos(&self) -> u128 {
423        self.as_nanos()
424    }
425
426    #[inline]
427    fn as_secs(&self) -> u64 {
428        self.as_secs()
429    }
430
431    #[inline]
432    fn checked_add(self, rhs: Self) -> Option<Self> {
433        self.checked_add(rhs)
434    }
435
436    #[inline]
437    fn checked_div(self, rhs: u32) -> Option<Self> {
438        self.checked_div(rhs.into())
439    }
440
441    #[inline]
442    fn checked_mul(self, rhs: u32) -> Option<Self> {
443        self.checked_mul(rhs.into())
444    }
445
446    #[inline]
447    fn checked_sub(self, rhs: Self) -> Option<Self> {
448        self.checked_sub(rhs)
449    }
450
451    #[inline]
452    fn from_micros(micros: u64) -> Self {
453        Self::from_micros(micros)
454    }
455
456    #[inline]
457    fn from_millis(millis: u64) -> Self {
458        Self::from_millis(millis)
459    }
460
461    #[inline]
462    fn from_nanos(nanos: u64) -> Self {
463        Self::from_nanos(nanos)
464    }
465
466    #[inline]
467    fn from_secs(secs: u64) -> Self {
468        Self::from_secs(secs)
469    }
470
471    #[inline]
472    fn is_zero(&self) -> bool {
473        self.is_zero()
474    }
475
476    #[inline]
477    fn saturating_add(self, rhs: Self) -> Self {
478        self.saturating_add(rhs)
479    }
480
481    #[inline]
482    fn saturating_mul(self, rhs: u32) -> Self {
483        self.saturating_mul(rhs.into())
484    }
485
486    #[inline]
487    fn saturating_sub(self, rhs: Self) -> Self {
488        self.saturating_sub(rhs)
489    }
490
491    #[inline]
492    fn subsec_micros(&self) -> u32 {
493        self.subsec_micros()
494    }
495
496    #[inline]
497    fn subsec_millis(&self) -> u32 {
498        self.subsec_millis()
499    }
500
501    #[inline]
502    fn subsec_nanos(&self) -> u32 {
503        self.subsec_nanos()
504    }
505}
506
507/// Invalid simulation time.
508pub const SIMTIME_INVALID: CSimulationTime = u64::MAX;
509
510/// Maximum and minimum valid values.
511//
512// cbindgen refuses to do the arithmetic here, so we we pre-compute,
513// and validate in the static assertion below.
514pub const SIMTIME_MAX: CSimulationTime = 17500059273709551614u64;
515const _: () =
516    assert!(SIMTIME_MAX == emulated_time::EMUTIME_MAX - emulated_time::EMUTIME_SIMULATION_START);
517
518pub const SIMTIME_MIN: CSimulationTime = 0u64;
519
520/// Represents one nanosecond in simulation time.
521pub const SIMTIME_ONE_NANOSECOND: CSimulationTime = 1u64;
522
523/// Represents one microsecond in simulation time.
524pub const SIMTIME_ONE_MICROSECOND: CSimulationTime = 1000u64;
525
526/// Represents one millisecond in simulation time.
527pub const SIMTIME_ONE_MILLISECOND: CSimulationTime = 1000000u64;
528
529/// Represents one second in simulation time.
530pub const SIMTIME_ONE_SECOND: CSimulationTime = 1000000000u64;
531
532/// Represents one minute in simulation time.
533pub const SIMTIME_ONE_MINUTE: CSimulationTime = 60000000000u64;
534
535/// Represents one hour in simulation time.
536pub const SIMTIME_ONE_HOUR: CSimulationTime = 3600000000000u64;
537
538pub mod export {
539    use super::*;
540    use crate::notnull::*;
541
542    #[unsafe(no_mangle)]
543    pub extern "C-unwind" fn simtime_from_timeval(val: libc::timeval) -> CSimulationTime {
544        SimulationTime::to_c_simtime(SimulationTime::try_from(val).ok())
545    }
546
547    #[unsafe(no_mangle)]
548    pub extern "C-unwind" fn simtime_from_timespec(val: libc::timespec) -> CSimulationTime {
549        SimulationTime::to_c_simtime(SimulationTime::try_from(val).ok())
550    }
551
552    /// # Safety
553    ///
554    /// Pointer args must be safe to write to.
555    #[must_use]
556    #[unsafe(no_mangle)]
557    pub unsafe extern "C-unwind" fn simtime_to_timeval(
558        val: CSimulationTime,
559        out: *mut libc::timeval,
560    ) -> bool {
561        let Some(simtime) = SimulationTime::from_c_simtime(val) else {
562            return false;
563        };
564        let Ok(tv) = libc::timeval::try_from(simtime) else {
565            return false;
566        };
567        unsafe { std::ptr::write(notnull_mut(out), tv) };
568        true
569    }
570
571    /// # Safety
572    ///
573    /// Pointer args must be safe to write to.
574    #[must_use]
575    #[unsafe(no_mangle)]
576    pub unsafe extern "C-unwind" fn simtime_to_timespec(
577        val: CSimulationTime,
578        out: *mut libc::timespec,
579    ) -> bool {
580        let Some(simtime) = SimulationTime::from_c_simtime(val) else {
581            return false;
582        };
583        let Ok(ts) = libc::timespec::try_from(simtime) else {
584            return false;
585        };
586        unsafe { std::ptr::write(out, ts) };
587        true
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use super::*;
594
595    #[test]
596    fn test_from_csimtime() {
597        let sim_time = 5 * SIMTIME_ONE_MINUTE + 7 * SIMTIME_ONE_MILLISECOND;
598        let rust_time = SimulationTime::from_c_simtime(sim_time).unwrap();
599
600        assert_eq!(Duration::from(rust_time).as_secs(), 5 * 60);
601        assert_eq!(Duration::from(rust_time).as_millis(), 5 * 60 * 1_000 + 7);
602
603        assert_eq!(
604            SimulationTime::from_c_simtime(SIMTIME_MAX).unwrap(),
605            SimulationTime::try_from(Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND))
606                .unwrap()
607        );
608        assert_eq!(SimulationTime::from_c_simtime(SIMTIME_MAX + 1), None);
609    }
610
611    #[test]
612    fn test_to_csimtime() {
613        let rust_time = SimulationTime::from_secs(5 * 60) + SimulationTime::from_millis(7);
614        let sim_time = 5 * SIMTIME_ONE_MINUTE + 7 * SIMTIME_ONE_MILLISECOND;
615
616        assert_eq!(SimulationTime::to_c_simtime(Some(rust_time)), sim_time);
617        assert_eq!(SimulationTime::to_c_simtime(None), SIMTIME_INVALID);
618        assert_eq!(
619            SimulationTime::to_c_simtime(Some(SimulationTime::MAX)),
620            SIMTIME_MAX
621        );
622    }
623
624    #[test]
625    fn test_from_timeval() {
626        use libc::timeval;
627
628        assert_eq!(
629            SimulationTime::try_from(timeval {
630                tv_sec: 0,
631                tv_usec: 0
632            }),
633            Ok(SimulationTime::ZERO)
634        );
635        assert_eq!(
636            SimulationTime::try_from(timeval {
637                tv_sec: 1,
638                tv_usec: 2
639            }),
640            Ok(
641                SimulationTime::try_from(Duration::from_secs(1) + Duration::from_micros(2))
642                    .unwrap()
643            )
644        );
645        assert_eq!(
646            SimulationTime::try_from(timeval {
647                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
648                tv_usec: SimulationTime::MAX.subsec_micros().into(),
649            }),
650            Ok(SimulationTime::from_micros(SimulationTime::MAX.as_micros()))
651        );
652
653        // Out of range
654        assert_eq!(
655            SimulationTime::try_from(timeval {
656                tv_sec: libc::time_t::MAX,
657                tv_usec: 999_999
658            }),
659            Err(())
660        );
661
662        assert_eq!(
663            SimulationTime::try_from(timeval {
664                tv_sec: 0,
665                tv_usec: 1_000_000
666            }),
667            Err(())
668        );
669        assert_eq!(
670            SimulationTime::try_from(timeval {
671                tv_sec: 0,
672                tv_usec: -1
673            }),
674            Err(())
675        );
676        assert_eq!(
677            SimulationTime::try_from(timeval {
678                tv_sec: -1,
679                tv_usec: 0
680            }),
681            Err(())
682        );
683        assert_eq!(
684            SimulationTime::try_from(timeval {
685                tv_sec: -1,
686                tv_usec: -1
687            }),
688            Err(())
689        );
690    }
691
692    #[test]
693    fn test_c_from_timeval() {
694        use export::simtime_from_timeval;
695        use libc::timeval;
696
697        assert_eq!(
698            simtime_from_timeval(timeval {
699                tv_sec: 0,
700                tv_usec: 0,
701            }),
702            0
703        );
704        assert_eq!(
705            simtime_from_timeval(timeval {
706                tv_sec: 1,
707                tv_usec: 2,
708            }),
709            SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_MICROSECOND
710        );
711
712        // Out of range
713        assert_eq!(
714            simtime_from_timeval(timeval {
715                tv_sec: libc::time_t::MAX,
716                tv_usec: 999_999,
717            }),
718            SIMTIME_INVALID
719        );
720
721        assert_eq!(
722            simtime_from_timeval(timeval {
723                tv_sec: 0,
724                tv_usec: 1_000_000,
725            }),
726            SIMTIME_INVALID
727        );
728        assert_eq!(
729            simtime_from_timeval(timeval {
730                tv_sec: 0,
731                tv_usec: -1,
732            }),
733            SIMTIME_INVALID
734        );
735        assert_eq!(
736            simtime_from_timeval(timeval {
737                tv_sec: -1,
738                tv_usec: 0,
739            }),
740            SIMTIME_INVALID
741        );
742        assert_eq!(
743            simtime_from_timeval(timeval {
744                tv_sec: -1,
745                tv_usec: -1,
746            }),
747            SIMTIME_INVALID
748        );
749    }
750
751    #[test]
752    fn test_to_timeval() {
753        use libc::timeval;
754
755        assert_eq!(
756            timeval::try_from(SimulationTime::ZERO),
757            Ok(timeval {
758                tv_sec: 0,
759                tv_usec: 0
760            })
761        );
762        assert_eq!(
763            timeval::try_from(
764                SimulationTime::try_from(Duration::from_secs(1) + Duration::from_micros(2))
765                    .unwrap()
766            ),
767            Ok(timeval {
768                tv_sec: 1,
769                tv_usec: 2
770            })
771        );
772        assert_eq!(
773            timeval::try_from(SimulationTime::MAX),
774            Ok(timeval {
775                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
776                tv_usec: SimulationTime::MAX.subsec_micros().into(),
777            })
778        );
779    }
780
781    #[test]
782    fn test_c_to_timeval() {
783        use export::simtime_to_timeval;
784        use libc::timeval;
785
786        let mut tv = unsafe { std::mem::zeroed() };
787
788        assert!(unsafe { simtime_to_timeval(0, &mut tv) });
789        assert_eq!(
790            tv,
791            timeval {
792                tv_sec: 0,
793                tv_usec: 0
794            }
795        );
796
797        assert!(unsafe {
798            simtime_to_timeval(SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_MICROSECOND, &mut tv)
799        });
800        assert_eq!(
801            tv,
802            timeval {
803                tv_sec: 1,
804                tv_usec: 2
805            }
806        );
807
808        {
809            assert!(unsafe { simtime_to_timeval(SIMTIME_MAX, &mut tv) });
810            let d = Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND);
811            assert_eq!(
812                tv,
813                timeval {
814                    tv_sec: d.as_secs().try_into().unwrap(),
815                    tv_usec: d.subsec_micros().into(),
816                }
817            );
818        }
819    }
820
821    #[test]
822    fn test_from_timespec() {
823        use libc::timespec;
824
825        assert_eq!(
826            SimulationTime::try_from(timespec {
827                tv_sec: 0,
828                tv_nsec: 0
829            }),
830            Ok(SimulationTime::ZERO)
831        );
832        assert_eq!(
833            SimulationTime::try_from(timespec {
834                tv_sec: 1,
835                tv_nsec: 2
836            }),
837            Ok(SimulationTime::try_from(Duration::from_secs(1) + Duration::from_nanos(2)).unwrap())
838        );
839        assert_eq!(
840            SimulationTime::try_from(timespec {
841                tv_sec: (SIMTIME_MAX / SIMTIME_ONE_SECOND).try_into().unwrap(),
842                tv_nsec: 0,
843            }),
844            Ok(
845                SimulationTime::try_from(Duration::from_secs(SIMTIME_MAX / SIMTIME_ONE_SECOND))
846                    .unwrap()
847            )
848        );
849
850        // The C SimulatedTime type is too small to represent this value.
851        // The Rust SimulationTime *could* represent it if we widen it.
852        assert_eq!(
853            SimulationTime::try_from(timespec {
854                tv_sec: libc::time_t::MAX,
855                tv_nsec: 999_999_999
856            }),
857            Err(())
858        );
859
860        assert_eq!(
861            SimulationTime::try_from(timespec {
862                tv_sec: 0,
863                tv_nsec: 1_000_000_000
864            }),
865            Err(())
866        );
867        assert_eq!(
868            SimulationTime::try_from(timespec {
869                tv_sec: 0,
870                tv_nsec: -1
871            }),
872            Err(())
873        );
874        assert_eq!(
875            SimulationTime::try_from(timespec {
876                tv_sec: -1,
877                tv_nsec: 0
878            }),
879            Err(())
880        );
881        assert_eq!(
882            SimulationTime::try_from(timespec {
883                tv_sec: -1,
884                tv_nsec: -1
885            }),
886            Err(())
887        );
888    }
889
890    #[test]
891    fn test_c_from_timespec() {
892        use export::simtime_from_timespec;
893        use libc::timespec;
894
895        assert_eq!(
896            simtime_from_timespec(timespec {
897                tv_sec: 0,
898                tv_nsec: 0,
899            }),
900            0
901        );
902        assert_eq!(
903            simtime_from_timespec(timespec {
904                tv_sec: 1,
905                tv_nsec: 2,
906            }),
907            SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_NANOSECOND
908        );
909
910        // The C SimulatedTime type is too small to represent this value.
911        assert_eq!(
912            simtime_from_timespec(timespec {
913                tv_sec: libc::time_t::MAX,
914                tv_nsec: 999_999_999,
915            }),
916            SIMTIME_INVALID
917        );
918
919        assert_eq!(
920            simtime_from_timespec(timespec {
921                tv_sec: 0,
922                tv_nsec: 1_000_000_000,
923            }),
924            SIMTIME_INVALID
925        );
926        assert_eq!(
927            simtime_from_timespec(timespec {
928                tv_sec: 0,
929                tv_nsec: -1,
930            }),
931            SIMTIME_INVALID
932        );
933        assert_eq!(
934            simtime_from_timespec(timespec {
935                tv_sec: -1,
936                tv_nsec: 0,
937            }),
938            SIMTIME_INVALID
939        );
940        assert_eq!(
941            simtime_from_timespec(timespec {
942                tv_sec: -1,
943                tv_nsec: -1,
944            }),
945            SIMTIME_INVALID
946        );
947    }
948
949    #[test]
950    fn test_to_timespec() {
951        use libc::timespec;
952
953        assert_eq!(
954            timespec::try_from(SimulationTime::ZERO),
955            Ok(timespec {
956                tv_sec: 0,
957                tv_nsec: 0
958            })
959        );
960        assert_eq!(
961            timespec::try_from(SimulationTime::from_secs(1) + SimulationTime::from_nanos(2)),
962            Ok(timespec {
963                tv_sec: 1,
964                tv_nsec: 2
965            })
966        );
967
968        assert_eq!(
969            timespec::try_from(SimulationTime::MAX),
970            Ok(timespec {
971                tv_sec: SimulationTime::MAX.as_secs().try_into().unwrap(),
972                tv_nsec: SimulationTime::MAX.subsec_nanos().into(),
973            })
974        );
975    }
976
977    #[test]
978    fn test_c_to_timespec() {
979        use export::simtime_to_timespec;
980        use libc::timespec;
981
982        let mut ts = unsafe { std::mem::zeroed() };
983
984        assert!(unsafe { simtime_to_timespec(0, &mut ts) });
985        assert_eq!(
986            ts,
987            timespec {
988                tv_sec: 0,
989                tv_nsec: 0
990            }
991        );
992
993        assert!(unsafe {
994            simtime_to_timespec(SIMTIME_ONE_SECOND + 2 * SIMTIME_ONE_NANOSECOND, &mut ts)
995        });
996        assert_eq!(
997            ts,
998            timespec {
999                tv_sec: 1,
1000                tv_nsec: 2
1001            }
1002        );
1003
1004        {
1005            assert!(unsafe { simtime_to_timespec(SIMTIME_MAX, &mut ts) });
1006            let d = Duration::from_nanos(SIMTIME_MAX / SIMTIME_ONE_NANOSECOND);
1007            assert_eq!(
1008                ts,
1009                timespec {
1010                    tv_sec: d.as_secs().try_into().unwrap(),
1011                    tv_nsec: d.subsec_nanos().into(),
1012                }
1013            );
1014        }
1015    }
1016}