#[cfg(feature = "std")]
use crate::std_clocks::InstantTime;
use crate::{CalibratedClock, Clock, ClockSynchronization, DurationCalibration, Time};
use core::cmp::{self};
#[derive(Copy, Clone, Debug)]
pub struct WrappingU64Time<const BITS: u32 = 64>;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct WrappingU64Instant<const BITS: u32 = 64>(u64);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct WrappingU64Duration<const BITS: u32 = 64>(u64);
const fn assert_bits(x: u32) {
assert!(x > 0 && x <= 64);
}
#[inline]
#[track_caller]
fn debug_assert_in_range(x: u64, unused_bits: u32) {
let mask = u64::MAX >> unused_bits;
debug_assert!(x & mask == x);
}
impl<const BITS: u32> WrappingU64Instant<BITS> {
#[inline]
pub fn new(x: u64) -> Self {
debug_assert_in_range(x, Self::UNUSED_BITS);
Self::wrapping_new(x)
}
#[inline]
pub fn wrapping_new(x: u64) -> Self {
const { assert_bits(BITS) };
Self(x << Self::UNUSED_BITS)
}
#[inline]
pub fn to_ticks(self) -> u64 {
self.0 >> Self::UNUSED_BITS
}
#[inline]
pub fn to_bits(self) -> u64 {
self.0
}
#[inline]
pub fn from_bits(x: u64) -> Self {
const { assert_bits(BITS) };
Self(x)
}
const UNUSED_BITS: u32 = 64 - BITS;
}
impl<const BITS: u32> WrappingU64Duration<BITS> {
#[inline]
pub fn new(x: u64) -> Self {
debug_assert_in_range(x, Self::UNUSED_BITS);
Self::wrapping_new(x)
}
#[inline]
pub fn wrapping_new(x: u64) -> Self {
const { assert_bits(BITS) };
Self(x << Self::UNUSED_BITS)
}
#[inline]
pub fn to_ticks(self) -> u64 {
self.0 >> Self::UNUSED_BITS
}
#[inline]
pub fn to_bits(self) -> u64 {
self.0
}
#[inline]
pub fn from_bits(x: u64) -> Self {
const { assert_bits(BITS) };
Self(x)
}
const UNUSED_BITS: u32 = 64 - BITS;
}
impl<const BITS: u32> Time for WrappingU64Time<BITS> {
type Instant = WrappingU64Instant<BITS>;
type Duration = WrappingU64Duration<BITS>;
#[inline]
fn instant_sub(a: Self::Instant, b: Self::Instant) -> Self::Duration {
debug_assert!(Self::instant_cmp(a, b).is_ge());
WrappingU64Duration(a.0.wrapping_sub(b.0))
}
#[inline]
fn duration_sub(a: Self::Duration, b: Self::Duration) -> Self::Duration {
WrappingU64Duration(a.0 - b.0)
}
#[inline]
fn duration_add(a: Self::Duration, b: Self::Duration) -> Self::Duration {
WrappingU64Duration(a.0 + b.0)
}
#[inline]
fn mixed_sub(a: Self::Instant, b: Self::Duration) -> Self::Instant {
WrappingU64Instant(a.0.wrapping_sub(b.0))
}
#[inline]
fn mixed_add(a: Self::Instant, b: Self::Duration) -> Self::Instant {
WrappingU64Instant(a.0.wrapping_add(b.0))
}
#[inline]
fn instant_cmp(a: Self::Instant, b: Self::Instant) -> cmp::Ordering {
(a.0 as i64).wrapping_sub(b.0 as i64).cmp(&0)
}
}
#[derive(Clone, Copy, Debug)]
pub struct U64Calibration<const BITS: u32 = 64> {
to_ns: u64,
to_ns_shift: u32,
from_ns: u64,
from_ns_shift: u32,
}
impl<const BITS: u32> U64Calibration<BITS> {
pub fn new(duration: WrappingU64Duration<BITS>, duration_ns: u64) -> Self {
assert!(duration.0 > 0);
assert!(duration_ns > 0);
let (to_ns, to_ns_shift) = make_mul_shift(duration.0, duration_ns);
let (from_ns, from_ns_shift) = make_mul_shift(duration_ns, duration.0);
U64Calibration {
to_ns,
to_ns_shift,
from_ns,
from_ns_shift,
}
}
pub fn new_with_reference_clock<C: Clock<Time = WrappingU64Time<BITS>>, R: Clock>(
clock: &C,
reference_clock: &CalibratedClock<R>,
min_duration: <R::Time as Time>::Duration,
mut wait: impl FnMut(<R::Time as Time>::Instant),
) -> (Self, ClockSynchronization<R::Time, C::Time>) {
let s1 = ClockSynchronization::new_aba_calibrated(reference_clock, clock);
let wait_until = R::Time::mixed_add(s1.epoch_a(), min_duration);
while R::Time::instant_cmp(reference_clock.clock.now(), wait_until).is_lt() {
wait(wait_until);
}
let s2 = ClockSynchronization::new_aba_calibrated(reference_clock, clock);
(
Self::new(
C::Time::instant_sub(s2.epoch_b(), s1.epoch_b()),
reference_clock
.calibration
.convert_to_ns(R::Time::instant_sub(s2.epoch_a(), s1.epoch_a())),
),
s2,
)
}
#[cfg(feature = "std")]
pub fn new_with_std_instant<C: Clock<Time = WrappingU64Time<BITS>>>(
clock: &C,
min_duration: std::time::Duration,
) -> (Self, ClockSynchronization<InstantTime, C::Time>) {
use crate::{InherentlyCalibrated, std_clocks::InstantClock};
Self::new_with_reference_clock(
clock,
&CalibratedClock {
clock: InstantClock,
calibration: InherentlyCalibrated,
},
min_duration,
|until| {
let now = std::time::Instant::now();
if let Some(remaining) = until.checked_duration_since(now) {
std::thread::sleep(remaining);
}
},
)
}
}
impl<const BITS: u32> DurationCalibration<WrappingU64Duration<BITS>> for U64Calibration<BITS> {
#[inline]
fn convert_to_ns(&self, d: WrappingU64Duration<BITS>) -> u64 {
apply_mul_shift(d.0, self.to_ns, self.to_ns_shift)
}
#[inline]
fn convert_from_ns(&self, ns: u64) -> WrappingU64Duration<BITS> {
WrappingU64Duration(apply_mul_shift(ns, self.from_ns, self.from_ns_shift))
}
}
fn make_mul_shift(from: u64, to: u64) -> (u64, u32) {
debug_assert!(from > 0 && from < (1 << 63));
debug_assert!(to > 0 && to < (1 << 63));
let l_to = 64 - to.leading_zeros();
let l_from = 64 - from.leading_zeros();
let s0 = l_from + 64 - l_to;
let shift = if (to as u128) << s0 < (from as u128) << 64 {
s0
} else {
s0 - 1
};
let mul = (((to as u128) << shift) / from as u128) as u64;
debug_assert!(mul >= 1 << 63);
(mul, shift)
}
#[inline]
fn apply_mul_shift(x: u64, mul: u64, shift: u32) -> u64 {
((mul as u128 * x as u128 + (1u128 << (shift - 1))) >> shift) as u64
}
#[test]
fn test_make_mul_shift() {
use std::vec::Vec;
let mut values: Vec<u64> = (1..61)
.flat_map(|s| (1..4).map(move |i| i << s))
.flat_map(|x| [x - 1, x, x + 1])
.filter(|&x| x > 0 && x < (1 << 63))
.collect();
values.sort_unstable();
values.dedup();
for &from in &values {
for &to in &values {
let (mul, shift) = make_mul_shift(from, to);
assert!(mul >= (1 << 63));
assert_eq!(apply_mul_shift(from, mul, shift), to);
}
}
}
#[test]
fn test_calibration() {
fn exact(x: u64, from: u64, to: u64) -> u128 {
(x as u128 * to as u128 + from as u128 / 2) / from as u128
}
fn check<const BITS: u32>(cal_ticks: u64, cal_ns: u64) {
if cal_ticks >= 1 << (BITS - 1) || cal_ns >= (1 << 63) {
return;
}
let cal_d = WrappingU64Duration::<BITS>::new(cal_ticks);
let c = U64Calibration::<BITS>::new(cal_d, cal_ns);
for i in 0..128u64 {
let d = WrappingU64Duration::<BITS>::new(cal_ticks / 64 * i);
let want = exact(d.to_bits(), cal_d.to_bits(), cal_ns);
let got = c.convert_to_ns(d) as u128;
assert!(got.abs_diff(want) <= 1, "BITS={BITS} to_ns {got} != {want}");
}
for i in 0..128u64 {
let ns = cal_ns / 64 * i;
let want = exact(ns, cal_ns, cal_d.to_bits());
if want < 1u128 << 63 {
let got = c.convert_from_ns(ns).to_bits() as u128;
assert!(
got.abs_diff(want) <= 1,
"BITS={BITS} from_ns {got} != {want}"
);
}
}
}
macro_rules! check_bits {
($($bits:literal),*) => {$({
let max = (1u64 << ($bits - 1)) - 1;
for ns in [1u64, 112,180,999_999_999, 1 << 62, (1u64 << 63) - 1,max] {
for ticks in [1,112,180,max,max/3+1]{
check::<$bits>(ticks, ns);
}
}
})*};
}
check_bits!(2, 3, 7, 8, 15, 16, 23, 24, 31, 32, 40, 48, 56, 63, 64);
}