pub struct ToUnixNanos<'anchor> { /* private fields */ }Expand description
Converts Ticks to UnixNanos against a live, re-anchorable
AnchorCell — the monotonic-to-wall-clock bridge, expressed as a
plain Pipe transform (In = Ticks, Out = UnixNanos) instead of a
bespoke wrapper type.
Compose it with any tick source via .and_then:
use proxima_clock::anchor::{AnchorCell, ToUnixNanos};
use proxima_clock::ticks::Ticks;
use proxima_clock::unix_nanos::UnixNanos;
use proxima_primitives::block_on;
use proxima_primitives::pipe::ext::PipeExt;
use proxima_primitives::pipe::primitives::Pipe;
use core::convert::Infallible;
use core::future::Future;
struct HardwareTicks(Ticks);
impl Pipe for HardwareTicks {
type In = ();
type Out = Ticks;
type Err = Infallible;
fn call(&self, (): ()) -> impl Future<Output = Result<Ticks, Infallible>> {
let ticks = self.0;
async move { Ok(ticks) }
}
}
let anchor = AnchorCell::new(Ticks::from_raw(0), UnixNanos::from_nanos(1_753_500_000_000_000_000));
let wall_clock = HardwareTicks(Ticks::from_raw(24_000_000))
.and_then(ToUnixNanos::new(&anchor, 24_000_000));
// `wall_clock` is itself `impl Pipe<In = (), Out = UnixNanos, Err = Infallible>` —
// no `WallClock<C>` wrapper type; the transform stage IS the bridge.
let now = block_on(wall_clock.call(())).expect("a tick read never fails");
assert_eq!(now, UnixNanos::from_nanos(1_753_500_001_000_000_000));frequency_hz is supplied once at construction (a hardware clock’s
nominal rate is fixed for its lifetime — a PTP discipline loop adjusts
AnchorCell’s offset, not this rate); it is not re-read per call.
Implementations§
Source§impl<'anchor> ToUnixNanos<'anchor>
impl<'anchor> ToUnixNanos<'anchor>
Sourcepub const fn new(anchor: &'anchor AnchorCell, frequency_hz: u64) -> Self
pub const fn new(anchor: &'anchor AnchorCell, frequency_hz: u64) -> Self
Build the conversion stage against anchor, at frequency_hz
(must be > 0 — see ToUnixNanos::call’s doc for the zero-frequency
clamp).
Trait Implementations§
Source§impl<'anchor> Clone for ToUnixNanos<'anchor>
impl<'anchor> Clone for ToUnixNanos<'anchor>
impl<'anchor> Copy for ToUnixNanos<'anchor>
Source§impl<'anchor> Debug for ToUnixNanos<'anchor>
impl<'anchor> Debug for ToUnixNanos<'anchor>
Source§impl Pipe for ToUnixNanos<'_>
impl Pipe for ToUnixNanos<'_>
Source§fn call(
&self,
ticks: Ticks,
) -> impl Future<Output = Result<UnixNanos, Infallible>>
fn call( &self, ticks: Ticks, ) -> impl Future<Output = Result<UnixNanos, Infallible>>
anchor_unix_nanos + (delta_ticks * 1_000_000_000 / frequency_hz),
computed in u128 so a large delta_ticks * 1e9 product cannot
overflow u64 before the division narrows it back down. u128
division lowers to a compiler_builtins soft-division call on
32-bit MCU targets (no native 64x64-bit or wider divide instruction)
— accepted here because this conversion runs once per read at the
edge (an export, a log timestamp, an NTP correction check), never
in the hot tick-counting loop where ticks stay raw u64 all the
way through.
delta_ticks uses Ticks::wrapping_sub, so a free-running
counter wrapping past u64::MAX between anchor and read still
converts correctly. A frequency_hz of 0 (an implementor
contract violation — no real oscillator runs at 0 Hz) is clamped
to 1 rather than dividing by zero and panicking: a bare-metal
caller may have no panic handler configured to unwind or abort
safely, so this conversion stays total.
Auto Trait Implementations§
impl<'anchor> Freeze for ToUnixNanos<'anchor>
impl<'anchor> RefUnwindSafe for ToUnixNanos<'anchor>
impl<'anchor> Send for ToUnixNanos<'anchor>
impl<'anchor> Sync for ToUnixNanos<'anchor>
impl<'anchor> Unpin for ToUnixNanos<'anchor>
impl<'anchor> UnsafeUnpin for ToUnixNanos<'anchor>
impl<'anchor> UnwindSafe for ToUnixNanos<'anchor>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<P> PipeExt for Pwhere
P: Pipe,
impl<P> PipeExt for Pwhere
P: Pipe,
Source§fn and_then<Next>(self, next: Next) -> AndThen<Self, Next>
fn and_then<Next>(self, next: Next) -> AndThen<Self, Next>
self.and_then(next) is AndThen::new(self, next) — reads
left-to-right at the call site.