summaryrefslogtreecommitdiff
path: root/src/sys
diff options
context:
space:
mode:
authorCarl Lerche <me@carllerche.com>2015-03-12 22:36:00 -0700
committerCarl Lerche <me@carllerche.com>2015-03-12 22:36:00 -0700
commit57f67e2895d19baacb5cf133ad577cdaec40e438 (patch)
treee2ae095778cb7e7df8ae2866b43ff3fad6aff1b0 /src/sys
parent67d6580d2c41c584d375ca57a67405fe8f5b4339 (diff)
downloadnix-57f67e2895d19baacb5cf133ad577cdaec40e438.zip
Add TimeVal and helpers
Diffstat (limited to 'src/sys')
-rw-r--r--src/sys/mod.rs2
-rw-r--r--src/sys/time.rs239
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");
+ }
+}