use std::ops::{Add, Sub};
use speedy::{Readable, Writable};
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use super::duration::Duration;
#[derive(
Debug, PartialEq, Eq, PartialOrd, Ord, Readable, Writable, Clone, Copy, Serialize, Deserialize,
)]
pub struct Timestamp {
seconds: u32,
fraction: u32,
}
impl Timestamp {
pub const ZERO: Self = Self {
seconds: 0,
fraction: 0,
};
pub const INVALID: Self = Self {
seconds: 0xFFFF_FFFF,
fraction: 0xFFFF_FFFF,
};
pub const INFINITE: Self = Self {
seconds: 0x7FFF_FFFF,
fraction: 0xFFFF_FFFF,
};
pub fn now() -> Self {
match chrono::Utc::now().timestamp_nanos_opt() {
None => {
error!("Timestamp out of range.");
Timestamp::INVALID
}
Some(negative) if negative < 0 => {
error!("Timestamp out of range (negative).");
Timestamp::INVALID
}
Some(non_negative) => Self::from_nanos(non_negative as u64),
}
}
fn to_ticks(self) -> u64 {
(u64::from(self.seconds) << 32) + u64::from(self.fraction)
}
fn from_ticks(ticks: u64) -> Self {
Self {
seconds: (ticks >> 32) as u32,
fraction: ticks as u32,
}
}
fn from_nanos(nanos_since_unix_epoch: u64) -> Self {
Self {
seconds: (nanos_since_unix_epoch / 1_000_000_000) as u32,
fraction: (((nanos_since_unix_epoch % 1_000_000_000) << 32) / 1_000_000_000) as u32,
}
}
pub fn duration_since(&self, since: Self) -> Duration {
*self - since
}
}
impl Sub for Timestamp {
type Output = Duration;
fn sub(self, other: Self) -> Duration {
let a = self.to_ticks();
let b = other.to_ticks();
Duration::from_ticks(a.wrapping_sub(b) as i64)
}
}
impl Sub<Duration> for Timestamp {
type Output = Self;
fn sub(self, rhs: Duration) -> Self::Output {
if self == Self::INVALID {
Self::INVALID
} else {
let stamp_ticks = self.to_ticks() as i64; let sub_ticks = rhs.to_ticks();
let new_stamp_ticks = stamp_ticks.wrapping_sub(sub_ticks);
Self::from_ticks(new_stamp_ticks as u64)
}
}
}
impl Add<Duration> for Timestamp {
type Output = Self;
fn add(self, rhs: Duration) -> Self::Output {
let lhs_ticks = self.to_ticks();
let rhs_ticks = rhs.to_ticks() as u64;
Self::from_ticks(lhs_ticks + rhs_ticks)
}
}
#[cfg(test)]
mod tests {
use super::*;
serialization_test!( type = Timestamp,
{
time_zero,
Timestamp::ZERO,
le = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
be = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
},
{
time_invalid,
Timestamp::INVALID,
le = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
be = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
},
{
time_infinite,
Timestamp::INFINITE,
le = [0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF],
be = [0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
},
{
time_current_empty_fraction,
Timestamp { seconds: 1_537_045_491, fraction: 0 },
le = [0xF3, 0x73, 0x9D, 0x5B, 0x00, 0x00, 0x00, 0x00],
be = [0x5B, 0x9D, 0x73, 0xF3, 0x00, 0x00, 0x00, 0x00]
},
{
time_from_wireshark,
Timestamp { seconds: 1_519_152_760, fraction: 1_328_210_046 },
le = [0x78, 0x6E, 0x8C, 0x5A, 0x7E, 0xE0, 0x2A, 0x4F],
be = [0x5A, 0x8C, 0x6E, 0x78, 0x4F, 0x2A, 0xE0, 0x7E]
});
}