prost_types/
lib.rs

1#![doc(html_root_url = "https://docs.rs/prost-types/0.9.0")]
2
3//! Protocol Buffers well-known types.
4//!
5//! Note that the documentation for the types defined in this crate are generated from the Protobuf
6//! definitions, so code examples are not in Rust.
7//!
8//! See the [Protobuf reference][1] for more information about well-known types.
9//!
10//! [1]: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
11
12#![cfg_attr(not(feature = "std"), no_std)]
13
14use core::convert::TryFrom;
15use core::i32;
16use core::i64;
17use core::time;
18
19include!("protobuf.rs");
20pub mod compiler {
21    include!("compiler.rs");
22}
23
24// The Protobuf `Duration` and `Timestamp` types can't delegate to the standard library equivalents
25// because the Protobuf versions are signed. To make them easier to work with, `From` conversions
26// are defined in both directions.
27
28const NANOS_PER_SECOND: i32 = 1_000_000_000;
29const NANOS_MAX: i32 = NANOS_PER_SECOND - 1;
30
31impl Duration {
32    /// Normalizes the duration to a canonical format.
33    ///
34    /// Based on [`google::protobuf::util::CreateNormalized`][1].
35    /// [1]: https://github.com/google/protobuf/blob/v3.3.2/src/google/protobuf/util/time_util.cc#L79-L100
36    pub fn normalize(&mut self) {
37        // Make sure nanos is in the range.
38        if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
39            if let Some(seconds) = self
40                .seconds
41                .checked_add((self.nanos / NANOS_PER_SECOND) as i64)
42            {
43                self.seconds = seconds;
44                self.nanos %= NANOS_PER_SECOND;
45            } else if self.nanos < 0 {
46                // Negative overflow! Set to the least normal value.
47                self.seconds = i64::MIN;
48                self.nanos = -NANOS_MAX;
49            } else {
50                // Positive overflow! Set to the greatest normal value.
51                self.seconds = i64::MAX;
52                self.nanos = NANOS_MAX;
53            }
54        }
55
56        // nanos should have the same sign as seconds.
57        if self.seconds < 0 && self.nanos > 0 {
58            if let Some(seconds) = self.seconds.checked_add(1) {
59                self.seconds = seconds;
60                self.nanos -= NANOS_PER_SECOND;
61            } else {
62                // Positive overflow! Set to the greatest normal value.
63                debug_assert_eq!(self.seconds, i64::MAX);
64                self.nanos = NANOS_MAX;
65            }
66        } else if self.seconds > 0 && self.nanos < 0 {
67            if let Some(seconds) = self.seconds.checked_sub(1) {
68                self.seconds = seconds;
69                self.nanos += NANOS_PER_SECOND;
70            } else {
71                // Negative overflow! Set to the least normal value.
72                debug_assert_eq!(self.seconds, i64::MIN);
73                self.nanos = -NANOS_MAX;
74            }
75        }
76        // TODO: should this be checked?
77        // debug_assert!(self.seconds >= -315_576_000_000 && self.seconds <= 315_576_000_000,
78        //               "invalid duration: {:?}", self);
79    }
80}
81
82/// Converts a `std::time::Duration` to a `Duration`.
83impl From<time::Duration> for Duration {
84    fn from(duration: time::Duration) -> Duration {
85        let seconds = duration.as_secs();
86        let seconds = if seconds > i64::MAX as u64 {
87            i64::MAX
88        } else {
89            seconds as i64
90        };
91        let nanos = duration.subsec_nanos();
92        let nanos = if nanos > i32::MAX as u32 {
93            i32::MAX
94        } else {
95            nanos as i32
96        };
97        let mut duration = Duration { seconds, nanos };
98        duration.normalize();
99        duration
100    }
101}
102
103impl TryFrom<Duration> for time::Duration {
104    type Error = time::Duration;
105
106    /// Converts a `Duration` to a result containing a positive (`Ok`) or negative (`Err`)
107    /// `std::time::Duration`.
108    fn try_from(mut duration: Duration) -> Result<time::Duration, time::Duration> {
109        duration.normalize();
110        if duration.seconds >= 0 {
111            Ok(time::Duration::new(
112                duration.seconds as u64,
113                duration.nanos as u32,
114            ))
115        } else {
116            Err(time::Duration::new(
117                (-duration.seconds) as u64,
118                (-duration.nanos) as u32,
119            ))
120        }
121    }
122}
123
124impl Timestamp {
125    /// Normalizes the timestamp to a canonical format.
126    ///
127    /// Based on [`google::protobuf::util::CreateNormalized`][1].
128    /// [1]: https://github.com/google/protobuf/blob/v3.3.2/src/google/protobuf/util/time_util.cc#L59-L77
129    #[cfg(feature = "std")]
130    pub fn normalize(&mut self) {
131        // Make sure nanos is in the range.
132        if self.nanos <= -NANOS_PER_SECOND || self.nanos >= NANOS_PER_SECOND {
133            if let Some(seconds) = self
134                .seconds
135                .checked_add((self.nanos / NANOS_PER_SECOND) as i64)
136            {
137                self.seconds = seconds;
138                self.nanos %= NANOS_PER_SECOND;
139            } else if self.nanos < 0 {
140                // Negative overflow! Set to the earliest normal value.
141                self.seconds = i64::MIN;
142                self.nanos = 0;
143            } else {
144                // Positive overflow! Set to the latest normal value.
145                self.seconds = i64::MAX;
146                self.nanos = 999_999_999;
147            }
148        }
149
150        // For Timestamp nanos should be in the range [0, 999999999].
151        if self.nanos < 0 {
152            if let Some(seconds) = self.seconds.checked_sub(1) {
153                self.seconds = seconds;
154                self.nanos += NANOS_PER_SECOND;
155            } else {
156                // Negative overflow! Set to the earliest normal value.
157                debug_assert_eq!(self.seconds, i64::MIN);
158                self.nanos = 0;
159            }
160        }
161
162        // TODO: should this be checked?
163        // debug_assert!(self.seconds >= -62_135_596_800 && self.seconds <= 253_402_300_799,
164        //               "invalid timestamp: {:?}", self);
165    }
166}
167
168/// Implements the unstable/naive version of `Eq`: a basic equality check on the internal fields of the `Timestamp`.
169/// This implies that `normalized_ts != non_normalized_ts` even if `normalized_ts == non_normalized_ts.normalized()`.
170#[cfg(feature = "std")]
171impl Eq for Timestamp {}
172
173#[cfg(feature = "std")]
174#[allow(clippy::derive_hash_xor_eq)] // Derived logic is correct: comparing the 2 feilds for equality
175impl std::hash::Hash for Timestamp {
176    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
177        self.seconds.hash(state);
178        self.nanos.hash(state);
179    }
180}
181
182#[cfg(feature = "std")]
183impl From<std::time::SystemTime> for Timestamp {
184    fn from(system_time: std::time::SystemTime) -> Timestamp {
185        let (seconds, nanos) = match system_time.duration_since(std::time::UNIX_EPOCH) {
186            Ok(duration) => {
187                let seconds = i64::try_from(duration.as_secs()).unwrap();
188                (seconds, duration.subsec_nanos() as i32)
189            }
190            Err(error) => {
191                let duration = error.duration();
192                let seconds = i64::try_from(duration.as_secs()).unwrap();
193                let nanos = duration.subsec_nanos() as i32;
194                if nanos == 0 {
195                    (-seconds, 0)
196                } else {
197                    (-seconds - 1, 1_000_000_000 - nanos)
198                }
199            }
200        };
201        Timestamp { seconds, nanos }
202    }
203}
204
205/// Indicates that a [`Timestamp`] could not be converted to
206/// [`SystemTime`][std::time::SystemTime] because it is out of range.
207///
208/// The range of times that can be represented by `SystemTime` depends on the platform.
209/// All `Timestamp`s are likely representable on 64-bit Unix-like platforms, but
210/// other platforms, such as Windows and 32-bit Linux, may not be able to represent
211/// the full range of `Timestamp`s.
212#[cfg(feature = "std")]
213#[derive(Debug)]
214#[non_exhaustive]
215pub struct TimestampOutOfSystemRangeError {
216    pub timestamp: Timestamp,
217}
218
219#[cfg(feature = "std")]
220impl core::fmt::Display for TimestampOutOfSystemRangeError {
221    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
222        write!(
223            f,
224            "{:?} is not representable as a `SystemTime` because it is out of range",
225            self
226        )
227    }
228}
229
230#[cfg(feature = "std")]
231impl std::error::Error for TimestampOutOfSystemRangeError {}
232
233#[cfg(feature = "std")]
234impl TryFrom<Timestamp> for std::time::SystemTime {
235    type Error = TimestampOutOfSystemRangeError;
236
237    fn try_from(mut timestamp: Timestamp) -> Result<std::time::SystemTime, Self::Error> {
238        let orig_timestamp = timestamp.clone();
239        timestamp.normalize();
240
241        let system_time = if timestamp.seconds >= 0 {
242            std::time::UNIX_EPOCH.checked_add(time::Duration::from_secs(timestamp.seconds as u64))
243        } else {
244            std::time::UNIX_EPOCH
245                .checked_sub(time::Duration::from_secs((-timestamp.seconds) as u64))
246        };
247
248        let system_time = system_time.and_then(|system_time| {
249            system_time.checked_add(time::Duration::from_nanos(timestamp.nanos as u64))
250        });
251
252        system_time.ok_or(TimestampOutOfSystemRangeError {
253            timestamp: orig_timestamp,
254        })
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use std::time::{Duration, SystemTime, UNIX_EPOCH};
261
262    use proptest::prelude::*;
263
264    use super::*;
265
266    #[cfg(feature = "std")]
267    proptest! {
268        #[test]
269        fn check_system_time_roundtrip(
270            system_time in SystemTime::arbitrary(),
271        ) {
272            prop_assert_eq!(SystemTime::try_from(Timestamp::from(system_time)).unwrap(), system_time);
273        }
274
275        #[test]
276        fn check_timestamp_roundtrip_via_system_time(
277            seconds in i64::arbitrary(),
278            nanos in i32::arbitrary(),
279        ) {
280            let mut timestamp = Timestamp { seconds, nanos };
281            timestamp.normalize();
282            if let Ok(system_time) = SystemTime::try_from(timestamp.clone()) {
283                prop_assert_eq!(Timestamp::from(system_time), timestamp);
284            }
285        }
286    }
287
288    #[cfg(feature = "std")]
289    #[test]
290    fn check_timestamp_negative_seconds() {
291        // Representative tests for the case of timestamps before the UTC Epoch time:
292        // validate the expected behaviour that "negative second values with fractions
293        // must still have non-negative nanos values that count forward in time"
294        // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp
295        //
296        // To ensure cross-platform compatibility, all nanosecond values in these
297        // tests are in minimum 100 ns increments.  This does not affect the general
298        // character of the behaviour being tested, but ensures that the tests are
299        // valid for both POSIX (1 ns precision) and Windows (100 ns precision).
300        assert_eq!(
301            Timestamp::from(UNIX_EPOCH - Duration::new(1_001, 0)),
302            Timestamp {
303                seconds: -1_001,
304                nanos: 0
305            }
306        );
307        assert_eq!(
308            Timestamp::from(UNIX_EPOCH - Duration::new(0, 999_999_900)),
309            Timestamp {
310                seconds: -1,
311                nanos: 100
312            }
313        );
314        assert_eq!(
315            Timestamp::from(UNIX_EPOCH - Duration::new(2_001_234, 12_300)),
316            Timestamp {
317                seconds: -2_001_235,
318                nanos: 999_987_700
319            }
320        );
321        assert_eq!(
322            Timestamp::from(UNIX_EPOCH - Duration::new(768, 65_432_100)),
323            Timestamp {
324                seconds: -769,
325                nanos: 934_567_900
326            }
327        );
328    }
329
330    #[cfg(all(unix, feature = "std"))]
331    #[test]
332    fn check_timestamp_negative_seconds_1ns() {
333        // UNIX-only test cases with 1 ns precision
334        assert_eq!(
335            Timestamp::from(UNIX_EPOCH - Duration::new(0, 999_999_999)),
336            Timestamp {
337                seconds: -1,
338                nanos: 1
339            }
340        );
341        assert_eq!(
342            Timestamp::from(UNIX_EPOCH - Duration::new(1_234_567, 123)),
343            Timestamp {
344                seconds: -1_234_568,
345                nanos: 999_999_877
346            }
347        );
348        assert_eq!(
349            Timestamp::from(UNIX_EPOCH - Duration::new(890, 987_654_321)),
350            Timestamp {
351                seconds: -891,
352                nanos: 12_345_679
353            }
354        );
355    }
356
357    #[test]
358    fn check_duration_normalize() {
359        #[rustfmt::skip] // Don't mangle the table formatting.
360        let cases = [
361            // --- Table of test cases ---
362            //        test seconds      test nanos  expected seconds  expected nanos
363            (line!(),            0,              0,                0,              0),
364            (line!(),            1,              1,                1,              1),
365            (line!(),           -1,             -1,               -1,             -1),
366            (line!(),            0,    999_999_999,                0,    999_999_999),
367            (line!(),            0,   -999_999_999,                0,   -999_999_999),
368            (line!(),            0,  1_000_000_000,                1,              0),
369            (line!(),            0, -1_000_000_000,               -1,              0),
370            (line!(),            0,  1_000_000_001,                1,              1),
371            (line!(),            0, -1_000_000_001,               -1,             -1),
372            (line!(),           -1,              1,                0,   -999_999_999),
373            (line!(),            1,             -1,                0,    999_999_999),
374            (line!(),           -1,  1_000_000_000,                0,              0),
375            (line!(),            1, -1_000_000_000,                0,              0),
376            (line!(), i64::MIN    ,              0,     i64::MIN    ,              0),
377            (line!(), i64::MIN + 1,              0,     i64::MIN + 1,              0),
378            (line!(), i64::MIN    ,              1,     i64::MIN + 1,   -999_999_999),
379            (line!(), i64::MIN    ,  1_000_000_000,     i64::MIN + 1,              0),
380            (line!(), i64::MIN    , -1_000_000_000,     i64::MIN    ,   -999_999_999),
381            (line!(), i64::MIN + 1, -1_000_000_000,     i64::MIN    ,              0),
382            (line!(), i64::MIN + 2, -1_000_000_000,     i64::MIN + 1,              0),
383            (line!(), i64::MIN    , -1_999_999_998,     i64::MIN    ,   -999_999_999),
384            (line!(), i64::MIN + 1, -1_999_999_998,     i64::MIN    ,   -999_999_998),
385            (line!(), i64::MIN + 2, -1_999_999_998,     i64::MIN + 1,   -999_999_998),
386            (line!(), i64::MIN    , -1_999_999_999,     i64::MIN    ,   -999_999_999),
387            (line!(), i64::MIN + 1, -1_999_999_999,     i64::MIN    ,   -999_999_999),
388            (line!(), i64::MIN + 2, -1_999_999_999,     i64::MIN + 1,   -999_999_999),
389            (line!(), i64::MIN    , -2_000_000_000,     i64::MIN    ,   -999_999_999),
390            (line!(), i64::MIN + 1, -2_000_000_000,     i64::MIN    ,   -999_999_999),
391            (line!(), i64::MIN + 2, -2_000_000_000,     i64::MIN    ,              0),
392            (line!(), i64::MIN    ,   -999_999_998,     i64::MIN    ,   -999_999_998),
393            (line!(), i64::MIN + 1,   -999_999_998,     i64::MIN + 1,   -999_999_998),
394            (line!(), i64::MAX    ,              0,     i64::MAX    ,              0),
395            (line!(), i64::MAX - 1,              0,     i64::MAX - 1,              0),
396            (line!(), i64::MAX    ,             -1,     i64::MAX - 1,    999_999_999),
397            (line!(), i64::MAX    ,  1_000_000_000,     i64::MAX    ,    999_999_999),
398            (line!(), i64::MAX - 1,  1_000_000_000,     i64::MAX    ,              0),
399            (line!(), i64::MAX - 2,  1_000_000_000,     i64::MAX - 1,              0),
400            (line!(), i64::MAX    ,  1_999_999_998,     i64::MAX    ,    999_999_999),
401            (line!(), i64::MAX - 1,  1_999_999_998,     i64::MAX    ,    999_999_998),
402            (line!(), i64::MAX - 2,  1_999_999_998,     i64::MAX - 1,    999_999_998),
403            (line!(), i64::MAX    ,  1_999_999_999,     i64::MAX    ,    999_999_999),
404            (line!(), i64::MAX - 1,  1_999_999_999,     i64::MAX    ,    999_999_999),
405            (line!(), i64::MAX - 2,  1_999_999_999,     i64::MAX - 1,    999_999_999),
406            (line!(), i64::MAX    ,  2_000_000_000,     i64::MAX    ,    999_999_999),
407            (line!(), i64::MAX - 1,  2_000_000_000,     i64::MAX    ,    999_999_999),
408            (line!(), i64::MAX - 2,  2_000_000_000,     i64::MAX    ,              0),
409            (line!(), i64::MAX    ,    999_999_998,     i64::MAX    ,    999_999_998),
410            (line!(), i64::MAX - 1,    999_999_998,     i64::MAX - 1,    999_999_998),
411        ];
412
413        for case in cases.iter() {
414            let mut test_duration = crate::Duration {
415                seconds: case.1,
416                nanos: case.2,
417            };
418            test_duration.normalize();
419
420            assert_eq!(
421                test_duration,
422                crate::Duration {
423                    seconds: case.3,
424                    nanos: case.4,
425                },
426                "test case on line {} doesn't match",
427                case.0,
428            );
429        }
430    }
431
432    #[cfg(feature = "std")]
433    #[test]
434    fn check_timestamp_normalize() {
435        // Make sure that `Timestamp::normalize` behaves correctly on and near overflow.
436        #[rustfmt::skip] // Don't mangle the table formatting.
437        let cases = [
438            // --- Table of test cases ---
439            //        test seconds      test nanos  expected seconds  expected nanos
440            (line!(),            0,              0,                0,              0),
441            (line!(),            1,              1,                1,              1),
442            (line!(),           -1,             -1,               -2,    999_999_999),
443            (line!(),            0,    999_999_999,                0,    999_999_999),
444            (line!(),            0,   -999_999_999,               -1,              1),
445            (line!(),            0,  1_000_000_000,                1,              0),
446            (line!(),            0, -1_000_000_000,               -1,              0),
447            (line!(),            0,  1_000_000_001,                1,              1),
448            (line!(),            0, -1_000_000_001,               -2,    999_999_999),
449            (line!(),           -1,              1,               -1,              1),
450            (line!(),            1,             -1,                0,    999_999_999),
451            (line!(),           -1,  1_000_000_000,                0,              0),
452            (line!(),            1, -1_000_000_000,                0,              0),
453            (line!(), i64::MIN    ,              0,     i64::MIN    ,              0),
454            (line!(), i64::MIN + 1,              0,     i64::MIN + 1,              0),
455            (line!(), i64::MIN    ,              1,     i64::MIN    ,              1),
456            (line!(), i64::MIN    ,  1_000_000_000,     i64::MIN + 1,              0),
457            (line!(), i64::MIN    , -1_000_000_000,     i64::MIN    ,              0),
458            (line!(), i64::MIN + 1, -1_000_000_000,     i64::MIN    ,              0),
459            (line!(), i64::MIN + 2, -1_000_000_000,     i64::MIN + 1,              0),
460            (line!(), i64::MIN    , -1_999_999_998,     i64::MIN    ,              0),
461            (line!(), i64::MIN + 1, -1_999_999_998,     i64::MIN    ,              0),
462            (line!(), i64::MIN + 2, -1_999_999_998,     i64::MIN    ,              2),
463            (line!(), i64::MIN    , -1_999_999_999,     i64::MIN    ,              0),
464            (line!(), i64::MIN + 1, -1_999_999_999,     i64::MIN    ,              0),
465            (line!(), i64::MIN + 2, -1_999_999_999,     i64::MIN    ,              1),
466            (line!(), i64::MIN    , -2_000_000_000,     i64::MIN    ,              0),
467            (line!(), i64::MIN + 1, -2_000_000_000,     i64::MIN    ,              0),
468            (line!(), i64::MIN + 2, -2_000_000_000,     i64::MIN    ,              0),
469            (line!(), i64::MIN    ,   -999_999_998,     i64::MIN    ,              0),
470            (line!(), i64::MIN + 1,   -999_999_998,     i64::MIN    ,              2),
471            (line!(), i64::MAX    ,              0,     i64::MAX    ,              0),
472            (line!(), i64::MAX - 1,              0,     i64::MAX - 1,              0),
473            (line!(), i64::MAX    ,             -1,     i64::MAX - 1,    999_999_999),
474            (line!(), i64::MAX    ,  1_000_000_000,     i64::MAX    ,    999_999_999),
475            (line!(), i64::MAX - 1,  1_000_000_000,     i64::MAX    ,              0),
476            (line!(), i64::MAX - 2,  1_000_000_000,     i64::MAX - 1,              0),
477            (line!(), i64::MAX    ,  1_999_999_998,     i64::MAX    ,    999_999_999),
478            (line!(), i64::MAX - 1,  1_999_999_998,     i64::MAX    ,    999_999_998),
479            (line!(), i64::MAX - 2,  1_999_999_998,     i64::MAX - 1,    999_999_998),
480            (line!(), i64::MAX    ,  1_999_999_999,     i64::MAX    ,    999_999_999),
481            (line!(), i64::MAX - 1,  1_999_999_999,     i64::MAX    ,    999_999_999),
482            (line!(), i64::MAX - 2,  1_999_999_999,     i64::MAX - 1,    999_999_999),
483            (line!(), i64::MAX    ,  2_000_000_000,     i64::MAX    ,    999_999_999),
484            (line!(), i64::MAX - 1,  2_000_000_000,     i64::MAX    ,    999_999_999),
485            (line!(), i64::MAX - 2,  2_000_000_000,     i64::MAX    ,              0),
486            (line!(), i64::MAX    ,    999_999_998,     i64::MAX    ,    999_999_998),
487            (line!(), i64::MAX - 1,    999_999_998,     i64::MAX - 1,    999_999_998),
488        ];
489
490        for case in cases.iter() {
491            let mut test_timestamp = crate::Timestamp {
492                seconds: case.1,
493                nanos: case.2,
494            };
495            test_timestamp.normalize();
496
497            assert_eq!(
498                test_timestamp,
499                crate::Timestamp {
500                    seconds: case.3,
501                    nanos: case.4,
502                },
503                "test case on line {} doesn't match",
504                case.0,
505            );
506        }
507    }
508}