Skip to content

Commit

Permalink
Merge pull request #759 from uuid-rs/chore/v7-counter-cleanup
Browse files Browse the repository at this point in the history
Clean up new Timestamp APIs
  • Loading branch information
KodrAus authored Jun 24, 2024
2 parents 49319a7 + 62e968c commit 62b7145
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ impl Uuid {
let seconds = millis / 1000;
let nanos = ((millis % 1000) * 1_000_000) as u32;

Some(Timestamp::from_unix_time(seconds, nanos, 0))
Some(Timestamp::from_unix_time(seconds, nanos, 0, 0))
}
_ => None,
}
Expand Down
62 changes: 29 additions & 33 deletions src/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,15 @@ pub struct Timestamp {
}

impl Timestamp {
/// Get a timestamp representing the current system time and up to a 16-bit counter.
/// Get a timestamp representing the current system time and up to a 128-bit counter.
///
/// This method defers to the standard library's `SystemTime` type.
///
/// # Panics
///
/// This method will panic if calculating the elapsed time since the Unix epoch fails.
#[cfg(feature = "std")]
pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
Self::now_128(context)
}

/// Get a timestamp representing the current system time and up to a 128-bit counter.
#[cfg(feature = "std")]
pub fn now_128(context: impl ClockSequence<Output = impl Into<u128>>) -> Self {
pub fn now(context: impl ClockSequence<Output = impl Into<u128>>) -> Self {
let (seconds, subsec_nanos) = now();

let (counter, seconds, subsec_nanos) =
Expand All @@ -78,7 +72,7 @@ impl Timestamp {
}
}

/// Construct a `Timestamp` from an RFC 9562 timestamp and 16-bit counter, as used
/// Construct a `Timestamp` from an RFC 9562 timestamp and 14-bit counter, as used
/// in versions 1 and 6 UUIDs.
///
/// # Overflow
Expand All @@ -92,17 +86,12 @@ impl Timestamp {
seconds,
subsec_nanos,
counter: counter as u128,
usable_counter_bits: 16,
usable_counter_bits: 14,
}
}

/// Construct a `Timestamp` from a Unix timestamp and a 16-bit counter, as used in version 7 UUIDs.
pub const fn from_unix_time(seconds: u64, subsec_nanos: u32, counter: u16) -> Self {
Self::from_unix_time_128(seconds, subsec_nanos, counter as u128, 16)
}

/// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs.
pub const fn from_unix_time_128(
pub const fn from_unix_time(
seconds: u64,
subsec_nanos: u32,
counter: u128,
Expand All @@ -116,17 +105,8 @@ impl Timestamp {
}
}

/// Construct a `Timestamp` from a Unix timestamp and up to a 16-bit counter, as used in version 7 UUIDs.
pub fn from_unix(
context: impl ClockSequence<Output = u16>,
seconds: u64,
subsec_nanos: u32,
) -> Self {
Self::from_unix_128(context, seconds, subsec_nanos)
}

/// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs.
pub fn from_unix_128(
pub fn from_unix(
context: impl ClockSequence<Output = impl Into<u128>>,
seconds: u64,
subsec_nanos: u32,
Expand Down Expand Up @@ -154,12 +134,13 @@ impl Timestamp {
pub const fn to_rfc4122(&self) -> (u64, u16) {
(
Self::unix_to_rfc4122_ticks(self.seconds, self.subsec_nanos),
self.counter as u16,
(self.counter as u16) & 0x3FFF,
)
}

// NOTE: This method is not public; the usable counter bits are lost in a version 7 UUID
// so can't be reliably recovered.
#[cfg(feature = "v7")]
pub(crate) const fn counter(&self) -> (u128, u8) {
(self.counter, self.usable_counter_bits)
}
Expand Down Expand Up @@ -369,7 +350,7 @@ fn now() -> (u64, u32) {
(ts.as_secs(), ts.subsec_nanos())
}

/// A counter that can be used by version 1 and version 6 UUIDs to support
/// A counter that can be used by versions 1 and 6 UUIDs to support
/// the uniqueness of timestamps.
///
/// # References
Expand Down Expand Up @@ -482,7 +463,7 @@ pub mod context {
/// 1. Atomically incrementing the counter value for each timestamp.
/// 2. Wrapping the counter back to zero if it overflows its 14-bit storage.
///
/// This type should be used when constructing version 1 and version 6 UUIDs.
/// This type should be used when constructing versions 1 and 6 UUIDs.
///
/// This type should not be used when constructing version 7 UUIDs. When used to
/// construct a version 7 UUID, the 14-bit counter will be padded with random data.
Expand Down Expand Up @@ -823,13 +804,13 @@ pub mod context {

let context = ContextV7::new();

let ts1 = Timestamp::from_unix_128(&context, seconds, subsec_nanos);
let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos);
assert_eq!(42, ts1.usable_counter_bits);

// Backwards second
let seconds = 1_496_854_534;

let ts2 = Timestamp::from_unix_128(&context, seconds, subsec_nanos);
let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos);

// The backwards time should be ignored
// The counter should still increment
Expand All @@ -840,7 +821,7 @@ pub mod context {
// Forwards second
let seconds = 1_496_854_536;

let ts3 = Timestamp::from_unix_128(&context, seconds, subsec_nanos);
let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos);

// The counter should have reseeded
assert_ne!(ts2.counter + 1, ts3.counter);
Expand All @@ -860,7 +841,7 @@ pub mod context {
counter: Cell::new(u64::MAX >> 22),
};

let ts = Timestamp::from_unix_128(&context, seconds, subsec_nanos);
let ts = Timestamp::from_unix(&context, seconds, subsec_nanos);

// The timestamp should be incremented by 1ms
let expected_ts = Duration::new(seconds, subsec_nanos / 1_000_000 * 1_000_000)
Expand Down Expand Up @@ -932,4 +913,19 @@ mod tests {

Timestamp::rfc4122_to_unix(u64::MAX);
}

#[test]
#[cfg_attr(
all(
target_arch = "wasm32",
target_vendor = "unknown",
target_os = "unknown"
),
wasm_bindgen_test
)]
fn to_rfc4122_truncates_to_usable_bits() {
let ts = Timestamp::from_rfc4122(123, u16::MAX);

assert_eq!((123, u16::MAX >> 2), ts.to_rfc4122());
}
}
12 changes: 6 additions & 6 deletions src/v7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl Uuid {
/// guaranteed to be ordered by their creation.
#[cfg(feature = "std")]
pub fn now_v7() -> Self {
Self::new_v7(Timestamp::now_128(
Self::new_v7(Timestamp::now(
crate::timestamp::context::shared_context_v7(),
))
}
Expand Down Expand Up @@ -50,8 +50,8 @@ impl Uuid {
/// ```rust
/// # use uuid::{Uuid, Timestamp, ContextV7};
/// let context = ContextV7::new();
/// let uuid1 = Uuid::new_v7(Timestamp::from_unix_128(&context, 1497624119, 1234));
/// let uuid2 = Uuid::new_v7(Timestamp::from_unix_128(&context, 1497624119, 1234));
/// let uuid1 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
/// let uuid2 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
///
/// assert!(uuid1 < uuid2);
/// ```
Expand Down Expand Up @@ -98,7 +98,7 @@ impl Uuid {
#[cfg(test)]
mod tests {
use super::*;

use crate::{std::string::ToString, ClockSequence, NoContext, Variant, Version};

#[cfg(all(
Expand Down Expand Up @@ -227,8 +227,8 @@ mod tests {
let time_fraction: u32 = 812_000_000;

// Ensure we don't overflow here
let ts = Timestamp::from_unix_128(MaxContext, time, time_fraction);
let ts = Timestamp::from_unix(MaxContext, time, time_fraction);

let uuid = Uuid::new_v7(ts);

assert_eq!(uuid.get_version(), Some(Version::SortRand));
Expand Down

0 comments on commit 62b7145

Please sign in to comment.