From 13f785d2025c8669bc348eb99d0017550b7e764f Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Mon, 22 Aug 2022 17:57:48 -0700 Subject: [PATCH] hacking the API to be backwards compatible --- src/lib.rs | 30 +++++++++--------- src/timestamp.rs | 80 +++++++++++++++++++++++++++++++++++++++++------- src/v1.rs | 41 +++++++++---------------- src/v6.rs | 46 +++++++++++----------------- src/v7.rs | 19 ++++++++++-- 5 files changed, 133 insertions(+), 83 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 862b0864..43143e0e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -879,9 +879,7 @@ impl Uuid { /// value into more commonly-used formats, such as a unix timestamp. /// /// [`Timestamp`]: v1/struct.Timestamp.html - pub const fn get_timestamp( - &self, - ) -> Option<(crate::timestamp::Timestamp, u16)> { + pub const fn get_timestamp(&self) -> Option { match self.get_version() { Some(Version::Mac) => { let bytes = self.as_bytes(); @@ -897,10 +895,7 @@ impl Uuid { let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); - Some(( - crate::timestamp::Timestamp::from_rfc4122(ticks), - counter, - )) + Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter)) } Some(Version::SortMac) => { let bytes = self.as_bytes(); @@ -916,10 +911,7 @@ impl Uuid { let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); - Some(( - crate::timestamp::Timestamp::from_rfc4122(ticks), - counter, - )) + Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter)) } Some(Version::SortRand) => { let bytes = self.as_bytes(); @@ -931,10 +923,18 @@ impl Uuid { | (bytes[5] as u64); let seconds = millis / 1000; let nanos = ((millis % 1000) * 1_000_000) as u32; - Some(( - crate::timestamp::Timestamp::from_unix(seconds, nanos), - 0, - )) + #[cfg(any(feature = "v1", feature = "v6"))] + { + Some(Timestamp { + seconds, + nanos, + counter: 0, + }) + } + #[cfg(not(any(feature = "v1", feature = "v6")))] + { + Some(Timestamp { seconds, nanos }) + } } _ => None, } diff --git a/src/timestamp.rs b/src/timestamp.rs index cb4db0bc..5f2d8d7b 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -10,6 +10,8 @@ pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; pub struct Timestamp { pub(crate) seconds: u64, pub(crate) nanos: u32, + #[cfg(any(feature = "v1", feature = "v6"))] + pub(crate) counter: u16, } impl Timestamp { @@ -20,9 +22,21 @@ impl Timestamp { /// as the number of 100-nanosecond intervals elapsed since 00:00:00.00, /// 15 Oct 1582, "the date of the Gregorian reform of the Christian /// calendar." - pub const fn from_rfc4122(ticks: u64) -> Self { + pub const fn from_rfc4122(ticks: u64, _counter: u16) -> Self { let (seconds, nanos) = Self::rfc4122_to_unix(ticks); - Timestamp { seconds, nanos } + + #[cfg(any(feature = "v1", feature = "v6"))] + { + Timestamp { + seconds, + nanos, + counter: _counter, + } + } + #[cfg(not(any(feature = "v1", feature = "v6")))] + { + Timestamp { seconds, nanos } + } } /// Construct a `Timestamp` from a unix timestamp @@ -33,13 +47,29 @@ impl Timestamp { /// `u32` fields representing the seconds, and "subsecond" or fractional /// nanoseconds elapsed since the timestamp's second began, /// respectively. - pub const fn from_unix(seconds: u64, nanos: u32) -> Self { - Timestamp { seconds, nanos } + pub fn from_unix( + _context: impl ClockSequence, + seconds: u64, + nanos: u32, + ) -> Self { + #[cfg(any(feature = "v1", feature = "v6"))] + { + let counter = _context.generate_sequence(seconds, nanos); + Timestamp { + seconds, + nanos, + counter, + } + } + #[cfg(not(any(feature = "v1", feature = "v6")))] + { + Timestamp { seconds, nanos } + } } /// Construct a `Timestamp` from the current time of day /// according to Rust's SystemTime - #[cfg(feature = "std")] + #[cfg(all(feature = "std", not(any(feature = "v1", feature = "v6"))))] pub fn now() -> Self { let dur = std::time::SystemTime::UNIX_EPOCH .elapsed() @@ -49,14 +79,30 @@ impl Timestamp { nanos: dur.subsec_nanos(), } } + #[cfg(all(feature = "std", any(feature = "v1", feature = "v6")))] + pub fn now(context: impl ClockSequence) -> Self { + let dur = std::time::SystemTime::UNIX_EPOCH + .elapsed() + .expect("Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality"); + Timestamp { + seconds: dur.as_secs(), + nanos: dur.subsec_nanos(), + counter: context + .generate_sequence(dur.as_secs(), dur.subsec_nanos()), + } + } /// Returns the raw RFC4122 timestamp "tick" values stored by the /// `Timestamp`. /// /// The ticks represent the number of 100-nanosecond intervals /// since 00:00:00.00, 15 Oct 1582. - pub const fn to_rfc4122(&self) -> u64 { - Self::unix_to_rfc4122_ticks(self.seconds, self.nanos) + #[cfg(any(feature = "v1", feature = "v6"))] + pub const fn to_rfc4122(&self) -> (u64, u16) { + ( + Self::unix_to_rfc4122_ticks(self.seconds, self.nanos), + self.counter, + ) } /// Returns the timestamp converted to the seconds and fractional @@ -101,13 +147,21 @@ pub trait ClockSequence { /// Return an arbitrary width number that will be used as the "clock sequence" in /// the UUID. The number must be different if the time has changed since /// the last time a clock sequence was requested. - fn next(&self, ts: &Timestamp) -> Self::Output; + fn generate_sequence( + &self, + seconds: u64, + subsec_nanos: u32, + ) -> Self::Output; } impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { type Output = T::Output; - fn next(&self, ts: &Timestamp) -> Self::Output { - (**self).next(ts) + fn generate_sequence( + &self, + seconds: u64, + subsec_nanos: u32, + ) -> Self::Output { + (**self).generate_sequence(seconds, subsec_nanos) } } @@ -158,7 +212,11 @@ pub mod context { impl super::ClockSequence for Context { type Output = u16; - fn next(&self, _: &super::Timestamp) -> Self::Output { + fn generate_sequence( + &self, + _seconds: u64, + _nanos: u32, + ) -> Self::Output { // RFC4122 reserves 2 bits of the clock sequence so the actual // maximum value is smaller than `u16::MAX`. Since we unconditionally // increment the clock sequence we want to wrap once it becomes larger diff --git a/src/v1.rs b/src/v1.rs index 6f399581..bd5dd390 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -3,7 +3,7 @@ //! Note that you need to enable the `v1` Cargo feature //! in order to use this module. -use crate::timestamp::{ClockSequence, Timestamp}; +use crate::timestamp::Timestamp; use crate::Uuid; /// The Context implementation is specific to Uuids v1 and v6 @@ -81,13 +81,8 @@ impl Uuid { /// [`Timestamp`]: v1/struct.Timestamp.html /// [`ClockSequence`]: v1/trait.ClockSequence.html /// [`Context`]: v1/struct.Context.html - pub fn new_v1( - ts: Timestamp, - ctx: &impl ClockSequence, - node_id: &[u8; 6], - ) -> Self { - let ticks = ts.to_rfc4122(); - let counter = ctx.next(&ts); + pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self { + let (ticks, counter) = ts.to_rfc4122(); let time_low = (ticks & 0xFFFF_FFFF) as u32; let time_mid = ((ticks >> 32) & 0xFFFF) as u16; let time_high_and_version = @@ -111,9 +106,8 @@ impl Uuid { #[cfg(test)] mod tests { use super::*; - use crate::{Variant, Version}; - use std::string::ToString; + use crate::{std::string::ToString, Variant, Version}; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -126,8 +120,7 @@ mod tests { let context = Context::new(0); let uuid = Uuid::new_v1( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); @@ -138,9 +131,9 @@ mod tests { "20616934-4ba2-11e7-8000-010203040506" ); - let ts = uuid.get_timestamp().unwrap().0.to_rfc4122(); + let ts = uuid.get_timestamp().unwrap().to_rfc4122(); - assert_eq!(ts - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); + assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); // Ensure parsing the same UUID produces the same timestamp let parsed = @@ -163,36 +156,32 @@ mod tests { let context = Context::new((u16::MAX >> 2) - 1); let uuid1 = Uuid::new_v1( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); let time: u64 = 1_496_854_536; let uuid2 = Uuid::new_v1( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); - assert_eq!(uuid1.get_timestamp().unwrap().1, 16382); - assert_eq!(uuid2.get_timestamp().unwrap().1, 0); + assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16382); + assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0); let time = 1_496_854_535; let uuid3 = Uuid::new_v1( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); let uuid4 = Uuid::new_v1( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); - assert_eq!(uuid3.get_timestamp().unwrap().1, 1); - assert_eq!(uuid4.get_timestamp().unwrap().1, 2); + assert_eq!(uuid3.get_timestamp().unwrap().to_rfc4122().1, 1); + assert_eq!(uuid4.get_timestamp().unwrap().to_rfc4122().1, 2); } } diff --git a/src/v6.rs b/src/v6.rs index 62bd272f..92b8adbb 100644 --- a/src/v6.rs +++ b/src/v6.rs @@ -41,9 +41,9 @@ impl Uuid { /// # use uuid::{Uuid, Timestamp, Context}; /// # fn random_seed() -> u16 { 42 } /// let context = Context::new(random_seed()); - /// let ts = Timestamp::from_unix(1497624119, 1234); + /// let ts = Timestamp::from_unix(context, 1497624119, 1234); /// - /// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), @@ -55,10 +55,10 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Timestamp, Context}; - /// let context = Context::new(42); - /// let ts = Timestamp::from_rfc4122(14976241191231231313); + /// let context = Context::new(random_seed()); + /// let ts = Timestamp::from_rfc4122(14976241191231231313, context.generate_sequence() ); /// - /// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), @@ -76,13 +76,8 @@ impl Uuid { /// [`Timestamp`]: v1/struct.Timestamp.html /// [`ClockSequence`]: v1/trait.ClockSequence.html /// [`Context`]: v1/struct.Context.html - pub fn new_v6( - ts: Timestamp, - context: impl ClockSequence, - node_id: &[u8; 6], - ) -> Self { - let ticks = ts.to_rfc4122(); - let counter = context.next(&ts); + pub fn new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self { + let (ticks, counter) = ts.to_rfc4122(); let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32; let time_mid = ((ticks >> 12) & 0xFFFF) as u16; let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12); @@ -120,8 +115,7 @@ mod tests { let context = Context::new(0); let uuid = Uuid::new_v6( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(context, time, time_fraction), &node, ); @@ -132,9 +126,9 @@ mod tests { "1e74ba22-0616-6934-8000-010203040506" ); - let ts = uuid.get_timestamp().unwrap().0.to_rfc4122(); + let ts = uuid.get_timestamp().unwrap().to_rfc4122(); - assert_eq!(ts - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); + assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); // Ensure parsing the same UUID produces the same timestamp let parsed = @@ -157,36 +151,32 @@ mod tests { let context = Context::new((u16::MAX >> 2) - 1); let uuid1 = Uuid::new_v6( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); let time: u64 = 1_496_854_536; let uuid2 = Uuid::new_v6( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); - assert_eq!(uuid1.get_timestamp().unwrap().1, 16382); - assert_eq!(uuid2.get_timestamp().unwrap().1, 0); + assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16382); + assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0); let time = 1_496_854_535; let uuid3 = Uuid::new_v6( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); let uuid4 = Uuid::new_v6( - Timestamp::from_unix(time, time_fraction), - &context, + Timestamp::from_unix(&context, time, time_fraction), &node, ); - assert_eq!(uuid3.get_timestamp().unwrap().1, 1); - assert_eq!(uuid4.get_timestamp().unwrap().1, 2); + assert_eq!(uuid3.get_timestamp().unwrap().counter, 1); + assert_eq!(uuid4.get_timestamp().unwrap().counter, 2); } } diff --git a/src/v7.rs b/src/v7.rs index c8294dbb..9d9e8094 100644 --- a/src/v7.rs +++ b/src/v7.rs @@ -21,8 +21,8 @@ impl Uuid { /// /// ```rust /// # use uuid::{Uuid, Timestamp}; - /// - /// let ts = Timestamp::from_unix(1497624119, 1234); + /// # use uuid::v7::NullSequence; + /// let ts = Timestamp::from_unix(NullSequence {}, 1497624119, 1234); /// /// let uuid = Uuid::new_v7(ts); /// @@ -50,6 +50,15 @@ impl Uuid { } } +pub struct NullSequence {} + +impl super::ClockSequence for NullSequence { + type Output = u16; + fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output { + 0 + } +} + #[cfg(test)] mod tests { use super::*; @@ -64,7 +73,11 @@ mod tests { let time: u64 = 1_496_854_535; let time_fraction: u32 = 812_946_000; - let uuid = Uuid::new_v7(Timestamp::from_unix(time, time_fraction)); + let uuid = Uuid::new_v7(Timestamp::from_unix( + NullSequence {}, + time, + time_fraction, + )); let uustr = uuid.hyphenated().to_string(); assert_eq!(uuid.get_version(), Some(Version::SortRand));