minerva 0.2.0

Causal ordering for distributed systems
#[cfg(kani)]
mod proofs;
mod wire;

pub use wire::{DecodeError, KAIROS_WIRE_LEN};

/// Converts a caller-defined kairotic tag into the stamp's `u16` field.
pub trait ToU16 {
    /// Returns the packed kairotic tag.
    fn to_u16(&self) -> u16;
}

/// `u16` is already the on-stamp representation.
impl ToU16 for u16 {
    fn to_u16(&self) -> u16 {
        *self
    }
}

/// A Hybrid Logical Timestamp (128 bits).
///
/// Layout, most significant first: `physical:u64 | logical:u16 |
/// kairotic:u16 | station_id:u32`. The derived `Ord` is therefore the
/// `(physical, logical, kairotic, station)` total order.
///
/// That order is total and deterministic, not a causality test: `a < b` does
/// not prove that `a` happened before `b`. Use it to sort and to break ties.
/// Use `metis` to decide causality.
///
/// `Hash` is derived from the packed `u128` and is therefore consistent with the
/// derived `Eq` and `Ord`: equal stamps hash equally. This makes `Kairos` usable
/// as a map or set key for the event-sourcing and protocol use cases in the crate
/// metadata. A stable on-the-wire byte encoding is a separate, versioned concern
/// specified in PRD 0002, not an implicit guarantee of this in-memory layout.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Kairos(u128);

impl Kairos {
    /// Packs physical time, the logical counter, station identity, and the
    /// kairotic tag into one stamp.
    ///
    /// `station_id` is not validated here. Non-zero is a
    /// [`Clock`](crate::kairos::Clock) construction invariant, not a stamp
    /// invariant.
    #[must_use]
    #[allow(
        clippy::needless_pass_by_value,
        reason = "kairotic is converted via ToU16::to_u16; by-value keeps the generic API ergonomic for Copy tags"
    )]
    pub fn new<T: ToU16>(physical: u64, logical: u16, station_id: u32, kairotic: T) -> Self {
        let kairotic_u16 = kairotic.to_u16();
        let value = (u128::from(physical) << 64)
            | (u128::from(logical) << 48)
            | (u128::from(kairotic_u16) << 32)
            | u128::from(station_id);
        Self(value)
    }

    /// Returns the physical time component of the timestamp, in nanoseconds.
    #[must_use]
    pub const fn physical(&self) -> u64 {
        (self.0 >> 64) as u64
    }

    /// Returns the logical counter component of the timestamp.
    #[must_use]
    pub const fn logical(&self) -> u16 {
        ((self.0 >> 48) & 0xFFFF) as u16
    }

    /// Returns the kairotic component of the timestamp.
    #[must_use]
    pub const fn kairotic(&self) -> u16 {
        ((self.0 >> 32) & 0xFFFF) as u16
    }

    /// Returns the station ID component of the timestamp.
    #[must_use]
    pub const fn station_id(&self) -> u32 {
        (self.0 & ((1 << 32) - 1)) as u32
    }
}