kala 2.0.0

Timing with nanosecond precision on multiple architectures.
Documentation
use crate::time::{Duration, Instant};

mod fallback;

cfg_select! {
  target_arch = "x86_64" => {
    mod x86_64;

    use x86_64::now;
    use x86_64::clock_fence;
    use x86_64::is_hw_supported;
    use x86_64::tick_frequency;
  },

  target_arch = "aarch64" => {
    mod aarch64;

    use aarch64::now;
    use aarch64::clock_fence;
    use aarch64::is_hw_supported;
    use aarch64::tick_frequency;
  },

  _ => {
    // We always write `fallback::$NAME` for actual fallback calls, but we need
    // the `use` statements to make the compiler happy on all architectures.
    use fallback::now;
    use fallback::clock_fence;
    use fallback::is_hw_supported;
    use fallback::tick_frequency;
  }
}

// The frequency (in Hz) of the TSC clock. The variant describes the source of
// the frequency data.
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(dead_code, reason = "Some variants aren't used on some architectures.")]
enum TscFrequency {
  CpuMetadata(u64), // From CPUID or CPU system register.
  Estimate(u64),    // An estimate we computed
}

// Provides access to arch-specific APIs with automatic software fallback where
// necessary.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum GuardedArch {
  Fallback,
  Hardware,
}

impl GuardedArch {
  pub fn best() -> GuardedArch {
    if is_hw_supported() {
      GuardedArch::Hardware
    } else {
      GuardedArch::Fallback
    }
  }

  pub fn is_hw_supported(&self) -> bool {
    match self {
      GuardedArch::Hardware => true,
      GuardedArch::Fallback => false,
    }
  }

  pub fn now(&self) -> Instant {
    match self {
      GuardedArch::Hardware => now(),
      GuardedArch::Fallback => fallback::now(),
    }
  }

  pub fn clock_fence(&self) {
    match self {
      GuardedArch::Hardware => clock_fence(),
      GuardedArch::Fallback => fallback::clock_fence(),
    }
  }

  pub fn elapsed(&self, start: Instant, end: Instant) -> Duration {
    match (start, end, *self) {
      (
        Instant::Ticks(start_ticks),
        Instant::Ticks(end_ticks),
        GuardedArch::Hardware,
      ) => {
        let freq = match tick_frequency() {
          TscFrequency::CpuMetadata(freq) => freq,
          TscFrequency::Estimate(freq) => freq,
        };

        Duration::from_ticks(end_ticks.saturating_sub(start_ticks), freq)
      },
      (
        Instant::Std(start_instant),
        Instant::Std(end_instant),
        GuardedArch::Fallback,
      ) => Duration::from_nanos(
        end_instant.duration_since(start_instant).as_nanos() as u64,
      ),
      _ => unreachable!("other type combinations should never happen"),
    }
  }
}