From 971a9e8c77c22e3a05229f2a97748e280af649e7 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 23 Aug 2022 08:02:07 +1000 Subject: [PATCH 01/20] get doc tests to pass without std and shrink build runs --- .github/workflows/ci.yml | 17 ++++++++++++----- Cargo.toml | 2 ++ src/builder.rs | 16 ++++++++-------- src/lib.rs | 26 +++++++++++++------------- src/parser.rs | 6 +++--- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 22e3d34a..9ca532cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,9 @@ name: Continuous integration +env: + VERSION_FEATURES: "v1 v3 v4 v5" + STABLE_DEP_FEATURES: "serde arbitrary" + on: pull_request: push: @@ -61,8 +65,11 @@ jobs: - name: Examples run: cargo test --all-features --examples - - name: Powerset - run: cargo hack test --feature-powerset --lib --optional-deps "serde arbitrary" --depth 3 + - name: Each version feature + run: cargo hack test --each-feature --optional-deps $STABLE_DEP_FEATURES + + - name: All version features + run: cargo hack test --features "$VERSION_FEATURES" --each-feature --optional-deps "$STABLE_DEP_FEATURES" msrv: name: "Tests / MSRV / OS: ${{ matrix.os }}" @@ -86,7 +93,7 @@ jobs: override: true - name: Version features - run: cargo test --features "v1 v3 v4 v5 serde" + run: cargo test --features "$VERSION_FEATURES $STABLE_DEP_FEATURES" wasm: name: Tests / WebAssembly @@ -102,7 +109,7 @@ jobs: run: wasm-pack test --node - name: Version features - run: wasm-pack test --node -- --features "js v1 v3 v4 v5" + run: wasm-pack test --node -- --features "$VERION_FEATURES $STABLE_DEP_FEATURES js" - name: Fast RNG run: wasm-pack test --node -- --features "js v4 fast-rng" @@ -174,4 +181,4 @@ jobs: run: cargo install cargo-hack - name: Powerset - run: cargo hack check --feature-powerset -Z avoid-dev-deps + run: cargo hack check --each-feature -Z avoid-dev-deps diff --git a/Cargo.toml b/Cargo.toml index 6b80bd45..0c87a27a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,8 @@ default = ["std"] std = [] macro-diagnostics = ["private_uuid-macro-internal"] +# NOTE: When adding new features, check the `ci.yml` workflow +# and include them where necessary (you can follow along with existing features) v1 = ["private_atomic"] v3 = ["md5"] v4 = ["rng"] diff --git a/src/builder.rs b/src/builder.rs index 6b2e05c5..0191bb11 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -283,7 +283,7 @@ impl Uuid { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::Uuid; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -324,7 +324,7 @@ impl Uuid { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::Uuid; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -359,7 +359,7 @@ impl Uuid { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::Uuid; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -390,7 +390,7 @@ impl Uuid { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::Uuid; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -422,7 +422,7 @@ impl Uuid { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::Uuid; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -491,7 +491,7 @@ impl Builder { /// Basic usage: /// /// ``` - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// # use uuid::{Builder, Uuid}; /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, @@ -565,7 +565,7 @@ impl Builder { /// /// ``` /// # use uuid::Builder; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, /// 0xb1, 0xb2, @@ -600,7 +600,7 @@ impl Builder { /// /// ``` /// # use uuid::Builder; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let bytes = [ /// 0xa1, 0xa2, 0xa3, 0xa4, /// 0xb1, 0xb2, diff --git a/src/lib.rs b/src/lib.rs index 7aee4f78..806b52e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,7 +157,7 @@ //! //! ``` //! # use uuid::Uuid; -//! # fn main() -> Result<(), Box> { +//! # fn main() -> Result<(), uuid::Error> { //! let my_uuid = Uuid::parse_str("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8")?; //! //! println!("{}", my_uuid.urn()); @@ -308,7 +308,7 @@ pub enum Variant { /// /// ``` /// # use uuid::Uuid; -/// # fn main() -> Result<(), Box> { +/// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8")?; /// /// println!("{}", my_uuid.urn()); @@ -345,7 +345,7 @@ pub enum Variant { /// /// ``` /// # use uuid::Uuid; -/// # fn main() -> Result<(), Box> { +/// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8")?; /// /// assert_eq!( @@ -360,7 +360,7 @@ pub enum Variant { /// /// ``` /// # use uuid::Uuid; -/// # fn main() -> Result<(), Box> { +/// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8")?; /// /// assert_eq!( @@ -436,7 +436,7 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Variant}; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("02f09a3f-1624-3b1d-8409-44eff7708208")?; /// /// assert_eq!(Variant::RFC4122, my_uuid.get_variant()); @@ -471,7 +471,7 @@ impl Uuid { /// /// ``` /// # use uuid::Uuid; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("02f09a3f-1624-3b1d-8409-44eff7708208")?; /// /// assert_eq!(3, my_uuid.get_version_num()); @@ -501,7 +501,7 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Version}; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let my_uuid = Uuid::parse_str("02f09a3f-1624-3b1d-8409-44eff7708208")?; /// /// assert_eq!(Some(Version::Md5), my_uuid.get_version()); @@ -548,7 +548,7 @@ impl Uuid { /// /// ``` /// # use uuid::Uuid; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::nil(); /// /// assert_eq!(uuid.as_fields(), (0, 0, 0, &[0u8; 8])); @@ -595,7 +595,7 @@ impl Uuid { /// ``` /// use uuid::Uuid; /// - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")?; /// /// assert_eq!( @@ -632,7 +632,7 @@ impl Uuid { /// /// ``` /// # use uuid::Uuid; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")?; /// /// assert_eq!( @@ -676,7 +676,7 @@ impl Uuid { /// /// ``` /// # use uuid::Uuid; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")?; /// /// assert_eq!( @@ -715,7 +715,7 @@ impl Uuid { /// /// ``` /// # use uuid::Uuid; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")?; /// assert_eq!( /// uuid.as_u64_pair(), @@ -789,7 +789,7 @@ impl Uuid { /// ``` /// use uuid::Uuid; /// - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")?; /// /// assert_eq!( diff --git a/src/parser.rs b/src/parser.rs index ea21d780..bc5f8263 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -51,7 +51,7 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Version, Variant}; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?; /// /// assert_eq!(Some(Version::Random), uuid.get_version()); @@ -84,7 +84,7 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Version, Variant}; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::try_parse("550e8400-e29b-41d4-a716-446655440000")?; /// /// assert_eq!(Some(Version::Random), uuid.get_version()); @@ -112,7 +112,7 @@ impl Uuid { /// /// ``` /// # use uuid::{Uuid, Version, Variant}; - /// # fn main() -> Result<(), Box> { + /// # fn main() -> Result<(), uuid::Error> { /// let uuid = Uuid::try_parse_ascii(b"550e8400-e29b-41d4-a716-446655440000")?; /// /// assert_eq!(Some(Version::Random), uuid.get_version()); From 21e6b2149900be5c07c54896af4d15a5b2c92040 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 23 Aug 2022 08:10:22 +1000 Subject: [PATCH 02/20] only run lib tests in feature combinations --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ca532cf..604b6c5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,10 +66,10 @@ jobs: run: cargo test --all-features --examples - name: Each version feature - run: cargo hack test --each-feature --optional-deps $STABLE_DEP_FEATURES + run: cargo hack test --lib --each-feature --optional-deps $STABLE_DEP_FEATURES - name: All version features - run: cargo hack test --features "$VERSION_FEATURES" --each-feature --optional-deps "$STABLE_DEP_FEATURES" + run: cargo hack test --lib --features "$VERSION_FEATURES" --each-feature --optional-deps "$STABLE_DEP_FEATURES" msrv: name: "Tests / MSRV / OS: ${{ matrix.os }}" From 66823da1cdd71876c05b5fafccd3e4c0f0d470f7 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Tue, 16 Aug 2022 17:20:28 -0700 Subject: [PATCH 03/20] merging in main --- Cargo.toml | 22 ++-- src/builder.rs | 26 ++++- src/lib.rs | 80 ++++++++++++- src/rng.rs | 4 +- src/timestamp.rs | 169 +++++++++++++++++++++++++++ src/v1.rs | 295 +++++++++++------------------------------------ src/v4.rs | 2 +- src/v6.rs | 192 ++++++++++++++++++++++++++++++ src/v7.rs | 78 +++++++++++++ src/v8.rs | 50 ++++++++ 10 files changed, 673 insertions(+), 245 deletions(-) create mode 100644 src/timestamp.rs create mode 100644 src/v6.rs create mode 100644 src/v7.rs create mode 100644 src/v8.rs diff --git a/Cargo.toml b/Cargo.toml index 6b80bd45..ab66a242 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = [ ] description = "A library to generate and parse UUIDs." documentation = "https://docs.rs/uuid" -edition = "2018" +edition = "2021" exclude = [ ".github/**" ] @@ -51,12 +51,13 @@ repository = "uuid-rs/uuid" default = ["std"] std = [] macro-diagnostics = ["private_uuid-macro-internal"] - -v1 = ["private_atomic"] +v1 = [] v3 = ["md5"] v4 = ["rng"] v5 = ["sha1"] - +v6 = [] +v7 = ["rng"] +v8 = [] js = ["private_getrandom", "private_getrandom/js"] rng = ["private_getrandom"] @@ -65,6 +66,9 @@ fast-rng = ["rng", "private_rand"] sha1 = ["private_sha1_smol"] md5 = ["private_md-5"] +[dependencies] +num-traits = "0.2.15" + # Public: Used in trait impls on `Uuid` [dependencies.serde] default-features = false @@ -84,7 +88,7 @@ version = "1" # Public (unstable): Used in `zerocopy` derive # Unstable: also need RUSTFLAGS="--cfg uuid_unstable" to work # This feature may break between releases, or be removed entirely before -# stabilization. +# stabilization. # See: https://github.com/uuid-rs/uuid/issues/588 [dependencies.zerocopy] optional = true @@ -133,14 +137,6 @@ version = "1.1.2" path = "macros" optional = true -# Private -# Don't depend on this optional feature directly: it may change at any time -[dependencies.private_atomic] -package = "atomic" -default-features = false -optional = true -version = "0.5" - [dev-dependencies.bincode] version = "1.0" diff --git a/src/builder.rs b/src/builder.rs index 6b2e05c5..fff0e427 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -40,7 +40,7 @@ use crate::{error::*, Bytes, Uuid, Variant, Version}; /// ``` #[allow(missing_copy_implementations)] #[derive(Debug)] -pub struct Builder(Uuid); +pub struct Builder(pub Uuid); impl Uuid { /// The 'nil UUID'. @@ -67,6 +67,30 @@ impl Uuid { Uuid::from_bytes([0; 16]) } + /// The 'max UUID'. + /// + /// The max UUID is a special form of UUID that is specified to have all + /// 128 bits set to one, as defined in [IETF RFC 4122 Update Section 5.4][Draft RFC]. + /// + /// [Draft RFC]: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#page-12 + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use uuid::Uuid; + /// let uuid = Uuid::max(); + /// + /// assert_eq!( + /// "ffffffff-ffff-ffff-ffff-ffffffffffff", + /// uuid.hyphenated().to_string(), + /// ); + /// ``` + pub const fn max() -> Self { + Uuid::from_bytes([0xFF; 16]) + } + /// Creates a UUID from four field values. /// /// # Examples diff --git a/src/lib.rs b/src/lib.rs index 7aee4f78..7cb3e295 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,9 +220,15 @@ use zerocopy::{AsBytes, FromBytes, Unaligned}; mod builder; mod error; +pub mod fmt; mod parser; +/// contains the `Timestamp` struct and `ClockSequence` trait +pub mod timestamp; -pub mod fmt; +pub use timestamp::{ClockSequence, Timestamp}; + +#[cfg(any(feature = "v1", feature = "v3"))] +pub use timestamp::context::Context; #[cfg(feature = "v1")] pub mod v1; @@ -232,6 +238,12 @@ mod v3; mod v4; #[cfg(feature = "v5")] mod v5; +#[cfg(feature = "v6")] +mod v6; +#[cfg(feature = "v7")] +mod v7; +#[cfg(feature = "v8")] +mod v8; #[cfg(feature = "md5")] mod md5; @@ -280,6 +292,12 @@ pub enum Version { Random, /// Version 5: SHA-1 hash. Sha1, + /// Version 6: Sortable MAC/Node-ID + SortMac, + /// Version 7: Timestamp + Random + SortRand, + /// Version 8: Custom + Custom, } /// The reserved variants of UUIDs. @@ -520,6 +538,9 @@ impl Uuid { 3 => Some(Version::Md5), 4 => Some(Version::Random), 5 => Some(Version::Sha1), + 6 => Some(Version::SortMac), + 7 => Some(Version::SortRand), + 8 => Some(Version::Custom), _ => None, } } @@ -843,6 +864,63 @@ impl Uuid { pub const fn encode_buffer() -> [u8; fmt::Urn::LENGTH] { [0; fmt::Urn::LENGTH] } + + /// If the UUID is the correct version (v1, v6, or v7) this will return + /// the timestamp and counter portion parsed from a V1 UUID. + /// + /// Returns `None` if the supplied UUID is not V1. + /// + /// The V1 timestamp format defined in RFC4122 specifies a 60-bit + /// integer representing the number of 100-nanosecond intervals + /// since 00:00:00.00, 15 Oct 1582. + /// + /// [`Timestamp`] offers several options for converting the raw RFC4122 + /// 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)> { + match self.get_version() { + Some(Version::Mac) => { + let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56 + | ((self.as_bytes()[7]) as u64) << 48 + | ((self.as_bytes()[4]) as u64) << 40 + | ((self.as_bytes()[5]) as u64) << 32 + | ((self.as_bytes()[0]) as u64) << 24 + | ((self.as_bytes()[1]) as u64) << 16 + | ((self.as_bytes()[2]) as u64) << 8 + | (self.as_bytes()[3] as u64); + + let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8 + | (self.as_bytes()[9] as u16); + + Some(( + crate::timestamp::Timestamp::from_rfc4122(ticks), + counter, + )) + } + Some(Version::SortMac) => { + let ticks: u64 = ((self.as_bytes()[0]) as u64) << 52 + | ((self.as_bytes()[1]) as u64) << 44 + | ((self.as_bytes()[2]) as u64) << 36 + | ((self.as_bytes()[3]) as u64) << 28 + | ((self.as_bytes()[4]) as u64) << 20 + | ((self.as_bytes()[5]) as u64) << 12 + | ((self.as_bytes()[6] & 0xF) as u64) << 8 + | (self.as_bytes()[7] as u64); + + let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8 + | (self.as_bytes()[9] as u16); + + Some(( + crate::timestamp::Timestamp::from_rfc4122(ticks), + counter, + )) + } + _ => None, + } + } } impl Default for Uuid { diff --git a/src/rng.rs b/src/rng.rs index fa21b626..2cf07894 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "v4")] +#[cfg(any(feature = "v4", feature = "v7"))] pub(crate) fn bytes() -> [u8; 16] { #[cfg(not(feature = "fast-rng"))] { @@ -18,7 +18,7 @@ pub(crate) fn bytes() -> [u8; 16] { } } -#[cfg(feature = "v1")] +#[cfg(any(feature = "v1", feature = "v7"))] pub(crate) fn u16() -> u16 { #[cfg(not(feature = "fast-rng"))] { diff --git a/src/timestamp.rs b/src/timestamp.rs new file mode 100644 index 00000000..4257b232 --- /dev/null +++ b/src/timestamp.rs @@ -0,0 +1,169 @@ +/// Contains the `Timestamp` struct and `ClockSequence` traits +/// as well as an implementation of ClockSequence for Context (only for features v1 and v6) + +/// The number of 100 ns ticks between the UUID epoch +/// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`. +pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; +/// Stores the number of seconds since epoch, +/// as well as the fractional nanoseconds of that second +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Timestamp { + pub(crate) seconds: u64, + pub(crate) nanos: u32, +} + +impl Timestamp { + /// Construct a `Timestamp` from its raw component values: an RFC4122 + /// timestamp and counter. + /// + /// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format + /// 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 { + let (seconds, nanos) = Self::rfc4122_to_unix(ticks); + Timestamp { seconds, nanos } + } + + /// Construct a `Timestamp` from a unix timestamp + /// + /// A unix timestamp represents the elapsed time since Jan 1 1970. Libc's + /// `clock_gettime` and other popular implementations traditionally + /// represent this duration as a `timespec`: a struct with `u64` and + /// `u32` fields representing the seconds, and "subsecond" or fractional + /// nanoseconds elapsed since the timestamp's second began, + /// respectively. + pub fn from_unix(seconds: u64, nanos: u32) -> Self { + Timestamp { seconds, nanos } + } + + /// Construct a `Timestamp` from the current time of day + /// according to Rust's SystemTime + pub fn now() -> 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(), + } + } + + /// 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) + } + + /// Returns the timestamp converted to the seconds and fractional + /// nanoseconds since Jan 1 1970. + pub const fn to_unix(&self) -> (u64, u32) { + (self.seconds, self.nanos) + } + + /// Returns the timestamp converted into nanoseconds elapsed since Jan 1 + /// 1970. + pub const fn to_unix_nanos(&self) -> u32 { + self.nanos + } + + /// internal utility functions for converting between Unix and Uuid-epoch + /// convert unix-timestamp into rfc4122 ticks + 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; + + ticks + } + + /// convert rfc4122 ticks into unix-timestamp + 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, + ) + } +} + +/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values. +/// +/// # References +/// +/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5) +pub trait ClockSequence { + /// The primitive type you wish out output + type Output; + /// 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; +} + +impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { + type Output = T::Output; + fn next(&self, ts: &Timestamp) -> Self::Output { + (**self).next(ts) + } +} + +/// For features v1 and v1, constructs a `Context` struct which implements the `ClockSequence` trait +#[cfg(any(feature = "v1", feature = "v6"))] +pub mod context { + use std::sync::atomic::{AtomicU16, Ordering}; + /// A thread-safe, stateful context for the v1 generator to help ensure + /// process-wide uniqueness. + #[derive(Debug)] + pub struct Context { + count: AtomicU16, + } + + impl Context { + /// Creates a thread-safe, internally mutable context to help ensure + /// uniqueness. + /// + /// This is a context which can be shared across threads. It maintains an + /// internal counter that is incremented at every request, the value ends + /// up in the clock_seq portion of the UUID (the fourth group). This + /// will improve the probability that the UUID is unique across the + /// process. + pub const fn new(count: u16) -> Self { + Self { + count: AtomicU16::new(count), + } + } + + /// Creates a thread-safe, internally mutable context that's seeded with a + /// random value. + /// + /// This method requires either the `rng` or `fast-rng` feature to also be + /// enabled. + /// + /// This is a context which can be shared across threads. It maintains an + /// internal counter that is incremented at every request, the value ends + /// up in the clock_seq portion of the UUID (the fourth group). This + /// will improve the probability that the UUID is unique across the + /// process. + #[cfg(feature = "rng")] + pub fn new_random() -> Self { + Self { + count: AtomicU16::new(crate::rng::u16()), + } + } + } + + impl super::ClockSequence for Context { + type Output = u16; + fn next(&self, _: &super::Timestamp) -> 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 + // than what we can represent in a "u14". Otherwise there'd be patches + // where the clock sequence doesn't change regardless of the timestamp + self.count.fetch_add(1, Ordering::AcqRel) % (u16::MAX >> 2) + } + } +} diff --git a/src/v1.rs b/src/v1.rs index 94dd4c71..9fa752cc 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -3,134 +3,16 @@ //! Note that you need to enable the `v1` Cargo feature //! in order to use this module. -use crate::{Uuid, Version}; +use crate::timestamp::{ClockSequence, Timestamp}; +use crate::Uuid; -use private_atomic::{Atomic, Ordering}; /// The number of 100 ns ticks between the UUID epoch /// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`. const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; -/// A thread-safe, stateful context for the v1 generator to help ensure -/// process-wide uniqueness. -#[derive(Debug)] -pub struct Context { - count: Atomic, -} - -/// Stores the number of nanoseconds from an epoch and a counter for ensuring -/// V1 ids generated on the same host are unique. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Timestamp { - ticks: u64, - counter: u16, -} - -impl Timestamp { - /// Construct a `Timestamp` from its raw component values: an RFC4122 - /// timestamp and counter. - /// - /// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format - /// 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." - /// - /// The counter value is used to differentiate between ids generated by - /// the same host computer in rapid succession (i.e. with the same observed - /// time). See the [`ClockSequence`] trait for a generic interface to any - /// counter generators that might be used. - /// - /// Internally, the timestamp is stored as a `u64`. For this reason, dates - /// prior to October 1582 are not supported. - /// - /// [`ClockSequence`]: trait.ClockSequence.html - pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { - Timestamp { ticks, counter } - } - - /// Construct a `Timestamp` from a unix timestamp and sequence-generating - /// `context`. - /// - /// A unix timestamp represents the elapsed time since Jan 1 1970. Libc's - /// `clock_gettime` and other popular implementations traditionally - /// represent this duration as a `timespec`: a struct with `u64` and - /// `u32` fields representing the seconds, and "subsecond" or fractional - /// nanoseconds elapsed since the timestamp's second began, - /// respectively. - /// - /// This constructs a `Timestamp` from the seconds and fractional - /// nanoseconds of a unix timestamp, converting the duration since 1970 - /// into the number of 100-nanosecond intervals since 00:00:00.00, 15 - /// Oct 1582 specified by RFC4122 and used internally by `Timestamp`. - /// - /// The function is not guaranteed to produce monotonically increasing - /// values however. There is a slight possibility that two successive - /// equal time values could be supplied and the sequence counter wraps back - /// over to 0. - /// - /// If uniqueness and monotonicity is required, the user is responsible for - /// ensuring that the time value always increases between calls (including - /// between restarts of the process and device). - pub fn from_unix(context: impl ClockSequence, seconds: u64, subsec_nanos: u32) -> Self { - let counter = context.generate_sequence(seconds, subsec_nanos); - let ticks = - UUID_TICKS_BETWEEN_EPOCHS + seconds * 10_000_000 + u64::from(subsec_nanos) / 100; - - Timestamp { ticks, counter } - } - - /// Returns the raw RFC4122 timestamp and counter values stored by the - /// `Timestamp`. - /// - /// The timestamp (the first, `u64` element in the tuple) represents the - /// number of 100-nanosecond intervals since 00:00:00.00, 15 Oct 1582. - /// The counter is used to differentiate between ids generated on the - /// same host computer with the same observed time. - pub const fn to_rfc4122(&self) -> (u64, u16) { - (self.ticks, self.counter) - } - - /// Returns the timestamp converted to the seconds and fractional - /// nanoseconds since Jan 1 1970. - /// - /// Internally, the time is stored in 100-nanosecond intervals, - /// thus the maximum precision represented by the fractional nanoseconds - /// value is less than its unit size (100 ns vs. 1 ns). - pub const fn to_unix(&self) -> (u64, u32) { - ( - (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, - ((self.ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100, - ) - } - - /// Returns the timestamp converted into nanoseconds elapsed since Jan 1 - /// 1970. Internally, the time is stored in 100-nanosecond intervals, - /// thus the maximum precision represented is less than the units it is - /// measured in (100 ns vs. 1 ns). The value returned represents the - /// same duration as [`Timestamp::to_unix`]; this provides it in nanosecond - /// units for convenience. - pub const fn to_unix_nanos(&self) -> u64 { - (self.ticks - UUID_TICKS_BETWEEN_EPOCHS) * 100 - } -} - -/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values. -/// -/// # References -/// -/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5) -pub trait ClockSequence { - /// Return a 16-bit 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 generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16; -} - -impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { - fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16 { - (**self).generate_sequence(seconds, subsec_nanos) - } -} +/// The Context implementation is specific to Uuids v1 and v6 +pub use crate::timestamp::context::Context; impl Uuid { /// Create a new UUID (version 1) using a time value + sequence + @@ -160,13 +42,13 @@ impl Uuid { /// is seeded with a random value: /// /// ```rust - /// # use uuid::v1::{Timestamp, Context}; + /// # use uuid::{Timestamp, Context}; /// # use uuid::Uuid; /// # fn random_seed() -> u16 { 42 } /// let context = Context::new(random_seed()); - /// let ts = Timestamp::from_unix(&context, 1497624119, 1234); + /// let ts = Timestamp::from_unix(1497624119, 1234); /// - /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), @@ -177,31 +59,49 @@ impl Uuid { /// The timestamp can also be created manually as per RFC4122: /// /// ``` - /// # use uuid::v1::{Timestamp, Context}; + /// # use uuid::{Timestamp, Context}; /// # use uuid::Uuid; /// let context = Context::new(42); - /// let ts = Timestamp::from_rfc4122(1497624119, 0); + /// let ts = Timestamp::from_rfc4122(14976234442241191232); /// - /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), - /// "5943ee37-0000-1000-8000-010203040506" + /// "b2c1ad40-45e0-1fd6-802a-010203040506" /// ); /// ``` /// + /// The timestamp can also just use the current SystemTime + /// + /// ```rust + /// # use uuid::{Timestamp, Context}; + /// # use uuid::Uuid; + /// let context = Context::new(42); + /// let ts = Timestamp::now(); + /// + /// let _uuid = Uuid::new_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// ``` + /// /// [`Timestamp`]: v1/struct.Timestamp.html /// [`ClockSequence`]: v1/trait.ClockSequence.html /// [`Context`]: v1/struct.Context.html - pub const fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self { - let time_low = (ts.ticks & 0xFFFF_FFFF) as u32; - let time_mid = ((ts.ticks >> 32) & 0xFFFF) as u16; - let time_high_and_version = (((ts.ticks >> 48) & 0x0FFF) as u16) | (1 << 12); + pub fn new_v1( + ts: Timestamp, + ctx: &impl ClockSequence, + node_id: &[u8; 6], + ) -> Self { + let ticks = ts.to_rfc4122(); + let counter = ctx.next(&ts); + let time_low = (ticks & 0xFFFF_FFFF) as u32; + let time_mid = ((ticks >> 32) & 0xFFFF) as u16; + let time_high_and_version = + (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12); let mut d4 = [0; 8]; - d4[0] = (((ts.counter & 0x3F00) >> 8) as u8) | 0x80; - d4[1] = (ts.counter & 0xFF) as u8; + d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; + d4[1] = (counter & 0xFF) as u8; d4[2] = node_id[0]; d4[3] = node_id[1]; d4[4] = node_id[2]; @@ -211,96 +111,17 @@ impl Uuid { Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4) } - - /// Returns an optional [`Timestamp`] storing the timestamp and - /// counter portion parsed from a V1 UUID. - /// - /// Returns `None` if the supplied UUID is not V1. - /// - /// The V1 timestamp format defined in RFC4122 specifies a 60-bit - /// integer representing the number of 100-nanosecond intervals - /// since 00:00:00.00, 15 Oct 1582. - /// - /// [`Timestamp`] offers several options for converting the raw RFC4122 - /// value into more commonly-used formats, such as a unix timestamp. - /// - /// [`Timestamp`]: v1/struct.Timestamp.html - pub const fn get_timestamp(&self) -> Option { - match self.get_version() { - Some(Version::Mac) => { - let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56 - | ((self.as_bytes()[7]) as u64) << 48 - | ((self.as_bytes()[4]) as u64) << 40 - | ((self.as_bytes()[5]) as u64) << 32 - | ((self.as_bytes()[0]) as u64) << 24 - | ((self.as_bytes()[1]) as u64) << 16 - | ((self.as_bytes()[2]) as u64) << 8 - | (self.as_bytes()[3] as u64); - - let counter: u16 = - ((self.as_bytes()[8] & 0x3F) as u16) << 8 | (self.as_bytes()[9] as u16); - - Some(Timestamp::from_rfc4122(ticks, counter)) - } - _ => None, - } - } -} - -impl Context { - /// Creates a thread-safe, internally mutable context to help ensure - /// uniqueness. - /// - /// This is a context which can be shared across threads. It maintains an - /// internal counter that is incremented at every request, the value ends - /// up in the clock_seq portion of the UUID (the fourth group). This - /// will improve the probability that the UUID is unique across the - /// process. - pub const fn new(count: u16) -> Self { - Self { - count: Atomic::new(count), - } - } - - /// Creates a thread-safe, internally mutable context that's seeded with a - /// random value. - /// - /// This method requires either the `rng` or `fast-rng` feature to also be - /// enabled. - /// - /// This is a context which can be shared across threads. It maintains an - /// internal counter that is incremented at every request, the value ends - /// up in the clock_seq portion of the UUID (the fourth group). This - /// will improve the probability that the UUID is unique across the - /// process. - #[cfg(feature = "rng")] - pub fn new_random() -> Self { - Self { - count: Atomic::new(crate::rng::u16()), - } - } -} - -impl ClockSequence for Context { - fn generate_sequence(&self, _: u64, _: u32) -> u16 { - // 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 - // than what we can represent in a "u14". Otherwise there'd be patches - // where the clock sequence doesn't change regardless of the timestamp - self.count.fetch_add(1, Ordering::AcqRel) % (u16::MAX >> 2) - } } #[cfg(test)] mod tests { use super::*; + use crate::{Variant, Version}; + use std::string::ToString; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - use crate::{std::string::ToString, Variant}; - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new_v1() { @@ -309,7 +130,11 @@ mod tests { let node = [1, 2, 3, 4, 5, 6]; let context = Context::new(0); - let uuid = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid = Uuid::new_v1( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); assert_eq!(uuid.get_version(), Some(Version::Mac)); assert_eq!(uuid.get_variant(), Variant::RFC4122); @@ -318,9 +143,9 @@ mod tests { "20616934-4ba2-11e7-8000-010203040506" ); - let ts = uuid.get_timestamp().unwrap().to_rfc4122(); + let ts = uuid.get_timestamp().unwrap().0.to_rfc4122(); - assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); + assert_eq!(ts - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); // Ensure parsing the same UUID produces the same timestamp let parsed = Uuid::parse_str("20616934-4ba2-11e7-8000-010203040506").unwrap(); @@ -341,21 +166,37 @@ mod tests { // This context will wrap let context = Context::new((u16::MAX >> 2) - 1); - let uuid1 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid1 = Uuid::new_v1( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); let time: u64 = 1_496_854_536; - let uuid2 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid2 = Uuid::new_v1( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); - assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16382); - assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0); + assert_eq!(uuid1.get_timestamp().unwrap().1, 16382); + assert_eq!(uuid2.get_timestamp().unwrap().1, 0); let time = 1_496_854_535; - let uuid3 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); - let uuid4 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid3 = Uuid::new_v1( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + let uuid4 = Uuid::new_v1( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); - assert_eq!(uuid3.get_timestamp().unwrap().to_rfc4122().1, 1); - assert_eq!(uuid4.get_timestamp().unwrap().to_rfc4122().1, 2); + assert_eq!(uuid3.get_timestamp().unwrap().1, 1); + assert_eq!(uuid4.get_timestamp().unwrap().1, 2); } } diff --git a/src/v4.rs b/src/v4.rs index 093bc0e7..7aafbfc8 100644 --- a/src/v4.rs +++ b/src/v4.rs @@ -34,8 +34,8 @@ impl Uuid { #[cfg(test)] mod tests { use super::*; - use crate::{Variant, Version}; + use std::string::ToString; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; diff --git a/src/v6.rs b/src/v6.rs new file mode 100644 index 00000000..62bd272f --- /dev/null +++ b/src/v6.rs @@ -0,0 +1,192 @@ +//! The implementation for Version 1 UUIDs. +//! +//! Note that you need to enable the `v1` Cargo feature +//! in order to use this module. + +use crate::timestamp::{ClockSequence, Timestamp}; +use crate::Uuid; + +/// The Context implementation is specific to Uuids v1 and v6 +pub use crate::timestamp::context::Context; + +impl Uuid { + /// Create a new UUID (version 6) using a time value + sequence + + /// *NodeId*. + /// This is similar to UUIDv1, except that it is lexographically sortable by timestamp. + /// + /// When generating [`Timestamp`]s using a [`ClockSequence`], this function + /// is only guaranteed to produce unique values if the following conditions + /// hold: + /// + /// 1. The *NodeId* is unique for this process, + /// 2. The *Context* is shared across all threads which are generating v1 + /// UUIDs, + /// 3. The [`ClockSequence`] implementation reliably returns unique + /// clock sequences (this crate provides [`Context`] for this + /// purpose. However you can create your own [`ClockSequence`] + /// implementation, if [`Context`] does not meet your needs). + /// + /// The NodeID must be exactly 6 bytes long. + /// + /// Note that usage of this method requires the `v6` feature of this crate + /// to be enabled. + /// + /// # Examples + /// + /// A UUID can be created from a unix [`Timestamp`] with a + /// [`ClockSequence`]. RFC4122 requires the clock sequence + /// is seeded with a random value: + /// + /// ```rust + /// # use uuid::{Uuid, Timestamp, Context}; + /// # fn random_seed() -> u16 { 42 } + /// let context = Context::new(random_seed()); + /// let ts = Timestamp::from_unix(1497624119, 1234); + /// + /// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// + /// assert_eq!( + /// uuid.hyphenated().to_string(), + /// "1e752a1f-3b49-658c-802a-010203040506" + /// ); + /// ``` + /// + /// The timestamp can also be created manually as per RFC4122: + /// + /// ``` + /// # use uuid::{Uuid, Timestamp, Context}; + /// let context = Context::new(42); + /// let ts = Timestamp::from_rfc4122(14976241191231231313); + /// + /// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// + /// assert_eq!( + /// uuid.hyphenated().to_string(), + /// "fd64c041-1e91-6551-802a-010203040506" + /// ); + /// ``` + /// The timestamp can also be created automatically from the current SystemTime + /// + /// # use uuid::{Uuid, Timestamp, Context}; + /// let context = Context::new(42); + /// let ts = Timestamp::from_rfc4122(14976241191231231313); + /// + /// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// + /// [`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); + 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); + + let mut d4 = [0; 8]; + + d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; + d4[1] = (counter & 0xFF) as u8; + d4[2] = node_id[0]; + d4[3] = node_id[1]; + d4[4] = node_id[2]; + d4[5] = node_id[3]; + d4[6] = node_id[4]; + d4[7] = node_id[5]; + + Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Variant, Version}; + use std::string::ToString; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new_v6() { + let time: u64 = 1_496_854_535; + let time_fraction: u32 = 812_946_000; + let node = [1, 2, 3, 4, 5, 6]; + let context = Context::new(0); + + let uuid = Uuid::new_v6( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + + assert_eq!(uuid.get_version(), Some(Version::SortMac)); + assert_eq!(uuid.get_variant(), Variant::RFC4122); + assert_eq!( + uuid.hyphenated().to_string(), + "1e74ba22-0616-6934-8000-010203040506" + ); + + let ts = uuid.get_timestamp().unwrap().0.to_rfc4122(); + + assert_eq!(ts - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); + + // Ensure parsing the same UUID produces the same timestamp + let parsed = + Uuid::parse_str("1e74ba22-0616-6934-8000-010203040506").unwrap(); + + assert_eq!( + uuid.get_timestamp().unwrap(), + parsed.get_timestamp().unwrap() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new_v6_context() { + let time: u64 = 1_496_854_535; + let time_fraction: u32 = 812_946_000; + let node = [1, 2, 3, 4, 5, 6]; + + // This context will wrap + let context = Context::new((u16::MAX >> 2) - 1); + + let uuid1 = Uuid::new_v6( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + + let time: u64 = 1_496_854_536; + + let uuid2 = Uuid::new_v6( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + + assert_eq!(uuid1.get_timestamp().unwrap().1, 16382); + assert_eq!(uuid2.get_timestamp().unwrap().1, 0); + + let time = 1_496_854_535; + + let uuid3 = Uuid::new_v6( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + let uuid4 = Uuid::new_v6( + Timestamp::from_unix(time, time_fraction), + &context, + &node, + ); + + assert_eq!(uuid3.get_timestamp().unwrap().1, 1); + assert_eq!(uuid4.get_timestamp().unwrap().1, 2); + } +} diff --git a/src/v7.rs b/src/v7.rs new file mode 100644 index 00000000..9e86abce --- /dev/null +++ b/src/v7.rs @@ -0,0 +1,78 @@ +//! The implementation for Version 1 UUIDs. +//! +//! Note that you need to enable the `v1` Cargo feature +//! in order to use this module. + +use crate::rng::{bytes, u16}; +use crate::timestamp::Timestamp; +use crate::{Uuid, Version}; + +impl Uuid { + /// Create a new UUID (version 7) using a time value + random number + /// + /// Note that usage of this method requires the `v7` feature of this crate + /// to be enabled. + /// + /// # Examples + /// + /// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit + /// random number. When supplied as such, the data will be + /// + /// ```rust + /// # use uuid::{Uuid, Timestamp}; + /// + /// let ts = Timestamp::from_unix(1497624119, 1234); + /// + /// let uuid = Uuid::new_v7(ts); + /// + /// assert!( + /// uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7") + /// ); + /// ``` + /// + /// The timestamp can also be created automatically from the current SystemTime + /// + /// let ts = Timestamp::now(); + /// + /// let uuid = Uuid::new_v7(ts); + /// + /// [`Timestamp`]: v1/struct.Timestamp.html + pub fn new_v7(ts: Timestamp) -> Self { + let millis = ts.seconds * 1000 + (ts.nanos as u64) / 1_000_000; + let ms_high = ((millis >> 16) & 0xFFFF_FFFF) as u32; + let ms_low = (millis & 0xFFFF) as u16; + let ver_rand = u16() & 0xFFF | (0x7 << 12); + let mut rnd = bytes(); + rnd[0] = (rnd[0] & 0x3F) | 0x80; + let buf: [u8; 8] = (&rnd[0..8]).try_into().unwrap(); + Uuid::from_fields(ms_high, ms_low, ver_rand, &buf) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{Variant, Version}; + use std::string::ToString; + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new_v7() { + 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 uustr = uuid.hyphenated().to_string(); + + assert_eq!(uuid.get_version(), Some(Version::SortRand)); + assert_eq!(uuid.get_variant(), Variant::RFC4122); + assert!(uustr.starts_with("015c837b-9e84-7")); + + // Ensure parsing the same UUID produces the same timestamp + let parsed = Uuid::parse_str(uustr.as_str()).unwrap(); + + assert_eq!(uuid, parsed,); + } +} diff --git a/src/v8.rs b/src/v8.rs new file mode 100644 index 00000000..956a7867 --- /dev/null +++ b/src/v8.rs @@ -0,0 +1,50 @@ +use crate::{Builder, Uuid, Variant, Version}; + +impl Uuid { + /// Creates a custom UUID comprised almost entirely of user-supplied bytes + /// + /// This will inject the UUID Version at 4 bits starting at the 48th bit + /// and the Variant into 2 bits 64th bit. + /// So if there are bits are supplied in the input buffer, they will not be + /// visible in the result + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use uuid::{Uuid, Version}; + /// let buf: [u8; 16] = *b"abcdefghijklmnop"; + /// let uuid = Uuid::new_v8(buf); + /// + /// assert_eq!(Some(Version::Custom), uuid.get_version()); + /// ``` + /// + /// [`getrandom`]: https://crates.io/crates/getrandom + /// [from_random_bytes]: struct.Builder.html#method.from_random_bytes + pub fn new_v8(buf: [u8; 16]) -> Uuid { + Builder(Uuid::from_bytes(buf)) + .with_variant(Variant::RFC4122) + .with_version(Version::Custom) + .into_uuid() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::rng::bytes; + use std::string::ToString; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new() { + let buf = bytes(); + let uuid = Uuid::new_v8(buf); + assert_eq!(uuid.get_version(), Some(Version::Custom)); + assert_eq!(uuid.get_variant(), Variant::RFC4122); + assert_eq!(uuid.get_version_num(), 8) + } +} From 14c131ab6244830df6bc5d193b0685ef0055659c Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 08:24:00 -0700 Subject: [PATCH 04/20] adding the v7 get_timestamp impl --- src/lib.rs | 57 +++++++++++++++++++++++++++++++----------------- src/timestamp.rs | 2 +- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7cb3e295..a694ba27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -883,17 +883,18 @@ impl Uuid { ) -> Option<(crate::timestamp::Timestamp, u16)> { match self.get_version() { Some(Version::Mac) => { - let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56 - | ((self.as_bytes()[7]) as u64) << 48 - | ((self.as_bytes()[4]) as u64) << 40 - | ((self.as_bytes()[5]) as u64) << 32 - | ((self.as_bytes()[0]) as u64) << 24 - | ((self.as_bytes()[1]) as u64) << 16 - | ((self.as_bytes()[2]) as u64) << 8 - | (self.as_bytes()[3] as u64); - - let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8 - | (self.as_bytes()[9] as u16); + let bytes = self.as_bytes(); + let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56 + | (bytes[7] as u64) << 48 + | (bytes[4] as u64) << 40 + | (bytes[5] as u64) << 32 + | (bytes[0] as u64) << 24 + | (bytes[1] as u64) << 16 + | (bytes[2] as u64) << 8 + | (bytes[3] as u64); + + let counter: u16 = + ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); Some(( crate::timestamp::Timestamp::from_rfc4122(ticks), @@ -901,23 +902,39 @@ impl Uuid { )) } Some(Version::SortMac) => { + let bytes = self.as_bytes(); let ticks: u64 = ((self.as_bytes()[0]) as u64) << 52 - | ((self.as_bytes()[1]) as u64) << 44 - | ((self.as_bytes()[2]) as u64) << 36 - | ((self.as_bytes()[3]) as u64) << 28 - | ((self.as_bytes()[4]) as u64) << 20 - | ((self.as_bytes()[5]) as u64) << 12 - | ((self.as_bytes()[6] & 0xF) as u64) << 8 - | (self.as_bytes()[7] as u64); + | (bytes[1] as u64) << 44 + | (bytes[2] as u64) << 36 + | (bytes[3] as u64) << 28 + | (bytes[4] as u64) << 20 + | (bytes[5] as u64) << 12 + | ((bytes[6] & 0xF) as u64) << 8 + | (bytes[7] as u64); - let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8 - | (self.as_bytes()[9] as u16); + let counter: u16 = + ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); Some(( crate::timestamp::Timestamp::from_rfc4122(ticks), counter, )) } + Some(Version::SortRand) => { + let bytes = self.as_bytes(); + let millis: u64 = (bytes[0] as u64) << 40 + | (bytes[1] as u64) << 32 + | (bytes[2] as u64) << 24 + | (bytes[3] as u64) << 16 + | (bytes[4] as u64) << 8 + | (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, + )) + } _ => None, } } diff --git a/src/timestamp.rs b/src/timestamp.rs index 4257b232..d406d570 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -33,7 +33,7 @@ impl Timestamp { /// `u32` fields representing the seconds, and "subsecond" or fractional /// nanoseconds elapsed since the timestamp's second began, /// respectively. - pub fn from_unix(seconds: u64, nanos: u32) -> Self { + pub const fn from_unix(seconds: u64, nanos: u32) -> Self { Timestamp { seconds, nanos } } From 3dcd4cbd84e409f6f3c85556df426580115168e4 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 10:20:44 -0700 Subject: [PATCH 05/20] fixing features powerset issues --- src/lib.rs | 2 +- src/rng.rs | 2 +- src/timestamp.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a694ba27..703de7d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,7 +227,7 @@ pub mod timestamp; pub use timestamp::{ClockSequence, Timestamp}; -#[cfg(any(feature = "v1", feature = "v3"))] +#[cfg(any(feature = "v1", feature = "v6"))] pub use timestamp::context::Context; #[cfg(feature = "v1")] diff --git a/src/rng.rs b/src/rng.rs index 2cf07894..21a4af32 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -18,7 +18,7 @@ pub(crate) fn bytes() -> [u8; 16] { } } -#[cfg(any(feature = "v1", feature = "v7"))] +#[cfg(any(feature = "v1", feature = "v6", feature = "v7"))] pub(crate) fn u16() -> u16 { #[cfg(not(feature = "fast-rng"))] { diff --git a/src/timestamp.rs b/src/timestamp.rs index d406d570..7b52e49d 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -39,6 +39,7 @@ impl Timestamp { /// Construct a `Timestamp` from the current time of day /// according to Rust's SystemTime + #[cfg(feature = "std")] pub fn now() -> Self { let dur = std::time::SystemTime::UNIX_EPOCH .elapsed() @@ -111,7 +112,7 @@ impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { } /// For features v1 and v1, constructs a `Context` struct which implements the `ClockSequence` trait -#[cfg(any(feature = "v1", feature = "v6"))] +#[cfg(any(feature = "v1", feature = "v6", feature = "v7"))] pub mod context { use std::sync::atomic::{AtomicU16, Ordering}; /// A thread-safe, stateful context for the v1 generator to help ensure From 95a54b8e63fa027b5939db518835ee7678755eb8 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 10:42:54 -0700 Subject: [PATCH 06/20] rem num_traits --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab66a242..5e6f85b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,9 +66,6 @@ fast-rng = ["rng", "private_rand"] sha1 = ["private_sha1_smol"] md5 = ["private_md-5"] -[dependencies] -num-traits = "0.2.15" - # Public: Used in trait impls on `Uuid` [dependencies.serde] default-features = false From 1b14a11c1f4a06f598461bd4dd366e414a4cabd7 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 10:46:21 -0700 Subject: [PATCH 07/20] reverting to 2018 to fix build checks --- Cargo.toml | 2 +- src/v7.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5e6f85b7..b660a5c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ categories = [ ] description = "A library to generate and parse UUIDs." documentation = "https://docs.rs/uuid" -edition = "2021" +edition = "2018" exclude = [ ".github/**" ] diff --git a/src/v7.rs b/src/v7.rs index 9e86abce..c8294dbb 100644 --- a/src/v7.rs +++ b/src/v7.rs @@ -6,6 +6,7 @@ use crate::rng::{bytes, u16}; use crate::timestamp::Timestamp; use crate::{Uuid, Version}; +use core::convert::TryInto; impl Uuid { /// Create a new UUID (version 7) using a time value + random number From 1b8dfdf90bf4ab03531d780b84b2f705ea14eec6 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 11:39:13 -0700 Subject: [PATCH 08/20] remove bytes dep for v8 test --- src/v8.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/v8.rs b/src/v8.rs index 956a7867..856178d3 100644 --- a/src/v8.rs +++ b/src/v8.rs @@ -18,9 +18,6 @@ impl Uuid { /// /// assert_eq!(Some(Version::Custom), uuid.get_version()); /// ``` - /// - /// [`getrandom`]: https://crates.io/crates/getrandom - /// [from_random_bytes]: struct.Builder.html#method.from_random_bytes pub fn new_v8(buf: [u8; 16]) -> Uuid { Builder(Uuid::from_bytes(buf)) .with_variant(Variant::RFC4122) @@ -32,7 +29,6 @@ impl Uuid { #[cfg(test)] mod tests { use super::*; - use crate::rng::bytes; use std::string::ToString; #[cfg(target_arch = "wasm32")] @@ -41,10 +37,17 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let buf = bytes(); + let buf: [u8; 16] = [ + 0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, + 0x2, 0x1, 0x0, + ]; let uuid = Uuid::new_v8(buf); assert_eq!(uuid.get_version(), Some(Version::Custom)); assert_eq!(uuid.get_variant(), Variant::RFC4122); - assert_eq!(uuid.get_version_num(), 8) + assert_eq!(uuid.get_version_num(), 8); + assert_eq!( + uuid.hyphenated().to_string(), + "0f0e0d0c-0b0a-8908-8706-050403020100" + ); } } From dbe1c7cb59ed9e8e47b4bd92005c284ee04070ff Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 17 Aug 2022 14:14:35 -0700 Subject: [PATCH 09/20] re-add atomic dep to support thumbv6m --- Cargo.toml | 10 ++++++++-- src/timestamp.rs | 10 +++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b660a5c1..22958b22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,11 +51,11 @@ repository = "uuid-rs/uuid" default = ["std"] std = [] macro-diagnostics = ["private_uuid-macro-internal"] -v1 = [] +v1 = ["private_atomic"] v3 = ["md5"] v4 = ["rng"] v5 = ["sha1"] -v6 = [] +v6 = ["private_atomic"] v7 = ["rng"] v8 = [] js = ["private_getrandom", "private_getrandom/js"] @@ -134,6 +134,12 @@ version = "1.1.2" path = "macros" optional = true +[dependencies.private_atomic] +package = "atomic" +default-features = false +optional = true +version = "0.5" + [dev-dependencies.bincode] version = "1.0" diff --git a/src/timestamp.rs b/src/timestamp.rs index 7b52e49d..cb4db0bc 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -112,14 +112,14 @@ impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { } /// For features v1 and v1, constructs a `Context` struct which implements the `ClockSequence` trait -#[cfg(any(feature = "v1", feature = "v6", feature = "v7"))] +#[cfg(any(feature = "v1", feature = "v6"))] pub mod context { - use std::sync::atomic::{AtomicU16, Ordering}; + use private_atomic::{Atomic, Ordering}; /// A thread-safe, stateful context for the v1 generator to help ensure /// process-wide uniqueness. #[derive(Debug)] pub struct Context { - count: AtomicU16, + count: Atomic, } impl Context { @@ -133,7 +133,7 @@ pub mod context { /// process. pub const fn new(count: u16) -> Self { Self { - count: AtomicU16::new(count), + count: Atomic::::new(count), } } @@ -151,7 +151,7 @@ pub mod context { #[cfg(feature = "rng")] pub fn new_random() -> Self { Self { - count: AtomicU16::new(crate::rng::u16()), + count: Atomic::::new(crate::rng::u16()), } } } From a2b1258a0480173b4acaa91c10c252e1e0be30af Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Mon, 22 Aug 2022 17:57:48 -0700 Subject: [PATCH 10/20] 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 703de7d1..0641c5cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -878,9 +878,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(); @@ -896,10 +894,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(); @@ -915,10 +910,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(); @@ -930,10 +922,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 9fa752cc..e6bb1b5b 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; @@ -86,13 +86,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 = @@ -116,9 +111,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::*; @@ -131,8 +125,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, ); @@ -143,9 +136,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 = Uuid::parse_str("20616934-4ba2-11e7-8000-010203040506").unwrap(); @@ -167,36 +160,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)); From 81f0c1b7f69452c07a974b46a3b907cf4bcf9b18 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Mon, 22 Aug 2022 19:37:40 -0700 Subject: [PATCH 11/20] making doctests work --- src/lib.rs | 8 +++----- src/timestamp.rs | 3 +++ src/v1.rs | 44 +++++++++++++------------------------------- src/v6.rs | 33 +++++++++------------------------ src/v7.rs | 9 ++++----- 5 files changed, 32 insertions(+), 65 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0641c5cf..ca564da3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,7 +241,7 @@ mod v5; #[cfg(feature = "v6")] mod v6; #[cfg(feature = "v7")] -mod v7; +pub mod v7; #[cfg(feature = "v8")] mod v8; @@ -891,8 +891,7 @@ impl Uuid { | (bytes[2] as u64) << 8 | (bytes[3] as u64); - let counter: u16 = - ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); + let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter)) } @@ -907,8 +906,7 @@ impl Uuid { | ((bytes[6] & 0xF) as u64) << 8 | (bytes[7] as u64); - let counter: u16 = - ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); + let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter)) } diff --git a/src/timestamp.rs b/src/timestamp.rs index 5f2d8d7b..0c5b7dac 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -79,6 +79,9 @@ impl Timestamp { nanos: dur.subsec_nanos(), } } + + /// Construct a `Timestamp` from the current time of day + /// according to Rust's SystemTime #[cfg(all(feature = "std", any(feature = "v1", feature = "v6")))] pub fn now(context: impl ClockSequence) -> Self { let dur = std::time::SystemTime::UNIX_EPOCH diff --git a/src/v1.rs b/src/v1.rs index e6bb1b5b..b166eecf 100644 --- a/src/v1.rs +++ b/src/v1.rs @@ -6,7 +6,6 @@ use crate::timestamp::Timestamp; use crate::Uuid; - /// The number of 100 ns ticks between the UUID epoch /// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`. const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; @@ -46,9 +45,9 @@ impl Uuid { /// # use uuid::Uuid; /// # 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_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), @@ -59,12 +58,11 @@ impl Uuid { /// The timestamp can also be created manually as per RFC4122: /// /// ``` - /// # use uuid::{Timestamp, Context}; - /// # use uuid::Uuid; + /// # use uuid::{Uuid, Timestamp, Context, ClockSequence}; /// let context = Context::new(42); - /// let ts = Timestamp::from_rfc4122(14976234442241191232); + /// let ts = Timestamp::from_rfc4122(14976234442241191232, context.generate_sequence(0, 0)); /// - /// let uuid = Uuid::new_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]); /// /// assert_eq!( /// uuid.hyphenated().to_string(), @@ -78,9 +76,9 @@ impl Uuid { /// # use uuid::{Timestamp, Context}; /// # use uuid::Uuid; /// let context = Context::new(42); - /// let ts = Timestamp::now(); + /// let ts = Timestamp::now(&context); /// - /// let _uuid = Uuid::new_v1(ts, &context, &[1, 2, 3, 4, 5, 6]); + /// let _uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]); /// ``` /// /// [`Timestamp`]: v1/struct.Timestamp.html @@ -90,8 +88,7 @@ impl Uuid { 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 = - (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12); + let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12); let mut d4 = [0; 8]; @@ -124,10 +121,7 @@ mod tests { let node = [1, 2, 3, 4, 5, 6]; let context = Context::new(0); - let uuid = Uuid::new_v1( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); assert_eq!(uuid.get_version(), Some(Version::Mac)); assert_eq!(uuid.get_variant(), Variant::RFC4122); @@ -159,31 +153,19 @@ mod tests { // This context will wrap let context = Context::new((u16::MAX >> 2) - 1); - let uuid1 = Uuid::new_v1( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid1 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); let time: u64 = 1_496_854_536; - let uuid2 = Uuid::new_v1( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid2 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); 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(&context, time, time_fraction), - &node, - ); - let uuid4 = Uuid::new_v1( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid3 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid4 = Uuid::new_v1(Timestamp::from_unix(&context, time, time_fraction), &node); 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 92b8adbb..5b9d97c5 100644 --- a/src/v6.rs +++ b/src/v6.rs @@ -54,9 +54,10 @@ impl Uuid { /// The timestamp can also be created manually as per RFC4122: /// /// ``` - /// # use uuid::{Uuid, Timestamp, Context}; + /// # use uuid::{Uuid, Timestamp, Context, ClockSequence}; + /// # fn random_seed() -> u16 { 42 } /// let context = Context::new(random_seed()); - /// let ts = Timestamp::from_rfc4122(14976241191231231313, context.generate_sequence() ); + /// let ts = Timestamp::from_rfc4122(14976241191231231313, context.generate_sequence(0, 0) ); /// /// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]); /// @@ -114,10 +115,7 @@ mod tests { let node = [1, 2, 3, 4, 5, 6]; let context = Context::new(0); - let uuid = Uuid::new_v6( - Timestamp::from_unix(context, time, time_fraction), - &node, - ); + let uuid = Uuid::new_v6(Timestamp::from_unix(context, time, time_fraction), &node); assert_eq!(uuid.get_version(), Some(Version::SortMac)); assert_eq!(uuid.get_variant(), Variant::RFC4122); @@ -131,8 +129,7 @@ mod tests { 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 = - Uuid::parse_str("1e74ba22-0616-6934-8000-010203040506").unwrap(); + let parsed = Uuid::parse_str("1e74ba22-0616-6934-8000-010203040506").unwrap(); assert_eq!( uuid.get_timestamp().unwrap(), @@ -150,31 +147,19 @@ mod tests { // This context will wrap let context = Context::new((u16::MAX >> 2) - 1); - let uuid1 = Uuid::new_v6( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid1 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node); let time: u64 = 1_496_854_536; - let uuid2 = Uuid::new_v6( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid2 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node); 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(&context, time, time_fraction), - &node, - ); - let uuid4 = Uuid::new_v6( - Timestamp::from_unix(&context, time, time_fraction), - &node, - ); + let uuid3 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node); + let uuid4 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node); 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 9d9e8094..dc4885c0 100644 --- a/src/v7.rs +++ b/src/v7.rs @@ -50,6 +50,9 @@ impl Uuid { } } +/// Dummy struct and ClockSequence implementation to ease the construction of v7 +/// using a Timestamp +#[derive(Debug)] pub struct NullSequence {} impl super::ClockSequence for NullSequence { @@ -73,11 +76,7 @@ mod tests { let time: u64 = 1_496_854_535; let time_fraction: u32 = 812_946_000; - let uuid = Uuid::new_v7(Timestamp::from_unix( - NullSequence {}, - 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)); From acc3f93cd52fd08cba7a3287a76c974cd5caed00 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Thu, 25 Aug 2022 15:05:49 -0700 Subject: [PATCH 12/20] rem whitespace --- .github/workflows/ci.yml | 65 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 604b6c5e..888212c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,6 @@ jobs: - nightly os: - macos-10.15 - - windows-2019 - ubuntu-20.04 rust_target: - x86_64-gnu @@ -69,7 +68,7 @@ jobs: run: cargo hack test --lib --each-feature --optional-deps $STABLE_DEP_FEATURES - name: All version features - run: cargo hack test --lib --features "$VERSION_FEATURES" --each-feature --optional-deps "$STABLE_DEP_FEATURES" + run: cargo hack test --lib --each-feature --features "$VERSION_FEATURES" --optional-deps "$STABLE_DEP_FEATURES" msrv: name: "Tests / MSRV / OS: ${{ matrix.os }}" @@ -78,7 +77,6 @@ jobs: matrix: os: - macos-10.15 - - windows-2019 - ubuntu-20.04 steps: @@ -120,7 +118,6 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v2 - - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: @@ -154,7 +151,6 @@ jobs: with: command: build args: -Z avoid-dev-deps --target thumbv6m-none-eabi --no-default-features - - name: Version features uses: actions-rs/cargo@v1 with: @@ -182,3 +178,62 @@ jobs: - name: Powerset run: cargo hack check --each-feature -Z avoid-dev-deps + win_tests: + name: "Tests / OS: Windows 2019 - ${{ matrix.channel }}-${{ matrix.rust_target }}" + runs-on: windows-2019 + env: + RUSTFLAGS: "--cfg uuid_unstable" + RUSTDOCFLAGS: "--cfg uuid_unstable" + strategy: + matrix: + channel: + - stable + - beta + - nightly + os: + - windows-2019 + rust_target: + - x86_64-gnu + - x86_64-msvc + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Rust Toolchain + uses: actions-rs/toolchain@v1 + with: + override: true + profile: minimal + toolchain: ${{ matrix.channel }}-${{ matrix.rust_target }} + + - name: Install cargo-hack + run: cargo install cargo-hack + + - name: Docs + run: cargo test --all-features --doc + + - name: Examples + run: cargo test --all-features --examples + + - name: Each version feature + run: cargo hack test --lib --each-feature --optional-deps $env:STABLE_DEP_FEATURES + + - name: All version features + run: cargo hack test --lib --each-feature --features "$env:VERSION_FEATURES" --optional-deps "$env:STABLE_DEP_FEATURES" + + win-msrv: + name: "Tests / MSRV / OS: Windows 2019" + runs-on: windows-2019 + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.57.0 + override: true + + - name: Version features + run: cargo test --features "$env:VERSION_FEATURES $env:STABLE_DEP_FEATURES" From c99fce1ce077c2f0f9a2d83ec1e469e857a61547 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Thu, 25 Aug 2022 15:26:33 -0700 Subject: [PATCH 13/20] adding v6 and v7 to updated CI --- .github/workflows/ci.yml | 20 ++++++++++---------- Cargo.toml | 11 +++++------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 888212c0..cd7556ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: Continuous integration env: - VERSION_FEATURES: "v1 v3 v4 v5" + VERSION_FEATURES: "v1 v3 v4 v5 v6 v7" STABLE_DEP_FEATURES: "serde arbitrary" on: @@ -39,11 +39,11 @@ jobs: os: - macos-10.15 - ubuntu-20.04 - rust_target: + rust_target: - x86_64-gnu - x86_64-msvc - x86_64-apple-darwin - + steps: - name: Checkout repository uses: actions/checkout@v2 @@ -66,7 +66,7 @@ jobs: - name: Each version feature run: cargo hack test --lib --each-feature --optional-deps $STABLE_DEP_FEATURES - + - name: All version features run: cargo hack test --lib --each-feature --features "$VERSION_FEATURES" --optional-deps "$STABLE_DEP_FEATURES" @@ -108,10 +108,10 @@ jobs: - name: Version features run: wasm-pack test --node -- --features "$VERION_FEATURES $STABLE_DEP_FEATURES js" - + - name: Fast RNG run: wasm-pack test --node -- --features "js v4 fast-rng" - + mips: name: Tests / MIPS (Big Endian) runs-on: ubuntu-latest @@ -155,7 +155,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: -Z avoid-dev-deps --target thumbv6m-none-eabi --no-default-features --features "v1 v3 v5 serde" + args: -Z avoid-dev-deps --target thumbv6m-none-eabi --no-default-features --features "v1 v3 v5 v6 v7 serde" nodeps: name: Build / No deps @@ -180,7 +180,7 @@ jobs: run: cargo hack check --each-feature -Z avoid-dev-deps win_tests: name: "Tests / OS: Windows 2019 - ${{ matrix.channel }}-${{ matrix.rust_target }}" - runs-on: windows-2019 + runs-on: windows-2019 env: RUSTFLAGS: "--cfg uuid_unstable" RUSTDOCFLAGS: "--cfg uuid_unstable" @@ -192,7 +192,7 @@ jobs: - nightly os: - windows-2019 - rust_target: + rust_target: - x86_64-gnu - x86_64-msvc steps: @@ -223,7 +223,7 @@ jobs: win-msrv: name: "Tests / MSRV / OS: Windows 2019" - runs-on: windows-2019 + runs-on: windows-2019 steps: - name: Checkout sources uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 0c87a27a..7827fbc6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,14 +51,15 @@ repository = "uuid-rs/uuid" default = ["std"] std = [] macro-diagnostics = ["private_uuid-macro-internal"] - -# NOTE: When adding new features, check the `ci.yml` workflow +# NOTE: When adding new features, check the `ci.yml` workflow .. # and include them where necessary (you can follow along with existing features) v1 = ["private_atomic"] v3 = ["md5"] v4 = ["rng"] v5 = ["sha1"] - +v6 = ["private_atomic"] +v7 = ["rng"] +v8 = [] js = ["private_getrandom", "private_getrandom/js"] rng = ["private_getrandom"] @@ -86,7 +87,7 @@ version = "1" # Public (unstable): Used in `zerocopy` derive # Unstable: also need RUSTFLAGS="--cfg uuid_unstable" to work # This feature may break between releases, or be removed entirely before -# stabilization. +# stabilization. # See: https://github.com/uuid-rs/uuid/issues/588 [dependencies.zerocopy] optional = true @@ -135,8 +136,6 @@ version = "1.1.2" path = "macros" optional = true -# Private -# Don't depend on this optional feature directly: it may change at any time [dependencies.private_atomic] package = "atomic" default-features = false From 09a73b81978292734f660edbb999602bf0566a32 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Thu, 25 Aug 2022 15:34:23 -0700 Subject: [PATCH 14/20] v7 won't work by default on thumbv6m --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd7556ea..4a2be3c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: -Z avoid-dev-deps --target thumbv6m-none-eabi --no-default-features --features "v1 v3 v5 v6 v7 serde" + args: -Z avoid-dev-deps --target thumbv6m-none-eabi --no-default-features --features "v1 v3 v5 v6 serde" nodeps: name: Build / No deps From f3ca816f27a1b95ed1b81e5aa00cb9b11b5eef38 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Mon, 29 Aug 2022 11:57:52 -0700 Subject: [PATCH 15/20] updating messaging on timestamp::now() --- src/timestamp.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/timestamp.rs b/src/timestamp.rs index 0c5b7dac..609d7d68 100644 --- a/src/timestamp.rs +++ b/src/timestamp.rs @@ -69,11 +69,13 @@ impl Timestamp { /// Construct a `Timestamp` from the current time of day /// according to Rust's SystemTime + /// NOTE: This function will panic if the current system time is earlier than + /// the Unix Epoch of 1970-01-01 00:00:00 So please use caution when time travelling. #[cfg(all(feature = "std", not(any(feature = "v1", feature = "v6"))))] pub fn now() -> Self { let dur = std::time::SystemTime::UNIX_EPOCH .elapsed() - .expect("Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality"); + .expect("Getting elapsed time since UNIX_EPOCH failed. This is due to a system time that is somehow earlier than Unix Epoch."); Timestamp { seconds: dur.as_secs(), nanos: dur.subsec_nanos(), From f4beda42b358d648a54c37cb2aa83775e5abe802 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Tue, 30 Aug 2022 09:08:17 -0700 Subject: [PATCH 16/20] Builder struct no pub --- src/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index bc155f6c..bc628db7 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -40,7 +40,7 @@ use crate::{error::*, Bytes, Uuid, Variant, Version}; /// ``` #[allow(missing_copy_implementations)] #[derive(Debug)] -pub struct Builder(pub Uuid); +pub struct Builder(Uuid); impl Uuid { /// The 'nil UUID'. From 4811a0632f7242183fd02f1a6378d2797ee21dd7 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 31 Aug 2022 11:32:36 -0700 Subject: [PATCH 17/20] Update src/v6.rs doc Co-authored-by: Aron Heinecke --- src/v6.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v6.rs b/src/v6.rs index 5b9d97c5..e4b56271 100644 --- a/src/v6.rs +++ b/src/v6.rs @@ -1,6 +1,6 @@ //! The implementation for Version 1 UUIDs. //! -//! Note that you need to enable the `v1` Cargo feature +//! Note that you need to enable the `v6` Cargo feature //! in order to use this module. use crate::timestamp::{ClockSequence, Timestamp}; From 9a7c5e9b97b1c7ff334dbecf136a15846f50bb10 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 31 Aug 2022 11:32:46 -0700 Subject: [PATCH 18/20] Update src/v7.rs doc Co-authored-by: Aron Heinecke --- src/v7.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v7.rs b/src/v7.rs index dc4885c0..6f96fef8 100644 --- a/src/v7.rs +++ b/src/v7.rs @@ -1,6 +1,6 @@ //! The implementation for Version 1 UUIDs. //! -//! Note that you need to enable the `v1` Cargo feature +//! Note that you need to enable the `v7` Cargo feature //! in order to use this module. use crate::rng::{bytes, u16}; From 1e47d4063104c37c2bd220f74040adb5585474f0 Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 31 Aug 2022 12:20:05 -0700 Subject: [PATCH 19/20] fix v8 example --- src/v8.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v8.rs b/src/v8.rs index 856178d3..f6a0005a 100644 --- a/src/v8.rs +++ b/src/v8.rs @@ -19,7 +19,7 @@ impl Uuid { /// assert_eq!(Some(Version::Custom), uuid.get_version()); /// ``` pub fn new_v8(buf: [u8; 16]) -> Uuid { - Builder(Uuid::from_bytes(buf)) + Builder::from_bytes(buf) .with_variant(Variant::RFC4122) .with_version(Version::Custom) .into_uuid() From 954e2793a5e5d39cab1e99140bd16353a9fed9de Mon Sep 17 00:00:00 2001 From: Rick Richardson Date: Wed, 31 Aug 2022 12:28:31 -0700 Subject: [PATCH 20/20] arbitrary appears to depend on an unstable array_from_fn in 1.1.4, locking the version to 1.1.3 for now --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7827fbc6..de7ccb69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ version = "2" # Public: Used in trait impls on `Uuid` [dependencies.arbitrary] optional = true -version = "1" +version = "=1.1.3" # Public (unstable): Used in `zerocopy` derive # Unstable: also need RUSTFLAGS="--cfg uuid_unstable" to work