1 #![doc(html_root_url = "https://docs.rs/prost-types/0.8.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 14 use core::convert::TryFrom; 15 use core::i32; 16 use core::i64; 17 use core::time; 18 19 include!("protobuf.rs"); 20 pub 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 28 const NANOS_PER_SECOND: i32 = 1_000_000_000; 29 const NANOS_MAX: i32 = NANOS_PER_SECOND - 1; 30 31 impl 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 normalize(&mut self)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`. 83 impl From<time::Duration> for Duration { from(duration: time::Duration) -> Duration84 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 103 impl 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`. try_from(mut duration: Duration) -> Result<time::Duration, 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 124 impl 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")] normalize(&mut self)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 #[cfg(feature = "std")] 169 impl From<std::time::SystemTime> for Timestamp { from(system_time: std::time::SystemTime) -> Timestamp170 fn from(system_time: std::time::SystemTime) -> Timestamp { 171 let (seconds, nanos) = match system_time.duration_since(std::time::UNIX_EPOCH) { 172 Ok(duration) => { 173 let seconds = i64::try_from(duration.as_secs()).unwrap(); 174 (seconds, duration.subsec_nanos() as i32) 175 } 176 Err(error) => { 177 let duration = error.duration(); 178 let seconds = i64::try_from(duration.as_secs()).unwrap(); 179 let nanos = duration.subsec_nanos() as i32; 180 if nanos == 0 { 181 (-seconds, 0) 182 } else { 183 (-seconds - 1, 1_000_000_000 - nanos) 184 } 185 } 186 }; 187 Timestamp { seconds, nanos } 188 } 189 } 190 191 /// Indicates that a [`Timestamp`] could not be converted to 192 /// [`SystemTime`][std::time::SystemTime] because it is out of range. 193 /// 194 /// The range of times that can be represented by `SystemTime` depends on the platform. 195 /// All `Timestamp`s are likely representable on 64-bit Unix-like platforms, but 196 /// other platforms, such as Windows and 32-bit Linux, may not be able to represent 197 /// the full range of `Timestamp`s. 198 #[cfg(feature = "std")] 199 #[derive(Debug)] 200 #[non_exhaustive] 201 pub struct TimestampOutOfSystemRangeError { 202 pub timestamp: Timestamp, 203 } 204 205 #[cfg(feature = "std")] 206 impl core::fmt::Display for TimestampOutOfSystemRangeError { fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 208 write!( 209 f, 210 "{:?} is not representable as a `SystemTime` because it is out of range", 211 self 212 ) 213 } 214 } 215 216 #[cfg(feature = "std")] 217 impl std::error::Error for TimestampOutOfSystemRangeError {} 218 219 #[cfg(feature = "std")] 220 impl TryFrom<Timestamp> for std::time::SystemTime { 221 type Error = TimestampOutOfSystemRangeError; 222 try_from(mut timestamp: Timestamp) -> Result<std::time::SystemTime, Self::Error>223 fn try_from(mut timestamp: Timestamp) -> Result<std::time::SystemTime, Self::Error> { 224 let orig_timestamp = timestamp.clone(); 225 timestamp.normalize(); 226 227 let system_time = if timestamp.seconds >= 0 { 228 std::time::UNIX_EPOCH.checked_add(time::Duration::from_secs(timestamp.seconds as u64)) 229 } else { 230 std::time::UNIX_EPOCH 231 .checked_sub(time::Duration::from_secs((-timestamp.seconds) as u64)) 232 }; 233 234 let system_time = system_time.and_then(|system_time| { 235 system_time.checked_add(time::Duration::from_nanos(timestamp.nanos as u64)) 236 }); 237 238 system_time.ok_or(TimestampOutOfSystemRangeError { 239 timestamp: orig_timestamp, 240 }) 241 } 242 } 243 244 #[cfg(test)] 245 mod tests { 246 use std::time::{Duration, SystemTime, UNIX_EPOCH}; 247 248 use proptest::prelude::*; 249 250 use super::*; 251 252 #[cfg(feature = "std")] 253 proptest! { 254 #[test] 255 fn check_system_time_roundtrip( 256 system_time in SystemTime::arbitrary(), 257 ) { 258 prop_assert_eq!(SystemTime::try_from(Timestamp::from(system_time)).unwrap(), system_time); 259 } 260 261 #[test] 262 fn check_timestamp_roundtrip_via_system_time( 263 seconds in i64::arbitrary(), 264 nanos in i32::arbitrary(), 265 ) { 266 let mut timestamp = Timestamp { seconds, nanos }; 267 timestamp.normalize(); 268 if let Ok(system_time) = SystemTime::try_from(timestamp.clone()) { 269 prop_assert_eq!(Timestamp::from(system_time), timestamp); 270 } 271 } 272 } 273 274 #[cfg(feature = "std")] 275 #[test] check_timestamp_negative_seconds()276 fn check_timestamp_negative_seconds() { 277 // Representative tests for the case of timestamps before the UTC Epoch time: 278 // validate the expected behaviour that "negative second values with fractions 279 // must still have non-negative nanos values that count forward in time" 280 // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp 281 // 282 // To ensure cross-platform compatibility, all nanosecond values in these 283 // tests are in minimum 100 ns increments. This does not affect the general 284 // character of the behaviour being tested, but ensures that the tests are 285 // valid for both POSIX (1 ns precision) and Windows (100 ns precision). 286 assert_eq!( 287 Timestamp::from(UNIX_EPOCH - Duration::new(1_001, 0)), 288 Timestamp { 289 seconds: -1_001, 290 nanos: 0 291 } 292 ); 293 assert_eq!( 294 Timestamp::from(UNIX_EPOCH - Duration::new(0, 999_999_900)), 295 Timestamp { 296 seconds: -1, 297 nanos: 100 298 } 299 ); 300 assert_eq!( 301 Timestamp::from(UNIX_EPOCH - Duration::new(2_001_234, 12_300)), 302 Timestamp { 303 seconds: -2_001_235, 304 nanos: 999_987_700 305 } 306 ); 307 assert_eq!( 308 Timestamp::from(UNIX_EPOCH - Duration::new(768, 65_432_100)), 309 Timestamp { 310 seconds: -769, 311 nanos: 934_567_900 312 } 313 ); 314 } 315 316 #[cfg(all(unix, feature = "std"))] 317 #[test] check_timestamp_negative_seconds_1ns()318 fn check_timestamp_negative_seconds_1ns() { 319 // UNIX-only test cases with 1 ns precision 320 assert_eq!( 321 Timestamp::from(UNIX_EPOCH - Duration::new(0, 999_999_999)), 322 Timestamp { 323 seconds: -1, 324 nanos: 1 325 } 326 ); 327 assert_eq!( 328 Timestamp::from(UNIX_EPOCH - Duration::new(1_234_567, 123)), 329 Timestamp { 330 seconds: -1_234_568, 331 nanos: 999_999_877 332 } 333 ); 334 assert_eq!( 335 Timestamp::from(UNIX_EPOCH - Duration::new(890, 987_654_321)), 336 Timestamp { 337 seconds: -891, 338 nanos: 12_345_679 339 } 340 ); 341 } 342 343 #[test] check_duration_normalize()344 fn check_duration_normalize() { 345 #[rustfmt::skip] // Don't mangle the table formatting. 346 let cases = [ 347 // --- Table of test cases --- 348 // test seconds test nanos expected seconds expected nanos 349 (line!(), 0, 0, 0, 0), 350 (line!(), 1, 1, 1, 1), 351 (line!(), -1, -1, -1, -1), 352 (line!(), 0, 999_999_999, 0, 999_999_999), 353 (line!(), 0, -999_999_999, 0, -999_999_999), 354 (line!(), 0, 1_000_000_000, 1, 0), 355 (line!(), 0, -1_000_000_000, -1, 0), 356 (line!(), 0, 1_000_000_001, 1, 1), 357 (line!(), 0, -1_000_000_001, -1, -1), 358 (line!(), -1, 1, 0, -999_999_999), 359 (line!(), 1, -1, 0, 999_999_999), 360 (line!(), -1, 1_000_000_000, 0, 0), 361 (line!(), 1, -1_000_000_000, 0, 0), 362 (line!(), i64::MIN , 0, i64::MIN , 0), 363 (line!(), i64::MIN + 1, 0, i64::MIN + 1, 0), 364 (line!(), i64::MIN , 1, i64::MIN + 1, -999_999_999), 365 (line!(), i64::MIN , 1_000_000_000, i64::MIN + 1, 0), 366 (line!(), i64::MIN , -1_000_000_000, i64::MIN , -999_999_999), 367 (line!(), i64::MIN + 1, -1_000_000_000, i64::MIN , 0), 368 (line!(), i64::MIN + 2, -1_000_000_000, i64::MIN + 1, 0), 369 (line!(), i64::MIN , -1_999_999_998, i64::MIN , -999_999_999), 370 (line!(), i64::MIN + 1, -1_999_999_998, i64::MIN , -999_999_998), 371 (line!(), i64::MIN + 2, -1_999_999_998, i64::MIN + 1, -999_999_998), 372 (line!(), i64::MIN , -1_999_999_999, i64::MIN , -999_999_999), 373 (line!(), i64::MIN + 1, -1_999_999_999, i64::MIN , -999_999_999), 374 (line!(), i64::MIN + 2, -1_999_999_999, i64::MIN + 1, -999_999_999), 375 (line!(), i64::MIN , -2_000_000_000, i64::MIN , -999_999_999), 376 (line!(), i64::MIN + 1, -2_000_000_000, i64::MIN , -999_999_999), 377 (line!(), i64::MIN + 2, -2_000_000_000, i64::MIN , 0), 378 (line!(), i64::MIN , -999_999_998, i64::MIN , -999_999_998), 379 (line!(), i64::MIN + 1, -999_999_998, i64::MIN + 1, -999_999_998), 380 (line!(), i64::MAX , 0, i64::MAX , 0), 381 (line!(), i64::MAX - 1, 0, i64::MAX - 1, 0), 382 (line!(), i64::MAX , -1, i64::MAX - 1, 999_999_999), 383 (line!(), i64::MAX , 1_000_000_000, i64::MAX , 999_999_999), 384 (line!(), i64::MAX - 1, 1_000_000_000, i64::MAX , 0), 385 (line!(), i64::MAX - 2, 1_000_000_000, i64::MAX - 1, 0), 386 (line!(), i64::MAX , 1_999_999_998, i64::MAX , 999_999_999), 387 (line!(), i64::MAX - 1, 1_999_999_998, i64::MAX , 999_999_998), 388 (line!(), i64::MAX - 2, 1_999_999_998, i64::MAX - 1, 999_999_998), 389 (line!(), i64::MAX , 1_999_999_999, i64::MAX , 999_999_999), 390 (line!(), i64::MAX - 1, 1_999_999_999, i64::MAX , 999_999_999), 391 (line!(), i64::MAX - 2, 1_999_999_999, i64::MAX - 1, 999_999_999), 392 (line!(), i64::MAX , 2_000_000_000, i64::MAX , 999_999_999), 393 (line!(), i64::MAX - 1, 2_000_000_000, i64::MAX , 999_999_999), 394 (line!(), i64::MAX - 2, 2_000_000_000, i64::MAX , 0), 395 (line!(), i64::MAX , 999_999_998, i64::MAX , 999_999_998), 396 (line!(), i64::MAX - 1, 999_999_998, i64::MAX - 1, 999_999_998), 397 ]; 398 399 for case in cases.iter() { 400 let mut test_duration = crate::Duration { 401 seconds: case.1, 402 nanos: case.2, 403 }; 404 test_duration.normalize(); 405 406 assert_eq!( 407 test_duration, 408 crate::Duration { 409 seconds: case.3, 410 nanos: case.4, 411 }, 412 "test case on line {} doesn't match", 413 case.0, 414 ); 415 } 416 } 417 418 #[cfg(feature = "std")] 419 #[test] check_timestamp_normalize()420 fn check_timestamp_normalize() { 421 // Make sure that `Timestamp::normalize` behaves correctly on and near overflow. 422 #[rustfmt::skip] // Don't mangle the table formatting. 423 let cases = [ 424 // --- Table of test cases --- 425 // test seconds test nanos expected seconds expected nanos 426 (line!(), 0, 0, 0, 0), 427 (line!(), 1, 1, 1, 1), 428 (line!(), -1, -1, -2, 999_999_999), 429 (line!(), 0, 999_999_999, 0, 999_999_999), 430 (line!(), 0, -999_999_999, -1, 1), 431 (line!(), 0, 1_000_000_000, 1, 0), 432 (line!(), 0, -1_000_000_000, -1, 0), 433 (line!(), 0, 1_000_000_001, 1, 1), 434 (line!(), 0, -1_000_000_001, -2, 999_999_999), 435 (line!(), -1, 1, -1, 1), 436 (line!(), 1, -1, 0, 999_999_999), 437 (line!(), -1, 1_000_000_000, 0, 0), 438 (line!(), 1, -1_000_000_000, 0, 0), 439 (line!(), i64::MIN , 0, i64::MIN , 0), 440 (line!(), i64::MIN + 1, 0, i64::MIN + 1, 0), 441 (line!(), i64::MIN , 1, i64::MIN , 1), 442 (line!(), i64::MIN , 1_000_000_000, i64::MIN + 1, 0), 443 (line!(), i64::MIN , -1_000_000_000, i64::MIN , 0), 444 (line!(), i64::MIN + 1, -1_000_000_000, i64::MIN , 0), 445 (line!(), i64::MIN + 2, -1_000_000_000, i64::MIN + 1, 0), 446 (line!(), i64::MIN , -1_999_999_998, i64::MIN , 0), 447 (line!(), i64::MIN + 1, -1_999_999_998, i64::MIN , 0), 448 (line!(), i64::MIN + 2, -1_999_999_998, i64::MIN , 2), 449 (line!(), i64::MIN , -1_999_999_999, i64::MIN , 0), 450 (line!(), i64::MIN + 1, -1_999_999_999, i64::MIN , 0), 451 (line!(), i64::MIN + 2, -1_999_999_999, i64::MIN , 1), 452 (line!(), i64::MIN , -2_000_000_000, i64::MIN , 0), 453 (line!(), i64::MIN + 1, -2_000_000_000, i64::MIN , 0), 454 (line!(), i64::MIN + 2, -2_000_000_000, i64::MIN , 0), 455 (line!(), i64::MIN , -999_999_998, i64::MIN , 0), 456 (line!(), i64::MIN + 1, -999_999_998, i64::MIN , 2), 457 (line!(), i64::MAX , 0, i64::MAX , 0), 458 (line!(), i64::MAX - 1, 0, i64::MAX - 1, 0), 459 (line!(), i64::MAX , -1, i64::MAX - 1, 999_999_999), 460 (line!(), i64::MAX , 1_000_000_000, i64::MAX , 999_999_999), 461 (line!(), i64::MAX - 1, 1_000_000_000, i64::MAX , 0), 462 (line!(), i64::MAX - 2, 1_000_000_000, i64::MAX - 1, 0), 463 (line!(), i64::MAX , 1_999_999_998, i64::MAX , 999_999_999), 464 (line!(), i64::MAX - 1, 1_999_999_998, i64::MAX , 999_999_998), 465 (line!(), i64::MAX - 2, 1_999_999_998, i64::MAX - 1, 999_999_998), 466 (line!(), i64::MAX , 1_999_999_999, i64::MAX , 999_999_999), 467 (line!(), i64::MAX - 1, 1_999_999_999, i64::MAX , 999_999_999), 468 (line!(), i64::MAX - 2, 1_999_999_999, i64::MAX - 1, 999_999_999), 469 (line!(), i64::MAX , 2_000_000_000, i64::MAX , 999_999_999), 470 (line!(), i64::MAX - 1, 2_000_000_000, i64::MAX , 999_999_999), 471 (line!(), i64::MAX - 2, 2_000_000_000, i64::MAX , 0), 472 (line!(), i64::MAX , 999_999_998, i64::MAX , 999_999_998), 473 (line!(), i64::MAX - 1, 999_999_998, i64::MAX - 1, 999_999_998), 474 ]; 475 476 for case in cases.iter() { 477 let mut test_timestamp = crate::Timestamp { 478 seconds: case.1, 479 nanos: case.2, 480 }; 481 test_timestamp.normalize(); 482 483 assert_eq!( 484 test_timestamp, 485 crate::Timestamp { 486 seconds: case.3, 487 nanos: case.4, 488 }, 489 "test case on line {} doesn't match", 490 case.0, 491 ); 492 } 493 } 494 } 495