Skip to content

Commit

Permalink
hacking the API to be backwards compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
rrichardson committed Aug 23, 2022
1 parent d684d42 commit 13f785d
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 83 deletions.
30 changes: 15 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<crate::timestamp::Timestamp> {
match self.get_version() {
Some(Version::Mac) => {
let bytes = self.as_bytes();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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,
}
Expand Down
80 changes: 69 additions & 11 deletions src/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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<Output = u16>,
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()
Expand All @@ -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<Output = u16>) -> 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
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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
Expand Down
41 changes: 15 additions & 26 deletions src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Output = u16>,
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 =
Expand All @@ -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::*;

Expand All @@ -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,
);

Expand All @@ -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 =
Expand All @@ -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);
}
}
Loading

0 comments on commit 13f785d

Please sign in to comment.