#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Epoch(pub u128);
impl Epoch {
pub const ZERO: Epoch = Epoch(0);
#[must_use]
pub fn to_wire(self) -> (u64, u64) {
((self.0 >> 64) as u64, self.0 as u64)
}
#[must_use]
pub fn from_wire(hi: u64, lo: u64) -> Self {
Epoch((u128::from(hi) << 64) | u128::from(lo))
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
#[test]
fn default_is_zero() {
assert_eq!(Epoch::default(), Epoch::ZERO);
}
#[test]
fn ordering() {
assert!(Epoch(1) < Epoch(2));
assert!(Epoch::ZERO < Epoch(1));
}
#[test]
fn wire_round_trip_spans_the_64_bit_boundary() {
for epoch in [
Epoch::ZERO,
Epoch(1),
Epoch(u128::from(u64::MAX)),
Epoch(u128::from(u64::MAX) + 1),
Epoch(u128::MAX),
] {
let (hi, lo) = epoch.to_wire();
assert_eq!(Epoch::from_wire(hi, lo), epoch);
}
}
#[test]
fn wire_halves_preserve_numeric_order() {
let just_over = Epoch::from_wire(1, 0);
let max_low = Epoch::from_wire(0, u64::MAX);
assert!(just_over > max_low);
}
proptest! {
#[test]
fn wire_round_trip(raw in any::<u128>()) {
let epoch = Epoch(raw);
let (hi, lo) = epoch.to_wire();
prop_assert_eq!(Epoch::from_wire(hi, lo), epoch);
}
#[test]
fn wire_halves_order_matches_numeric(a in any::<u128>(), b in any::<u128>()) {
let (ea, eb) = (Epoch(a), Epoch(b));
prop_assert_eq!(ea.to_wire().cmp(&eb.to_wire()), ea.cmp(&eb));
}
}
}