1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//
// Copyright (c) 2017, 2020 ADLINK Technology Inc.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
//
use humantime::format_rfc3339_nanos;
use std::fmt;
use std::ops::{Add, AddAssign, Sub};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

// maximal number of seconds that can be represented in the 32-bits part
const MAX_NB_SEC: u64 = (1u64 << 32) - 1;
// number of NTP fraction per second (2^32)
const FRAC_PER_SEC: u64 = 1u64 << 32;
// Bit-mask for the fraction of a second part within an NTP timestamp
const FRAC_MASK: u64 = 0xFFFF_FFFFu64;

// number of nanoseconds in 1 second
const NANO_PER_SEC: u64 = 1_000_000_000;

/// A NTP 64-bits format as specified in
/// [RFC-5909](https://tools.ietf.org/html/rfc5905#section-6)
///
/// The first 32-bits part is the number of second since the EPOCH of the physical clock,
/// and the second 32-bits part is the fraction of second.  
/// In case it's part of a [`crate::Timestamp`] generated by an [`crate::HLC`] the last few bits are replaced
/// by the HLC logical counter. The size of this counter currently hard-coded in [`crate::CSIZE`].
///
/// Note that this timestamp in actually similar to a [`std::time::Duration`], as it doesn't
/// define an EPOCH. Only the [`NTP64::to_system_time()`] and [`std::fmt::Display::fmt()`] operations assume that
/// it's relative to UNIX_EPOCH (1st Jan 1970) to display the timpestamp in RFC-3339 format.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct NTP64(pub u64);

impl NTP64 {
    /// Returns this NTP64 as a u64.
    #[inline]
    pub fn as_u64(&self) -> u64 {
        self.0
    }

    /// Returns the 32-bits seconds part.
    #[inline]
    pub fn as_secs(&self) -> u32 {
        (self.0 >> 32) as u32
    }

    /// Returns the 32-bits fraction of second part converted to nanoseconds.
    #[inline]
    pub fn subsec_nanos(&self) -> u32 {
        let frac = self.0 & FRAC_MASK;
        ((frac * NANO_PER_SEC) / FRAC_PER_SEC) as u32
    }

    /// Convert to a [`Duration`].
    #[inline]
    pub fn to_duration(&self) -> Duration {
        Duration::new(self.as_secs().into(), self.subsec_nanos())
    }

    /// Convert to a [`SystemTime`] (making the assumption that this NTP64 is relative to [`UNIX_EPOCH`]).
    #[inline]
    pub fn to_system_time(&self) -> SystemTime {
        UNIX_EPOCH + self.to_duration()
    }
}

impl Add for NTP64 {
    type Output = Self;

    #[inline]
    fn add(self, other: Self) -> Self {
        Self(self.0 + other.0)
    }
}

impl<'a> Add<NTP64> for &'a NTP64 {
    type Output = <NTP64 as Add<NTP64>>::Output;

    #[inline]
    fn add(self, other: NTP64) -> <NTP64 as Add<NTP64>>::Output {
        Add::add(*self, other)
    }
}

impl Add<&NTP64> for NTP64 {
    type Output = <NTP64 as Add<NTP64>>::Output;

    #[inline]
    fn add(self, other: &NTP64) -> <NTP64 as Add<NTP64>>::Output {
        Add::add(self, *other)
    }
}

impl Add<&NTP64> for &NTP64 {
    type Output = <NTP64 as Add<NTP64>>::Output;

    #[inline]
    fn add(self, other: &NTP64) -> <NTP64 as Add<NTP64>>::Output {
        Add::add(*self, *other)
    }
}

impl Add<u64> for NTP64 {
    type Output = Self;

    #[inline]
    fn add(self, other: u64) -> Self {
        Self(self.0 + other)
    }
}

impl AddAssign<u64> for NTP64 {
    #[inline]
    fn add_assign(&mut self, other: u64) {
        *self = Self(self.0 + other);
    }
}

impl Sub for NTP64 {
    type Output = Self;

    #[inline]
    fn sub(self, other: Self) -> Self {
        Self(self.0 - other.0)
    }
}

impl fmt::Display for NTP64 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", format_rfc3339_nanos(self.to_system_time()))
    }
}

impl fmt::Debug for NTP64 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:x}", self.0)
    }
}

impl From<Duration> for NTP64 {
    fn from(duration: Duration) -> NTP64 {
        let secs = duration.as_secs();
        assert!(secs <= MAX_NB_SEC);
        let nanos: u64 = duration.subsec_nanos().into();
        NTP64((secs << 32) + ((nanos * FRAC_PER_SEC) / NANO_PER_SEC) + 1)
    }
}