diff --git a/src/timestamp.rs b/src/timestamp.rs index add62dad..fe647694 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -72,6 +72,11 @@ impl Timestamp { /// Construct a `Timestamp` from an RFC4122 timestamp and counter, as used /// in versions 1 and 6 UUIDs. + /// + /// # Overflow + /// + /// If conversion from RFC4122 ticks to the internal timestamp format would overflow + /// it will wrap. pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { #[cfg(not(any(feature = "v1", feature = "v6")))] { @@ -89,6 +94,11 @@ impl Timestamp { } /// Construct a `Timestamp` from a Unix timestamp, as used in version 7 UUIDs. + /// + /// # Overflow + /// + /// If conversion from RFC4122 ticks to the internal timestamp format would overflow + /// it will wrap. pub fn from_unix(context: impl ClockSequence, seconds: u64, nanos: u32) -> Self { #[cfg(not(any(feature = "v1", feature = "v6")))] { @@ -110,6 +120,11 @@ impl Timestamp { /// Get the value of the timestamp as an RFC4122 timestamp and counter, /// as used in versions 1 and 6 UUIDs. + /// + /// # Overflow + /// + /// If conversion from RFC4122 ticks to the internal timestamp format would overflow + /// it will wrap. #[cfg(any(feature = "v1", feature = "v6"))] pub const fn to_rfc4122(&self) -> (u64, u16) { ( @@ -119,21 +134,26 @@ impl Timestamp { } /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs. + /// + /// # Overflow + /// + /// If conversion from RFC4122 ticks to the internal timestamp format would overflow + /// it will wrap. pub const fn to_unix(&self) -> (u64, u32) { (self.seconds, self.nanos) } #[cfg(any(feature = "v1", feature = "v6"))] const fn unix_to_rfc4122_ticks(seconds: u64, nanos: u32) -> u64 { - let ticks = UUID_TICKS_BETWEEN_EPOCHS + seconds * 10_000_000 + nanos as u64 / 100; + let ticks = UUID_TICKS_BETWEEN_EPOCHS.wrapping_add(seconds.wrapping_mul(10_000_000)).wrapping_add(nanos as u64 / 100); ticks } const fn rfc4122_to_unix(ticks: u64) -> (u64, u32) { ( - (ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, - ((ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100, + ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, + (ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100, ) } @@ -268,6 +288,7 @@ fn now() -> (u64, u32) { #[wasm_bindgen] extern "C" { + // NOTE: This signature works around https://bugzilla.mozilla.org/show_bug.cgi?id=1787770 #[wasm_bindgen(js_namespace = Date, catch)] fn now() -> Result; } @@ -401,3 +422,17 @@ pub mod context { } } } + +#[cfg(all(test, any(feature = "v1", feature = "v6")))] +mod tests { + use super::*; + + #[test] + fn rfc4122_unix_wraps() { + // Ensure timestamp conversions never panic + Timestamp::unix_to_rfc4122_ticks(u64::MAX, 0); + Timestamp::unix_to_rfc4122_ticks(0, u32::MAX); + + Timestamp::rfc4122_to_unix(u64::MAX); + } +}