statime_wire/common/
timestamp.rs1use statime_base::TAI;
2
3use crate::Error;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
10pub struct Timestamp {
11 seconds: u64,
14 nanos: u32,
17}
18
19impl Timestamp {
20 pub fn new(seconds: u64, nanos: u32) -> Result<Self, Error> {
28 if seconds >= (1 << 48) || nanos >= 1_000_000_000 {
29 Err(Error::Invalid)
30 } else {
31 Ok(Self { seconds, nanos })
32 }
33 }
34
35 #[must_use]
37 pub fn seconds(self) -> u64 {
38 self.seconds
39 }
40
41 #[must_use]
43 pub fn nanos(self) -> u32 {
44 self.nanos
45 }
46
47 pub fn try_set_seconds(&mut self, seconds: u64) -> Result<(), Error> {
52 if seconds >= (1 << 48) {
53 Err(Error::Invalid)
54 } else {
55 self.seconds = seconds;
56 Ok(())
57 }
58 }
59
60 pub fn try_set_nanos(&mut self, nanos: u32) -> Result<(), Error> {
65 if nanos >= 1_000_000_000 {
66 Err(Error::Invalid)
67 } else {
68 self.nanos = nanos;
69 Ok(())
70 }
71 }
72
73 pub fn serialize(self, buffer: &mut [u8]) -> Result<(), Error> {
78 buffer
79 .get_mut(0..6)
80 .ok_or(Error::BufferTooShort)?
81 .copy_from_slice(&self.seconds.to_be_bytes()[2..8]);
82 buffer
83 .get_mut(6..10)
84 .ok_or(Error::BufferTooShort)?
85 .copy_from_slice(&self.nanos.to_be_bytes());
86 Ok(())
87 }
88
89 #[allow(
95 clippy::missing_panics_doc,
96 reason = "Clippy can't tell the try_into will never fail"
97 )]
98 pub fn deserialize(buffer: &[u8]) -> Result<Self, Error> {
99 let mut seconds_buffer = [0; 8];
100 seconds_buffer[2..8].copy_from_slice(buffer.get(0..6).ok_or(Error::BufferTooShort)?);
101
102 let nanos = u32::from_be_bytes(
103 buffer
104 .get(6..10)
105 .ok_or(Error::BufferTooShort)?
106 .try_into()
107 .unwrap(),
108 );
109
110 if nanos > 1_000_000_000 {
111 return Err(Error::Invalid);
112 }
113
114 Ok(Self {
115 seconds: u64::from_be_bytes(seconds_buffer),
116 nanos,
117 })
118 }
119}
120
121impl From<Timestamp> for statime_base::Timestamp<TAI> {
122 fn from(value: Timestamp) -> Self {
123 statime_base::Timestamp::from_seconds_nanos_since_unix_epoch(value.seconds, value.nanos)
124 }
125}
126
127impl TryFrom<statime_base::Timestamp<TAI>> for Timestamp {
128 type Error = Error;
129
130 fn try_from(value: statime_base::Timestamp<TAI>) -> Result<Self, Self::Error> {
131 let nanos = (value - statime_base::Timestamp::UNIX_EPOCH).as_nanos();
132 let seconds = nanos.div_euclid(1_000_000_000);
133 let nanos = nanos.rem_euclid(1_000_000_000);
134 Timestamp::new(
135 seconds.try_into().map_err(|_| Error::Invalid)?,
136 nanos.try_into().map_err(|_| Error::Invalid)?,
137 )
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn timestamp_wireformat() {
147 let representations = [
148 (
149 [0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01u8],
150 Timestamp {
151 seconds: 0x0000_0000_0002,
152 nanos: 0x0000_0001,
153 },
154 ),
155 (
156 [0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01u8],
157 Timestamp {
158 seconds: 0x1000_0000_0002,
159 nanos: 0x1000_0001,
160 },
161 ),
162 ];
163
164 for (byte_representation, object_representation) in representations {
165 let mut serialization_buffer = [0; 10];
167 object_representation
168 .serialize(&mut serialization_buffer)
169 .unwrap();
170 assert_eq!(serialization_buffer, byte_representation);
171
172 let deserialized_data = Timestamp::deserialize(&byte_representation).unwrap();
174 assert_eq!(deserialized_data, object_representation);
175 }
176 }
177}