diff options
author | Carl Lerche <me@carllerche.com> | 2015-03-12 22:36:00 -0700 |
---|---|---|
committer | Carl Lerche <me@carllerche.com> | 2015-03-12 22:36:00 -0700 |
commit | 57f67e2895d19baacb5cf133ad577cdaec40e438 (patch) | |
tree | e2ae095778cb7e7df8ae2866b43ff3fad6aff1b0 /src/sys | |
parent | 67d6580d2c41c584d375ca57a67405fe8f5b4339 (diff) | |
download | nix-57f67e2895d19baacb5cf133ad577cdaec40e438.zip |
Add TimeVal and helpers
Diffstat (limited to 'src/sys')
-rw-r--r-- | src/sys/mod.rs | 2 | ||||
-rw-r--r-- | src/sys/time.rs | 239 |
2 files changed, 241 insertions, 0 deletions
diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 8e608d1e..06688204 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -31,3 +31,5 @@ pub mod wait; pub mod mman; pub mod uio; + +pub mod time; diff --git a/src/sys/time.rs b/src/sys/time.rs new file mode 100644 index 00000000..fbe17c49 --- /dev/null +++ b/src/sys/time.rs @@ -0,0 +1,239 @@ +use std::{fmt, ops}; +use std::num::Int; +use std::i64; +use libc::{time_t, suseconds_t}; + +#[repr(C)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +pub struct TimeVal { + pub tv_sec: time_t, + pub tv_usec: suseconds_t, +} + +const MICROS_PER_SEC: i64 = 1_000_000; +const SECS_PER_MINUTE: i64 = 60; +const SECS_PER_HOUR: i64 = 3600; + +const MIN: TimeVal = TimeVal { + tv_sec: -(i64::MAX / MICROS_PER_SEC - 1), + tv_usec: (-(MICROS_PER_SEC - 1)) as suseconds_t, +}; + +const MAX: TimeVal = TimeVal { + tv_sec: i64::MAX / MICROS_PER_SEC - 1, + tv_usec: (MICROS_PER_SEC - 1) as suseconds_t, +}; + +impl TimeVal { + #[inline] + pub fn zero() -> TimeVal { + TimeVal::microseconds(0) + } + + #[inline] + pub fn hours(hours: i64) -> TimeVal { + let secs = hours.checked_mul(SECS_PER_HOUR) + .expect("TimeVal::hours ouf of bounds"); + + TimeVal::seconds(secs) + } + + #[inline] + pub fn minutes(minutes: i64) -> TimeVal { + let secs = minutes.checked_mul(SECS_PER_MINUTE) + .expect("TimeVal::minutes out of bounds"); + + TimeVal::seconds(secs) + } + + #[inline] + pub fn seconds(seconds: i64) -> TimeVal { + let ret = TimeVal { tv_sec: seconds, tv_usec: 0 }; + assert!(ret >= MIN && ret <= MAX, "TimeVal out of bounds"); + ret + } + + #[inline] + pub fn milliseconds(milliseconds: i64) -> TimeVal { + let microseconds = milliseconds.checked_mul(1_000) + .expect("TimeVal::milliseconds out of bounds"); + + TimeVal::microseconds(microseconds) + } + + /// Makes a new `TimeVal` with given number of microseconds. + #[inline] + #[unstable(feature = "std_misc")] + pub fn microseconds(microseconds: i64) -> TimeVal { + let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); + let ret = TimeVal { tv_sec: secs, tv_usec: micros as suseconds_t }; + assert!(ret >= MIN && ret <= MAX, "TimeVal out of bounds"); + ret + } + + pub fn num_hours(&self) -> i64 { + self.num_seconds() / 3600 + } + + pub fn num_minutes(&self) -> i64 { + self.num_seconds() / 60 + } + + pub fn num_seconds(&self) -> i64 { + if self.tv_sec < 0 && self.tv_usec > 0 { + self.tv_sec + 1 + } else { + self.tv_sec + } + } + + pub fn num_milliseconds(&self) -> i64 { + self.num_microseconds() / 1_000 + } + + pub fn num_microseconds(&self) -> i64 { + let secs = self.num_seconds() * 1_000_000; + let usec = self.micros_mod_sec(); + secs + usec as i64 + } + + fn micros_mod_sec(&self) -> suseconds_t { + if self.tv_sec < 0 && self.tv_usec > 0 { + self.tv_usec - MICROS_PER_SEC as suseconds_t + } else { + self.tv_usec + } + } +} + +impl ops::Neg for TimeVal { + type Output = TimeVal; + + fn neg(self) -> TimeVal { + TimeVal::microseconds(-self.num_microseconds()) + } +} + +impl ops::Add for TimeVal { + type Output = TimeVal; + + fn add(self, rhs: TimeVal) -> TimeVal { + TimeVal::microseconds( + self.num_microseconds() + rhs.num_microseconds()) + } +} + +impl ops::Sub for TimeVal { + type Output = TimeVal; + + fn sub(self, rhs: TimeVal) -> TimeVal { + TimeVal::microseconds( + self.num_microseconds() - rhs.num_microseconds()) + } +} + +impl ops::Mul<i32> for TimeVal { + type Output = TimeVal; + + fn mul(self, rhs: i32) -> TimeVal { + let usec = self.num_microseconds().checked_mul(rhs as i64) + .expect("TimeVal multiply out of bounds"); + + TimeVal::microseconds(usec) + } +} + +impl ops::Div<i32> for TimeVal { + type Output = TimeVal; + + fn div(self, rhs: i32) -> TimeVal { + let usec = self.num_microseconds() / rhs as i64; + TimeVal::microseconds(usec) + } +} + +impl fmt::Display for TimeVal { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (abs, sign) = if self.tv_sec < 0 { + (-*self, "-") + } else { + (*self, "") + }; + + let sec = abs.tv_sec; + + try!(write!(f, "{}", sign)); + + if abs.tv_usec == 0 { + if abs.tv_sec == 1 { + try!(write!(f, "{} second", sec)); + } else { + try!(write!(f, "{} seconds", sec)); + } + } else if abs.tv_usec % 1000 == 0 { + try!(write!(f, "{}.{:03} seconds", sec, abs.tv_usec / 1000)); + } else { + try!(write!(f, "{}.{:06} seconds", sec, abs.tv_usec)); + } + + Ok(()) + } +} + +#[inline] +fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { + (div_floor_64(this, other), mod_floor_64(this, other)) +} + +#[inline] +fn div_floor_64(this: i64, other: i64) -> i64 { + match div_rem_64(this, other) { + (d, r) if (r > 0 && other < 0) + || (r < 0 && other > 0) => d - 1, + (d, _) => d, + } +} + +#[inline] +fn mod_floor_64(this: i64, other: i64) -> i64 { + match this % other { + r if (r > 0 && other < 0) + || (r < 0 && other > 0) => r + other, + r => r, + } +} + +#[inline] +fn div_rem_64(this: i64, other: i64) -> (i64, i64) { + (this / other, this % other) +} + +#[cfg(test)] +mod test { + use super::TimeVal; + + #[test] + pub fn test_time_val() { + assert!(TimeVal::seconds(1) != TimeVal::zero()); + assert_eq!(TimeVal::seconds(1) + TimeVal::seconds(2), TimeVal::seconds(3)); + assert_eq!(TimeVal::minutes(3) + TimeVal::seconds(2), + TimeVal::seconds(182)); + } + + #[test] + pub fn test_time_val_neg() { + let a = TimeVal::seconds(1) + TimeVal::microseconds(123); + let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123); + + assert_eq!(a, -b); + } + + #[test] + pub fn test_time_val_fmt() { + assert_eq!(TimeVal::zero().to_string(), "0 seconds"); + assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds"); + assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds"); + assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds"); + assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds"); + } +} |