Xclox
C++11 header-only cross-platform date and time library with an asynchronous NTP client
packet.hpp
1 /*
2  * Copyright (c) 2024 Abdullatif Kalla.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE.txt file in the root directory of this source tree.
6  */
7 
8 #ifndef XCLOX_PACKET_HPP
9 #define XCLOX_PACKET_HPP
10 
11 #include "coder.hpp"
12 #include "timestamp.hpp"
13 
14 #include <algorithm>
15 #include <array>
16 #include <memory>
17 
18 namespace xclox {
19 
20 namespace ntp {
21 
22  namespace internal {
23  using DataType = std::array<uint8_t, 48>;
24 
25  inline bool isZeroed(const DataType& data)
26  {
27  return !std::any_of(data.cbegin(), data.cend(), [](int x) { return x; });
28  }
29 
30  inline std::shared_ptr<const DataType> pointerize(const DataType& data)
31  {
32  return isZeroed(data) ? nullptr : std::make_shared<const DataType>(data);
33  }
34  }
35 
54  class Packet {
55  public:
56  using DataType = internal::DataType;
57 
59  explicit Packet(uint8_t leap, uint8_t version, uint8_t mode, uint8_t stratum, int8_t poll, int8_t precision, uint32_t rootDelay, uint32_t rootDispersion, uint32_t referenceID, uint64_t referenceTimestamp, uint64_t originTimestamp, uint64_t receiveTimestamp, uint64_t transmitTimestamp)
60  {
61  DataType d;
62  Coder::serialize<uint8_t>(static_cast<uint8_t>(leap << 6 | version << 3 | mode), &d[0]);
63  Coder::serialize<uint8_t>(stratum, &d[1]);
64  Coder::serialize<uint8_t>(static_cast<uint8_t>(poll), &d[2]);
65  Coder::serialize<uint8_t>(static_cast<uint8_t>(precision), &d[3]);
66  Coder::serialize<uint32_t>(rootDelay, &d[4]);
67  Coder::serialize<uint32_t>(rootDispersion, &d[8]);
68  Coder::serialize<uint32_t>(referenceID, &d[12]);
69  Coder::serialize<uint64_t>(referenceTimestamp, &d[16]);
70  Coder::serialize<uint64_t>(originTimestamp, &d[24]);
71  Coder::serialize<uint64_t>(receiveTimestamp, &d[32]);
72  Coder::serialize<uint64_t>(transmitTimestamp, &d[40]);
73  m_data = internal::pointerize(d);
74  }
75 
77  explicit Packet(const DataType& data)
78  : m_data(internal::pointerize(data))
79  {
80  }
81 
83  Packet() = default;
84 
86  ~Packet() = default;
87 
89  Packet(const Packet&) = default;
90 
92  Packet(Packet&&) = default;
93 
95  Packet& operator=(const Packet&) = delete;
96 
98  Packet& operator=(Packet&&) = delete;
99 
101  bool operator==(const Packet& other) const
102  {
103  return m_data && other.m_data ? *m_data == *other.m_data : isNull() && other.isNull();
104  }
105 
107  bool operator!=(const Packet& other) const
108  {
109  return !operator==(other);
110  }
111 
113  DataType data() const
114  {
115  return m_data ? *m_data : DataType {};
116  }
117 
119  bool isNull() const
120  {
121  return !m_data || internal::isZeroed(*m_data);
122  }
123 
134  uint8_t leap() const
135  {
136  return static_cast<uint8_t>(m_data ? m_data->at(0) >> 6 : 0);
137  }
138 
140  uint8_t version() const
141  {
142  return static_cast<uint8_t>(m_data ? m_data->at(0) >> 3 & 7 : 0);
143  }
144 
159  uint8_t mode() const
160  {
161  return static_cast<uint8_t>(m_data ? m_data->at(0) & 7 : 0);
162  }
163 
175  uint8_t stratum() const
176  {
177  return static_cast<uint8_t>(m_data ? m_data->at(1) : 0);
178  }
179 
181  int8_t poll() const
182  {
183  return m_data ? static_cast<int8_t>(m_data->at(2)) : 0;
184  }
185 
187  int8_t precision() const
188  {
189  return m_data ? static_cast<int8_t>(m_data->at(3)) : 0;
190  }
191 
193  uint32_t rootDelay() const
194  {
195  return m_data ? Coder::deserialize<uint32_t>(&m_data->at(4)) : 0;
196  }
197 
199  uint32_t rootDispersion() const
200  {
201  return m_data ? Coder::deserialize<uint32_t>(&m_data->at(8)) : 0;
202  }
203 
205  uint32_t referenceID() const
206  {
207  return m_data ? Coder::deserialize<uint32_t>(&m_data->at(12)) : 0;
208  }
209 
211  uint64_t referenceTimestamp() const
212  {
213  return m_data ? Coder::deserialize<uint64_t>(&m_data->at(16)) : 0;
214  }
215 
217  uint64_t originTimestamp() const
218  {
219  return m_data ? Coder::deserialize<uint64_t>(&m_data->at(24)) : 0;
220  }
221 
223  uint64_t receiveTimestamp() const
224  {
225  return m_data ? Coder::deserialize<uint64_t>(&m_data->at(32)) : 0;
226  }
227 
229  uint64_t transmitTimestamp() const
230  {
231  return m_data ? Coder::deserialize<uint64_t>(&m_data->at(40)) : 0;
232  }
233 
240  std::chrono::system_clock::duration delay(uint64_t destination) const
241  {
242  return (Timestamp(destination - originTimestamp())) - (Timestamp(transmitTimestamp() - receiveTimestamp()));
243  }
244 
253  std::chrono::system_clock::duration offset(uint64_t destination) const
254  {
255  return ((Timestamp(receiveTimestamp()) - Timestamp(originTimestamp())) + (Timestamp(transmitTimestamp()) - Timestamp(destination))) / 2;
256  }
257 
265  std::chrono::system_clock::duration offset(const std::chrono::system_clock::time_point& destination) const
266  {
267  const auto& rawOffset = offset(Timestamp(destination.time_since_epoch() + internal::EpochDeltaSeconds).value());
268  return std::chrono::seconds(static_cast<int32_t>(std::chrono::duration_cast<std::chrono::seconds>(rawOffset).count())) + rawOffset % std::chrono::seconds(1);
269  }
270 
271  private:
272  std::shared_ptr<const internal::DataType> m_data;
273  };
274 
275 } // namespace ntp
276 
277 } // namespace xclox
278 
279 #endif // XCLOX_PACKET_HPP
Packet is an immutable raw NTP packet.
Definition: packet.hpp:54
~Packet()=default
Default destructor.
Packet(const Packet &)=default
Copy constructor.
int8_t precision() const
Returns a signed integer representing the precision of the system clock, in log2 seconds.
Definition: packet.hpp:187
bool isNull() const
Returns whether the underlying data is all zeros.
Definition: packet.hpp:119
uint64_t transmitTimestamp() const
Returns the server's time at which the packet departed to the client.
Definition: packet.hpp:229
internal::DataType DataType
Type of packet's underlying data.
Definition: packet.hpp:56
uint8_t leap() const
Returns an integer warning of an impending leap second to be inserted or deleted in the last minute o...
Definition: packet.hpp:134
uint8_t version() const
Returns an unsigned integer representing the NTP version number.
Definition: packet.hpp:140
std::chrono::system_clock::duration offset(uint64_t destination) const
Returns the time offset of the server relative to the client.
Definition: packet.hpp:253
uint32_t referenceID() const
Returns a 32-bit code identifying the particular server or reference clock.
Definition: packet.hpp:205
uint8_t mode() const
Returns an unsigned integer representing the relationship between two NTP speakers.
Definition: packet.hpp:159
Packet(Packet &&)=default
Move constructor.
uint32_t rootDispersion() const
Returns the total dispersion to the reference clock, in NTP short format.
Definition: packet.hpp:199
std::chrono::system_clock::duration delay(uint64_t destination) const
Returns the round-trip delay of the NTP packet passed from client to server and back again.
Definition: packet.hpp:240
Packet(uint8_t leap, uint8_t version, uint8_t mode, uint8_t stratum, int8_t poll, int8_t precision, uint32_t rootDelay, uint32_t rootDispersion, uint32_t referenceID, uint64_t referenceTimestamp, uint64_t originTimestamp, uint64_t receiveTimestamp, uint64_t transmitTimestamp)
Constructs a NTP packet from the given values.
Definition: packet.hpp:59
uint64_t originTimestamp() const
Returns the client's time at which the packet departed to the server.
Definition: packet.hpp:217
Packet & operator=(const Packet &)=delete
Copy assignment operator.
bool operator==(const Packet &other) const
Equality operator.
Definition: packet.hpp:101
Packet()=default
Constructs a null NTP packet that has no underlying data.
uint64_t referenceTimestamp() const
Returns the server's time at which the system clock was last set or corrected.
Definition: packet.hpp:211
Packet(const DataType &data)
Constructs a NTP packet from the given raw data buffer.
Definition: packet.hpp:77
int8_t poll() const
Returns a signed integer representing the maximum interval between successive messages,...
Definition: packet.hpp:181
bool operator!=(const Packet &other) const
Inequality operator.
Definition: packet.hpp:107
DataType data() const
Returns a raw data representation of the underlying packet.
Definition: packet.hpp:113
uint64_t receiveTimestamp() const
Returns the server's time at which the packet arrived from the client.
Definition: packet.hpp:223
uint8_t stratum() const
Returns an unsigned integer representing the level of the server in the NTP hierarchy.
Definition: packet.hpp:175
std::chrono::system_clock::duration offset(const std::chrono::system_clock::time_point &destination) const
Returns the time offset of the server relative to the client.
Definition: packet.hpp:265
Packet & operator=(Packet &&)=delete
Move assignment operator.
uint32_t rootDelay() const
Returns the total round-trip delay to the reference clock, in NTP short format.
Definition: packet.hpp:193
Timestamp is an immutable class representing a NTP timestamp.
Definition: timestamp.hpp:45
uint64_t value() const
Returns the NTP timestamp in long format.
Definition: timestamp.hpp:87