#![cfg_attr(feature = "tsc", feature(asm))]
use std::sync::Arc;
mod monotonic;
use self::monotonic::Monotonic;
mod counter;
#[allow(unused_imports)]
use self::counter::Counter;
mod mock;
pub use self::mock::Mock;
type Reference = Monotonic;
#[cfg(feature = "tsc")]
type Source = Counter;
#[cfg(not(feature = "tsc"))]
type Source = Monotonic;
#[derive(Clone)]
enum ClockType {
Optimized(Reference, Source, Calibration),
Mock(Arc<Mock>),
}
trait ClockSource {
fn now(&self) -> u64;
fn start(&self) -> u64;
fn end(&self) -> u64;
}
#[derive(Clone)]
pub(crate) struct Calibration {
identical: bool,
ref_time: f64,
src_time: f64,
ref_hz: f64,
src_hz: f64,
hz_ratio: f64,
}
impl Calibration {
pub fn new() -> Calibration {
Calibration {
identical: false,
ref_time: 0.0,
src_time: 0.0,
ref_hz: 1_000_000_000.0,
src_hz: 1_000_000_000.0,
hz_ratio: 1.0,
}
}
#[allow(dead_code)]
pub fn identical() -> Calibration {
let mut calibration = Self::new();
calibration.identical = true;
calibration
}
#[allow(dead_code)]
pub fn calibrate<R, S>(&mut self, reference: &R, source: &S)
where
R: ClockSource,
S: ClockSource,
{
self.ref_time = reference.now() as f64;
self.src_time = source.start() as f64;
let ref_end = self.ref_time + self.ref_hz;
loop {
let t = reference.now() as f64;
if t >= ref_end {
break;
}
}
let src_end = source.end() as f64;
let ref_d = ref_end - self.ref_time;
let src_d = src_end - self.src_time;
self.src_hz = (src_d * self.ref_hz) / ref_d;
self.hz_ratio = self.ref_hz / self.src_hz;
}
}
impl Default for Calibration {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone)]
pub struct Clock {
inner: ClockType,
}
impl Clock {
#[cfg(feature = "tsc")]
pub fn new() -> Clock {
let reference = Reference::new();
let source = Source::new();
let mut calibration = Calibration::new();
calibration.calibrate(&reference, &source);
Clock {
inner: ClockType::Optimized(reference, source, calibration),
}
}
#[cfg(not(feature = "tsc"))]
pub fn new() -> Clock {
let reference = Reference::new();
let source = Source::new();
let calibration = Calibration::identical();
Clock {
inner: ClockType::Optimized(reference, source, calibration),
}
}
pub fn mock() -> (Clock, Arc<Mock>) {
let mock = Arc::new(Mock::new(0));
let clock = Clock {
inner: ClockType::Mock(mock.clone()),
};
(clock, mock)
}
pub fn now(&self) -> u64 {
match &self.inner {
ClockType::Optimized(_, source, _) => self.scaled(source.now()),
ClockType::Mock(mock) => mock.now(),
}
}
pub fn raw(&self) -> u64 {
match &self.inner {
ClockType::Optimized(_, source, _) => source.now(),
ClockType::Mock(mock) => mock.now(),
}
}
pub fn start(&self) -> u64 {
match &self.inner {
ClockType::Optimized(_, source, _) => source.start(),
ClockType::Mock(mock) => mock.now(),
}
}
pub fn end(&self) -> u64 {
match &self.inner {
ClockType::Optimized(_, source, _) => source.end(),
ClockType::Mock(mock) => mock.now(),
}
}
pub fn scaled(&self, value: u64) -> u64 {
match &self.inner {
ClockType::Optimized(_, _, calibration) => {
if calibration.identical {
value
} else {
(((value as f64 - calibration.src_time) * calibration.hz_ratio) + calibration.ref_time) as u64
}
}
ClockType::Mock(_) => value,
}
}
pub fn delta(&self, start: u64, end: u64) -> u64 {
let raw_delta = end.wrapping_sub(start);
match &self.inner {
ClockType::Optimized(_, _, calibration) => (raw_delta as f64 * calibration.hz_ratio) as u64,
ClockType::Mock(_) => raw_delta,
}
}
}
impl Default for Clock {
fn default() -> Clock {
Clock::new()
}
}
#[cfg(test)]
mod tests {
use super::Clock;
#[test]
fn test_mock() {
let (clock, mock) = Clock::mock();
assert_eq!(clock.now(), 0);
mock.increment(42);
assert_eq!(clock.now(), 42);
}
#[test]
fn test_now() {
let clock = Clock::new();
assert!(clock.now() > 0);
}
#[test]
fn test_raw() {
let clock = Clock::new();
assert!(clock.raw() > 0);
}
#[test]
fn test_start() {
let clock = Clock::new();
assert!(clock.start() > 0);
}
#[test]
fn test_end() {
let clock = Clock::new();
assert!(clock.end() > 0);
}
#[test]
fn test_scaled() {
let clock = Clock::new();
let raw = clock.raw();
assert!(clock.scaled(raw) > 0);
}
}