use std::fmt;
pub type TimeStamp = u64;
pub type Duration = u64;
#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
pub struct Time {
pub seconds: u64,
pub frac: f64,
}
impl Time {
const SECONDS_PER_MINUTE: u64 = 60;
const SECONDS_PER_HOUR: u64 = 60 * 60;
const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000;
const NANOSECONDS_PER_SECOND_INV: f64 = 1.0 / 1_000_000_000.0;
pub fn new(seconds: u64, frac: f64) -> Self {
Time { seconds, frac }
}
pub fn from_ss(s: u8, ns: u32) -> Option<Time> {
if s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
return None;
}
let seconds = u64::from(s);
let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
Some(Time { seconds, frac })
}
pub fn from_mmss(m: u8, s: u8, ns: u32) -> Option<Time> {
if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
return None;
}
let seconds = (Time::SECONDS_PER_MINUTE * u64::from(m)) + u64::from(s);
let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
Some(Time { seconds, frac })
}
pub fn from_hhmmss(h: u32, m: u8, s: u8, ns: u32) -> Option<Time> {
if m > 59 || s > 59 || ns >= Time::NANOSECONDS_PER_SECOND {
return None;
}
let seconds = (Time::SECONDS_PER_HOUR * u64::from(h))
+ (Time::SECONDS_PER_MINUTE * u64::from(m))
+ u64::from(s);
let frac = Time::NANOSECONDS_PER_SECOND_INV * f64::from(ns);
Some(Time { seconds, frac })
}
}
impl From<u8> for Time {
fn from(seconds: u8) -> Self {
Time::new(u64::from(seconds), 0.0)
}
}
impl From<u16> for Time {
fn from(seconds: u16) -> Self {
Time::new(u64::from(seconds), 0.0)
}
}
impl From<u32> for Time {
fn from(seconds: u32) -> Self {
Time::new(u64::from(seconds), 0.0)
}
}
impl From<u64> for Time {
fn from(seconds: u64) -> Self {
Time::new(seconds, 0.0)
}
}
impl From<f32> for Time {
fn from(seconds: f32) -> Self {
if seconds >= 0.0 {
Time::new(seconds.trunc() as u64, f64::from(seconds.fract()))
}
else {
Time::new(0, 0.0)
}
}
}
impl From<f64> for Time {
fn from(seconds: f64) -> Self {
if seconds >= 0.0 {
Time::new(seconds.trunc() as u64, seconds.fract())
}
else {
Time::new(0, 0.0)
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct TimeBase {
pub numer: u32,
pub denom: u32,
}
impl TimeBase {
pub fn new(numer: u32, denom: u32) -> Self {
if numer == 0 || denom == 0 {
panic!("TimeBase cannot have 0 numerator or denominator");
}
TimeBase { numer, denom }
}
pub fn calc_time(&self, ts: TimeStamp) -> Time {
assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
let dividend = u128::from(ts) * u128::from(self.numer);
if dividend < (1 << 52) {
let seconds = (dividend as f64) / f64::from(self.denom);
Time::new(seconds.trunc() as u64, seconds.fract())
}
else {
let quotient = dividend / u128::from(self.denom);
let rem = (dividend - (quotient * u128::from(self.denom))) as u32;
let frac = f64::from(rem) / f64::from(self.denom);
Time::new(quotient as u64, frac)
}
}
pub fn calc_timestamp(&self, time: Time) -> TimeStamp {
assert!(self.numer > 0 && self.denom > 0, "TimeBase numerator or denominator are 0.");
assert!(time.frac >= 0.0 && time.frac < 1.0, "Invalid range for Time fractional part.");
let k = 1.0 / f64::from(self.numer);
let product = u128::from(time.seconds) * u128::from(self.denom);
let a = if product > (1 << 52) {
let u = ((product & !0xffff_ffff_ffff) >> 48) as u64;
let l = ((product & 0xffff_ffff_ffff) >> 0) as u64;
let uk = (u as f64) * k;
let ul = (l as f64) * k;
((uk as u64) << 48).wrapping_add(ul as u64)
}
else {
((product as f64) * k) as u64
};
let b = (k * f64::from(self.denom) * time.frac) as u64;
a.wrapping_add(b)
}
}
impl From<TimeBase> for f64 {
fn from(timebase: TimeBase) -> Self {
f64::from(timebase.numer) / f64::from(timebase.denom)
}
}
impl fmt::Display for TimeBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.numer, self.denom)
}
}
#[cfg(test)]
mod tests {
use super::{Time, TimeBase};
#[test]
fn verify_timebase() {
let tb1 = TimeBase::new(1, 320);
assert_eq!(tb1.calc_time(0), Time::new(0, 0.0));
assert_eq!(tb1.calc_time(12_345), Time::new(38, 0.578125));
assert_eq!(tb1.calc_time(0x0f_ffff_ffff_ffff), Time::new(14_073_748_835_532, 0.796875));
assert_eq!(tb1.calc_time(0x10_0000_0000_0001), Time::new(14_073_748_835_532, 0.803125));
assert_eq!(tb1.calc_time(u64::MAX), Time::new(57_646_075_230_342_348, 0.796875));
let tb2 = TimeBase::new(320, 1);
assert_eq!(tb2.calc_time(u64::MAX), Time::new(18_446_744_073_709_551_296, 0.0));
assert_eq!(tb1.calc_timestamp(Time::new(0, 0.0)), 0);
assert_eq!(tb1.calc_timestamp(Time::new(38, 0.578125)), 12_345);
assert_eq!(
tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.796875)),
0x0f_ffff_ffff_ffff
);
assert_eq!(
tb1.calc_timestamp(Time::new(14_073_748_835_532, 0.803125)),
0x10_0000_0000_0001
);
assert_eq!(tb1.calc_timestamp(Time::new(57_646_075_230_342_348, 0.796875)), u64::MAX);
}
}