mirror of
https://github.com/uuid-rs/uuid.git
synced 2025-09-30 06:21:02 +00:00
Merge pull request #625 from KodrAus/chore/api-docs-and-deprecations
Some work on the new version features
This commit is contained in:
commit
7e4c88a2b1
@ -48,6 +48,7 @@ status = "actively-developed"
|
||||
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"]
|
||||
@ -55,8 +56,9 @@ v3 = ["md5"]
|
||||
v4 = ["rng"]
|
||||
v5 = ["sha1"]
|
||||
v6 = ["private_atomic"]
|
||||
v7 = ["rng"]
|
||||
v7 = ["private_atomic", "rng"]
|
||||
v8 = []
|
||||
|
||||
js = ["private_getrandom", "private_getrandom/js"]
|
||||
|
||||
rng = ["private_getrandom"]
|
||||
|
15
examples/sortable_uuid.rs
Normal file
15
examples/sortable_uuid.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//! Generating a sortable UUID.
|
||||
//!
|
||||
//! If you enable the `v7` feature you can generate sortable UUIDs.
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "v7")]
|
||||
fn generate_sortable_uuid() {
|
||||
use uuid::Uuid;
|
||||
|
||||
let uuid = Uuid::now_v7();
|
||||
|
||||
assert_eq!(Some(uuid::Version::SortRand), uuid.get_version());
|
||||
}
|
||||
|
||||
fn main() {}
|
116
src/builder.rs
116
src/builder.rs
@ -13,7 +13,7 @@
|
||||
//!
|
||||
//! [`Uuid`]: ../struct.Uuid.html
|
||||
|
||||
use crate::{error::*, Bytes, Uuid, Variant, Version};
|
||||
use crate::{error::*, timestamp, Bytes, Uuid, Variant, Version};
|
||||
|
||||
/// A builder struct for creating a UUID.
|
||||
///
|
||||
@ -43,12 +43,14 @@ use crate::{error::*, Bytes, Uuid, Variant, Version};
|
||||
pub struct Builder(Uuid);
|
||||
|
||||
impl Uuid {
|
||||
/// The 'nil UUID'.
|
||||
/// The 'nil UUID' (all zeros).
|
||||
///
|
||||
/// The nil UUID is a special form of UUID that is specified to have all
|
||||
/// 128 bits set to zero, as defined in [IETF RFC 4122 Section 4.1.7][RFC].
|
||||
/// 128 bits set to zero.
|
||||
///
|
||||
/// [RFC]: https://tools.ietf.org/html/rfc4122.html#section-4.1.7
|
||||
/// # References
|
||||
///
|
||||
/// * [Nil UUID in RFC4122]: https://tools.ietf.org/html/rfc4122.html#section-4.1.7
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -67,12 +69,14 @@ impl Uuid {
|
||||
Uuid::from_bytes([0; 16])
|
||||
}
|
||||
|
||||
/// The 'max UUID'.
|
||||
/// The 'max UUID' (all ones).
|
||||
///
|
||||
/// 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].
|
||||
/// 128 bits set to one.
|
||||
///
|
||||
/// [Draft RFC]: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#page-12
|
||||
/// # References
|
||||
///
|
||||
/// * [Max UUID in Draft RFC: New UUID Formats, Version 4]: https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -141,6 +145,8 @@ impl Uuid {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// # use uuid::Uuid;
|
||||
/// let d1 = 0xa1a2a3a4;
|
||||
@ -537,11 +543,22 @@ impl Builder {
|
||||
Builder(Uuid::from_bytes_le(b))
|
||||
}
|
||||
|
||||
/// Creates a `Builder` using the supplied random bytes.
|
||||
/// Creates a `Builder` for a version 1 UUID using the supplied timestamp and node id.
|
||||
pub const fn from_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
|
||||
Builder(timestamp::encode_rfc4122_timestamp(ticks, counter, node_id))
|
||||
}
|
||||
|
||||
/// Creates a `Builder` for a version 3 UUID using the supplied MD5 hashed bytes.
|
||||
pub const fn from_md5_bytes(md5_bytes: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(md5_bytes))
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Md5)
|
||||
}
|
||||
|
||||
/// Creates a `Builder` for a version 4 UUID using the supplied random bytes.
|
||||
///
|
||||
/// This method can be useful in environments where the `v4` feature isn't
|
||||
/// available. This method will take care of setting the appropriate
|
||||
/// version and variant fields.
|
||||
/// This method assumes the bytes are already sufficiently random, it will only
|
||||
/// set the appropriate bits for the UUID version and variant.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -557,26 +574,79 @@ impl Builder {
|
||||
/// assert_eq!(Some(Version::Random), uuid.get_version());
|
||||
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
||||
/// ```
|
||||
pub const fn from_random_bytes(b: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(b))
|
||||
pub const fn from_random_bytes(random_bytes: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(random_bytes))
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Random)
|
||||
}
|
||||
|
||||
/// Creates a `Builder` using the supplied MD5 hashed bytes.
|
||||
pub const fn from_md5_bytes(b: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(b))
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Md5)
|
||||
}
|
||||
|
||||
/// Creates a `Builder` using the supplied SHA1 hashed bytes.
|
||||
pub const fn from_sha1_bytes(b: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(b))
|
||||
/// Creates a `Builder` for a version 5 UUID using the supplied SHA-1 hashed bytes.
|
||||
///
|
||||
/// This method assumes the bytes are already a SHA-1 hash, it will only set the appropriate
|
||||
/// bits for the UUID version and variant.
|
||||
pub const fn from_sha1_bytes(sha1_bytes: Bytes) -> Self {
|
||||
Builder(Uuid::from_bytes(sha1_bytes))
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Sha1)
|
||||
}
|
||||
|
||||
/// Creates a `Builder` for a version 6 UUID using the supplied timestamp and node id.
|
||||
///
|
||||
/// This method will encode the ticks, counter, and node id in a sortable UUID.
|
||||
pub const fn from_sorted_rfc4122_timestamp(
|
||||
ticks: u64,
|
||||
counter: u16,
|
||||
node_id: &[u8; 6],
|
||||
) -> Self {
|
||||
Builder(timestamp::encode_sorted_rfc4122_timestamp(
|
||||
ticks, counter, node_id,
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a `Builder` for a version 7 UUID using the supplied Unix timestamp and random bytes.
|
||||
///
|
||||
/// This method assumes the bytes are already sufficiently random.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a UUID using the current system timestamp:
|
||||
///
|
||||
/// ```
|
||||
/// # use std::convert::TryInto;
|
||||
/// use std::time::{Duration, SystemTime};
|
||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// # use uuid::{Builder, Uuid, Variant, Version, Timestamp, NoContext};
|
||||
/// # let rng = || [
|
||||
/// # 70, 235, 208, 238, 14, 109, 67, 201, 185, 13
|
||||
/// # ];
|
||||
/// let ts = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||
///
|
||||
/// let random_bytes = rng();
|
||||
///
|
||||
/// let uuid = Builder::from_unix_timestamp_millis(ts.as_millis().try_into()?, &random_bytes).into_uuid();
|
||||
///
|
||||
/// assert_eq!(Some(Version::SortRand), uuid.get_version());
|
||||
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub const fn from_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Self {
|
||||
Builder(timestamp::encode_unix_timestamp_millis(
|
||||
millis,
|
||||
random_bytes,
|
||||
))
|
||||
}
|
||||
|
||||
/// Creates a `Builder` for a version 8 UUID using the supplied user-defined bytes.
|
||||
///
|
||||
/// This method won't interpret the given bytes in any way, except to set the appropriate
|
||||
/// bits for the UUID version and variant.
|
||||
pub const fn from_custom_bytes(custom_bytes: Bytes) -> Self {
|
||||
Builder::from_bytes(custom_bytes)
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Custom)
|
||||
}
|
||||
|
||||
/// Creates a `Builder` using the supplied bytes.
|
||||
///
|
||||
/// # Errors
|
||||
|
160
src/lib.rs
160
src/lib.rs
@ -30,6 +30,9 @@
|
||||
//! practical purposes, it can be assumed that an unintentional collision would
|
||||
//! be extremely unlikely.
|
||||
//!
|
||||
//! UUIDs have a number of standardized encodings that are specified in [RFC4122](http://tools.ietf.org/html/rfc4122),
|
||||
//! with recent additions [in draft](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04).
|
||||
//!
|
||||
//! # Getting started
|
||||
//!
|
||||
//! Add the following to your `Cargo.toml`:
|
||||
@ -65,21 +68,32 @@
|
||||
//! const ID: Uuid = uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8");
|
||||
//! ```
|
||||
//!
|
||||
//! # Dependencies
|
||||
//! # Working with different UUID versions
|
||||
//!
|
||||
//! By default, this crate depends on nothing but `std` and can parse and format
|
||||
//! UUIDs, but cannot generate them. You need to enable the following Cargo
|
||||
//! features to enable various pieces of functionality:
|
||||
//! This library supports all standardized methods for generating UUIDs through individual Cargo features.
|
||||
//!
|
||||
//! * `v1` - adds the [`Uuid::new_v1`] function and the ability to create a V1
|
||||
//! UUID using an implementation of [`v1::ClockSequence`] (usually
|
||||
//! [`v1::Context`]) and a UNIX timestamp.
|
||||
//! * `v3` - adds the [`Uuid::new_v3`] function and the ability to create a V3
|
||||
//! UUID based on the MD5 hash of some data.
|
||||
//! * `v4` - adds the [`Uuid::new_v4`] function and the ability to randomly
|
||||
//! generate a UUID.
|
||||
//! * `v5` - adds the [`Uuid::new_v5`] function and the ability to create a V5
|
||||
//! UUID based on the SHA1 hash of some data.
|
||||
//! By default, this crate depends on nothing but the Rust standard library and can parse and format
|
||||
//! UUIDs, but cannot generate them. Depending on the kind of UUID you'd like to work with, there
|
||||
//! are Cargo features that enable generating them:
|
||||
//!
|
||||
//! * `v1` - Version 1 UUIDs using a timestamp and monotonic counter.
|
||||
//! * `v3` - Version 3 UUIDs based on the MD5 hash of some data.
|
||||
//! * `v4` - Version 4 UUIDs with random data.
|
||||
//! * `v5` - Version 5 UUIDs based on the SHA1 hash of some data.
|
||||
//! * `v6` - Version 6 UUIDs using a timestamp and monotonic counter.
|
||||
//! * `v7` - Version 7 UUIDs using a Unix timestamp.
|
||||
//! * `v8` - Version 8 UUIDs using user-defined data.
|
||||
//!
|
||||
//! This library also includes a [`Builder`] type that can be used to help construct UUIDs of any
|
||||
//! version without any additional dependencies or features.
|
||||
//!
|
||||
//! ## Which UUID version should I use?
|
||||
//!
|
||||
//! If you just want to generate unique identifiers then consider version 4 (`v4`) UUIDs. If you want
|
||||
//! to use UUIDs as database keys or need to sort them then consider version 7 (`v7`) UUIDs.
|
||||
//! Other versions should generally be avoided unless there's an existing need for them.
|
||||
//!
|
||||
//! # Other features
|
||||
//!
|
||||
//! Other crate features can also be useful beyond the version support:
|
||||
//!
|
||||
@ -88,11 +102,11 @@
|
||||
//! `serde`.
|
||||
//! * `arbitrary` - adds an `Arbitrary` trait implementation to `Uuid` for
|
||||
//! fuzzing.
|
||||
//! * `fast-rng` - when combined with `v4` uses a faster algorithm for
|
||||
//! generating random UUIDs. This feature requires more dependencies to
|
||||
//! compile, but is just as suitable for UUIDs as the default algorithm.
|
||||
//! * `fast-rng` - uses a faster algorithm for generating random UUIDs.
|
||||
//! This feature requires more dependencies to compile, but is just as suitable for
|
||||
//! UUIDs as the default algorithm.
|
||||
//!
|
||||
//! ## Unstable features
|
||||
//! # Unstable features
|
||||
//!
|
||||
//! Some features are unstable. They may be incomplete or depend on other
|
||||
//! unstable libraries. These include:
|
||||
@ -114,8 +128,7 @@
|
||||
//!
|
||||
//! ## WebAssembly
|
||||
//!
|
||||
//! For WebAssembly, enable the `js` feature along with `v4` for a
|
||||
//! source of randomness:
|
||||
//! For WebAssembly, enable the `js` feature:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies.uuid]
|
||||
@ -126,9 +139,6 @@
|
||||
//! ]
|
||||
//! ```
|
||||
//!
|
||||
//! You don't need the `js` feature to use `uuid` in WebAssembly if you're
|
||||
//! not also enabling `v4`.
|
||||
//!
|
||||
//! ## Embedded
|
||||
//!
|
||||
//! For embedded targets without the standard library, you'll need to
|
||||
@ -142,10 +152,10 @@
|
||||
//!
|
||||
//! Some additional features are supported in no-std environments:
|
||||
//!
|
||||
//! * `v1`, `v3`, and `v5`
|
||||
//! * `serde`
|
||||
//! * `v1`, `v3`, `v5`, `v6`, and `v8`.
|
||||
//! * `serde`.
|
||||
//!
|
||||
//! If you need to use `v4` in a no-std environment, you'll need to
|
||||
//! If you need to use `v4` or `v7` in a no-std environment, you'll need to
|
||||
//! follow [`getrandom`'s docs] on configuring a source of randomness
|
||||
//! on currently unsupported targets. Alternatively, you can produce
|
||||
//! random bytes yourself and then pass them to [`Builder::from_random_bytes`]
|
||||
@ -153,7 +163,7 @@
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! To parse a UUID given in the simple format and print it as a urn:
|
||||
//! To parse a UUID given in the simple format and print it as a URN:
|
||||
//!
|
||||
//! ```
|
||||
//! # use uuid::Uuid;
|
||||
@ -182,17 +192,11 @@
|
||||
//! # References
|
||||
//!
|
||||
//! * [Wikipedia: Universally Unique Identifier](http://en.wikipedia.org/wiki/Universally_unique_identifier)
|
||||
//! * [RFC4122: A Universally Unique IDentifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122)
|
||||
//! * [RFC4122: A Universally Unique Identifier (UUID) URN Namespace](http://tools.ietf.org/html/rfc4122)
|
||||
//! * [Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04)
|
||||
//!
|
||||
//! [`wasm-bindgen`]: https://crates.io/crates/wasm-bindgen
|
||||
//! [`cargo-web`]: https://crates.io/crates/cargo-web
|
||||
//! [`Uuid`]: struct.Uuid.html
|
||||
//! [`Uuid::new_v1`]: struct.Uuid.html#method.new_v1
|
||||
//! [`Uuid::new_v3`]: struct.Uuid.html#method.new_v3
|
||||
//! [`Uuid::new_v4`]: struct.Uuid.html#method.new_v4
|
||||
//! [`Uuid::new_v5`]: struct.Uuid.html#method.new_v5
|
||||
//! [`v1::ClockSequence`]: v1/trait.ClockSequence.html
|
||||
//! [`v1::Context`]: v1/struct.Context.html
|
||||
//! [`getrandom`'s docs]: https://docs.rs/getrandom
|
||||
|
||||
#![no_std]
|
||||
@ -220,17 +224,20 @@ use zerocopy::{AsBytes, FromBytes, Unaligned};
|
||||
|
||||
mod builder;
|
||||
mod error;
|
||||
pub mod fmt;
|
||||
mod parser;
|
||||
/// contains the `Timestamp` struct and `ClockSequence` trait
|
||||
|
||||
pub mod fmt;
|
||||
pub mod timestamp;
|
||||
|
||||
pub use timestamp::{ClockSequence, Timestamp};
|
||||
pub use timestamp::{context::NoContext, ClockSequence, Timestamp};
|
||||
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
pub use timestamp::context::Context;
|
||||
|
||||
#[cfg(feature = "v1")]
|
||||
#[doc(hidden)]
|
||||
// Soft-deprecated (Rust doesn't support deprecating re-exports)
|
||||
// Use `Context` from the crate root instead
|
||||
pub mod v1;
|
||||
#[cfg(feature = "v3")]
|
||||
mod v3;
|
||||
@ -241,7 +248,7 @@ mod v5;
|
||||
#[cfg(feature = "v6")]
|
||||
mod v6;
|
||||
#[cfg(feature = "v7")]
|
||||
pub mod v7;
|
||||
mod v7;
|
||||
#[cfg(feature = "v8")]
|
||||
mod v8;
|
||||
|
||||
@ -257,6 +264,7 @@ mod external;
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
extern crate alloc;
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "macro-diagnostics")]
|
||||
pub extern crate private_uuid_macro_internal;
|
||||
@ -280,9 +288,9 @@ pub type Bytes = [u8; 16];
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Version {
|
||||
/// Special case for `nil` UUID.
|
||||
/// The _nil_ (all zeros) UUID.
|
||||
Nil = 0,
|
||||
/// Version 1: MAC address.
|
||||
/// Version 1: Timestamp and node ID.
|
||||
Mac,
|
||||
/// Version 2: DCE Security.
|
||||
Dce,
|
||||
@ -292,11 +300,11 @@ pub enum Version {
|
||||
Random,
|
||||
/// Version 5: SHA-1 hash.
|
||||
Sha1,
|
||||
/// Version 6: Sortable MAC/Node-ID
|
||||
/// Version 6: Sortable Timestamp and node ID.
|
||||
SortMac,
|
||||
/// Version 7: Timestamp + Random
|
||||
/// Version 7: Timestamp and random.
|
||||
SortRand,
|
||||
/// Version 8: Custom
|
||||
/// Version 8: Custom.
|
||||
Custom,
|
||||
}
|
||||
|
||||
@ -877,61 +885,37 @@ impl Uuid {
|
||||
/// [`Timestamp`] offers several options for converting the raw RFC4122
|
||||
/// value into more commonly-used formats, such as a unix timestamp.
|
||||
///
|
||||
/// # Roundtripping
|
||||
///
|
||||
/// This method is unlikely to roundtrip a timestamp in a UUID due to the way
|
||||
/// UUIDs encode timestamps. The timestamp returned from this method will be truncated to
|
||||
/// 100ns precision for version 1 and 6 UUIDs, and to millisecond precision for version 7 UUIDs.
|
||||
///
|
||||
/// [`Timestamp`]: v1/struct.Timestamp.html
|
||||
pub const fn get_timestamp(&self) -> Option<crate::timestamp::Timestamp> {
|
||||
pub const fn get_timestamp(&self) -> Option<Timestamp> {
|
||||
match self.get_version() {
|
||||
Some(Version::Mac) => {
|
||||
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 (ticks, counter) = timestamp::decode_rfc4122_timestamp(self);
|
||||
|
||||
let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
||||
|
||||
Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter))
|
||||
Some(Timestamp::from_rfc4122(ticks, counter))
|
||||
}
|
||||
Some(Version::SortMac) => {
|
||||
let bytes = self.as_bytes();
|
||||
let ticks: u64 = ((self.as_bytes()[0]) as u64) << 52
|
||||
| (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 (ticks, counter) = timestamp::decode_sorted_rfc4122_timestamp(self);
|
||||
|
||||
let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
||||
|
||||
Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter))
|
||||
Some(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 millis = timestamp::decode_unix_timestamp_millis(self);
|
||||
|
||||
let seconds = millis / 1000;
|
||||
let nanos = ((millis % 1000) * 1_000_000) as u32;
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
{
|
||||
Some(Timestamp {
|
||||
seconds,
|
||||
nanos,
|
||||
counter: 0,
|
||||
})
|
||||
}
|
||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||
{
|
||||
Some(Timestamp { seconds, nanos })
|
||||
}
|
||||
|
||||
Some(Timestamp {
|
||||
seconds,
|
||||
nanos,
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
counter: 0,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -1162,7 +1146,7 @@ mod tests {
|
||||
let uuid1 = new();
|
||||
let s = uuid1.hyphenated().to_string();
|
||||
|
||||
assert!(s.len() == 36);
|
||||
assert_eq!(36, s.len());
|
||||
assert!(s.chars().all(|c| c.is_digit(16) || c == '-'));
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ pub(crate) fn bytes() -> [u8; 16] {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "v1", feature = "v6", feature = "v7"))]
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
pub(crate) fn u16() -> u16 {
|
||||
#[cfg(not(feature = "fast-rng"))]
|
||||
{
|
||||
|
415
src/timestamp.rs
415
src/timestamp.rs
@ -1,11 +1,42 @@
|
||||
/// Contains the `Timestamp` struct and `ClockSequence` traits
|
||||
/// as well as an implementation of ClockSequence for Context (only for features v1 and v6)
|
||||
//! Generating UUIDs from timestamps.
|
||||
//!
|
||||
//! Timestamps are used in a few UUID versions as a source of decentralized
|
||||
//! uniqueness (as in versions 1 and 6), and as a way to enable sorting (as
|
||||
//! in versions 6 and 7). Timestamps aren't encoded the same way by all UUID
|
||||
//! versions so this module provides a single [`Timestamp`] type that can
|
||||
//! convert between them.
|
||||
//!
|
||||
//! # Timestamp representations in UUIDs
|
||||
//!
|
||||
//! Versions 1 and 6 UUIDs use a bespoke timestamp that consists of the
|
||||
//! number of 100ns ticks since `1582-10-15 00:00:00`, along with
|
||||
//! a counter value to avoid duplicates.
|
||||
//!
|
||||
//! Version 7 UUIDs use a more standard timestamp that consists of the
|
||||
//! number of millisecond ticks since the Unix epoch (`1970-01-01 00:00:00`).
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
|
||||
//! * [Timestamp in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-6.1)
|
||||
|
||||
/// 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`.
|
||||
use crate::Uuid;
|
||||
|
||||
/// The number of 100 nanosecond ticks between the RFC4122 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
|
||||
|
||||
/// A timestamp that can be encoded into a UUID.
|
||||
///
|
||||
/// This type abstracts the specific encoding, so versions 1, 6, and 7
|
||||
/// UUIDs can both be supported through the same type, even
|
||||
/// though they have a different representation of a timestamp.
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
|
||||
/// * [Timestamp in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-6.1)
|
||||
/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Timestamp {
|
||||
pub(crate) seconds: u64,
|
||||
@ -15,93 +46,72 @@ pub struct Timestamp {
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
/// Construct a `Timestamp` from its raw component values: an RFC4122
|
||||
/// timestamp and counter.
|
||||
/// Get a timestamp representing the current system time.
|
||||
///
|
||||
/// 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, _counter: u16) -> Self {
|
||||
let (seconds, nanos) = Self::rfc4122_to_unix(ticks);
|
||||
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
{
|
||||
Timestamp {
|
||||
seconds,
|
||||
nanos,
|
||||
counter: _counter,
|
||||
}
|
||||
}
|
||||
/// This method defers to the standard library's `SystemTime` type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if calculating the elapsed time since the Unix epoch fails.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
|
||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||
{
|
||||
Timestamp { seconds, nanos }
|
||||
let _ = context;
|
||||
}
|
||||
|
||||
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(),
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
counter: context.generate_sequence(dur.as_secs(), dur.subsec_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(
|
||||
_context: impl ClockSequence<Output = u16>,
|
||||
seconds: u64,
|
||||
nanos: u32,
|
||||
) -> Self {
|
||||
/// Construct a `Timestamp` from an RFC4122 timestamp and counter, as used
|
||||
/// in versions 1 and 6 UUIDs.
|
||||
pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
|
||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||
{
|
||||
let _ = counter;
|
||||
}
|
||||
|
||||
let (seconds, nanos) = Self::rfc4122_to_unix(ticks);
|
||||
|
||||
Timestamp {
|
||||
seconds,
|
||||
nanos,
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
counter,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a `Timestamp` from a Unix timestamp, as used in version 7 UUIDs.
|
||||
pub fn from_unix(context: impl ClockSequence<Output = u16>, seconds: u64, nanos: u32) -> Self {
|
||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||
{
|
||||
let _ = context;
|
||||
|
||||
Timestamp { seconds, nanos }
|
||||
}
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
{
|
||||
let counter = _context.generate_sequence(seconds, nanos);
|
||||
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
|
||||
/// 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 failed. This is due to a system time that is somehow earlier than Unix Epoch.");
|
||||
Timestamp {
|
||||
seconds: dur.as_secs(),
|
||||
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<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.
|
||||
/// Get the value of the timestamp as an RFC4122 timestamp and counter,
|
||||
/// as used in versions 1 and 6 UUIDs.
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
pub const fn to_rfc4122(&self) -> (u64, u16) {
|
||||
(
|
||||
@ -110,103 +120,242 @@ impl Timestamp {
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the timestamp converted to the seconds and fractional
|
||||
/// nanoseconds since Jan 1 1970.
|
||||
/// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs.
|
||||
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
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
const fn unix_to_rfc4122_ticks(seconds: u64, nanos: u32) -> u64 {
|
||||
let ticks = UUID_TICKS_BETWEEN_EPOCHS
|
||||
+ seconds * 10_000_000
|
||||
+ nanos as u64 / 100;
|
||||
let ticks = UUID_TICKS_BETWEEN_EPOCHS + 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,
|
||||
)
|
||||
}
|
||||
|
||||
#[deprecated(note = "use `to_unix` instead")]
|
||||
/// Get the number of fractional nanoseconds in the Unix timestamp.
|
||||
///
|
||||
/// This method is deprecated and probably doesn't do what you're expecting it to.
|
||||
/// It doesn't return the timestamp as nanoseconds since the Unix epoch, it returns
|
||||
/// the fractional seconds of the timestamp.
|
||||
pub const fn to_unix_nanos(&self) -> u32 {
|
||||
// NOTE: This method never did what it said on the tin: instead of
|
||||
// converting the timestamp into nanos it simply returned the nanoseconds
|
||||
// part of the timestamp.
|
||||
//
|
||||
// We can't fix the behavior because the return type is too small to fit
|
||||
// a useful value for nanoseconds since the epoch.
|
||||
self.nanos
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values.
|
||||
pub(crate) const fn encode_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Uuid {
|
||||
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] = (((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_low, time_mid, time_high_and_version, &d4)
|
||||
}
|
||||
|
||||
pub(crate) const fn decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
|
||||
let bytes = uuid.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);
|
||||
|
||||
(ticks, counter)
|
||||
}
|
||||
|
||||
pub(crate) const fn encode_sorted_rfc4122_timestamp(
|
||||
ticks: u64,
|
||||
counter: u16,
|
||||
node_id: &[u8; 6],
|
||||
) -> Uuid {
|
||||
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)
|
||||
}
|
||||
|
||||
pub(crate) const fn decode_sorted_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
|
||||
let bytes = uuid.as_bytes();
|
||||
|
||||
let ticks: u64 = ((bytes[0]) as u64) << 52
|
||||
| (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 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
||||
|
||||
(ticks, counter)
|
||||
}
|
||||
|
||||
pub(crate) const fn encode_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Uuid {
|
||||
let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
|
||||
let millis_low = (millis & 0xFFFF) as u16;
|
||||
|
||||
let random_and_version =
|
||||
(random_bytes[0] as u16 | ((random_bytes[1] as u16) << 8) & 0x0FFF) | (0x7 << 12);
|
||||
|
||||
let mut d4 = [0; 8];
|
||||
|
||||
d4[0] = (random_bytes[2] & 0x3F) | 0x80;
|
||||
d4[1] = random_bytes[3];
|
||||
d4[2] = random_bytes[4];
|
||||
d4[3] = random_bytes[5];
|
||||
d4[4] = random_bytes[6];
|
||||
d4[5] = random_bytes[7];
|
||||
d4[6] = random_bytes[8];
|
||||
d4[7] = random_bytes[9];
|
||||
|
||||
Uuid::from_fields(millis_high, millis_low, random_and_version, &d4)
|
||||
}
|
||||
|
||||
pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 {
|
||||
let bytes = uuid.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);
|
||||
|
||||
millis
|
||||
}
|
||||
|
||||
/// A counter that can be used by version 1 and version 6 UUIDs to support
|
||||
/// the uniqueness of timestamps.
|
||||
///
|
||||
/// # 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
|
||||
/// The type of sequence returned by this counter.
|
||||
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 generate_sequence(
|
||||
&self,
|
||||
seconds: u64,
|
||||
subsec_nanos: u32,
|
||||
) -> Self::Output;
|
||||
|
||||
/// Get the next value in the sequence to feed into a timestamp.
|
||||
///
|
||||
/// This method will be called each time a [`Timestamp`] is constructed.
|
||||
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 generate_sequence(
|
||||
&self,
|
||||
seconds: u64,
|
||||
subsec_nanos: u32,
|
||||
) -> Self::Output {
|
||||
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
|
||||
(**self).generate_sequence(seconds, subsec_nanos)
|
||||
}
|
||||
}
|
||||
|
||||
/// For features v1 and v1, constructs a `Context` struct which implements the `ClockSequence` trait
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
/// Default implementations for the [`ClockSequence`] trait.
|
||||
pub mod context {
|
||||
use super::ClockSequence;
|
||||
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
use private_atomic::{Atomic, Ordering};
|
||||
/// A thread-safe, stateful context for the v1 generator to help ensure
|
||||
/// process-wide uniqueness.
|
||||
|
||||
/// An empty counter that will always return the value `0`.
|
||||
///
|
||||
/// This type should be used when constructing timestamps for version 7 UUIDs,
|
||||
/// since they don't need a counter for uniqueness.
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct NoContext;
|
||||
|
||||
impl ClockSequence for NoContext {
|
||||
type Output = u16;
|
||||
|
||||
fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
|
||||
static CONTEXT: Context = Context {
|
||||
count: Atomic::new(0),
|
||||
};
|
||||
|
||||
#[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
|
||||
static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false);
|
||||
|
||||
#[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
|
||||
pub(crate) fn shared_context() -> &'static Context {
|
||||
// If the context is in its initial state then assign it to a random value
|
||||
// It doesn't matter if multiple threads observe `false` here and initialize the context
|
||||
if CONTEXT_INITIALIZED
|
||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_ok()
|
||||
{
|
||||
CONTEXT.count.store(crate::rng::u16(), Ordering::Release);
|
||||
}
|
||||
|
||||
&CONTEXT
|
||||
}
|
||||
|
||||
/// A thread-safe, wrapping counter that produces 14-bit numbers.
|
||||
///
|
||||
/// This type should be used when constructing version 1 and version 6 UUIDs.
|
||||
#[derive(Debug)]
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
pub struct Context {
|
||||
count: Atomic<u16>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
impl Context {
|
||||
/// Creates a thread-safe, internally mutable context to help ensure
|
||||
/// uniqueness.
|
||||
/// Construct a new context that's initialized with the given value.
|
||||
///
|
||||
/// 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.
|
||||
/// The starting value should be a random number, so that UUIDs from
|
||||
/// different systems with the same timestamps are less likely to collide.
|
||||
/// When the `rng` feature is enabled, prefer the [`Context::new_random`] method.
|
||||
pub const fn new(count: u16) -> Self {
|
||||
Self {
|
||||
count: Atomic::<u16>::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.
|
||||
/// Construct a new context that's initialized with a random value.
|
||||
#[cfg(feature = "rng")]
|
||||
pub fn new_random() -> Self {
|
||||
Self {
|
||||
@ -215,13 +364,11 @@ pub mod context {
|
||||
}
|
||||
}
|
||||
|
||||
impl super::ClockSequence for Context {
|
||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||
impl ClockSequence for Context {
|
||||
type Output = u16;
|
||||
fn generate_sequence(
|
||||
&self,
|
||||
_seconds: u64,
|
||||
_nanos: u32,
|
||||
) -> 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
|
||||
|
67
src/v1.rs
67
src/v1.rs
@ -1,36 +1,40 @@
|
||||
//! The implementation for Version 1 UUIDs.
|
||||
//!
|
||||
//! Note that you need to enable the `v1` Cargo feature
|
||||
//! in order to use this module.
|
||||
//! This module is soft-deprecated. Instead of using the `Context` type re-exported here,
|
||||
//! use the one from the crate root.
|
||||
|
||||
use crate::timestamp::Timestamp;
|
||||
use crate::Uuid;
|
||||
use crate::{Builder, Timestamp, 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;
|
||||
|
||||
/// 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 +
|
||||
/// *NodeId*.
|
||||
/// Create a new version 1 UUID using the current system time and a node id.
|
||||
///
|
||||
/// This method is only available if both the `std` and `rng` features are enabled.
|
||||
///
|
||||
/// This method is a convenient alternative to [`Uuid::new_v1`] that uses the current system time
|
||||
/// as the source timestamp.
|
||||
#[cfg(all(feature = "std", feature = "rng"))]
|
||||
pub fn now_v1(node_id: &[u8; 6]) -> Self {
|
||||
let ts = Timestamp::now(crate::timestamp::context::shared_context());
|
||||
|
||||
Self::new_v1(ts, node_id)
|
||||
}
|
||||
|
||||
/// Create a new version 1 UUID using the given timestamp and node id.
|
||||
///
|
||||
/// 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
|
||||
/// 1. The *node id* is unique for this process,
|
||||
/// 2. The *context* is shared across all threads which are generating version 1
|
||||
/// 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 `v1` feature of this crate
|
||||
/// to be enabled.
|
||||
///
|
||||
@ -40,7 +44,7 @@ impl Uuid {
|
||||
/// [`ClockSequence`]. RFC4122 requires the clock sequence
|
||||
/// is seeded with a random value:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```
|
||||
/// # use uuid::{Timestamp, Context};
|
||||
/// # use uuid::Uuid;
|
||||
/// # fn random_seed() -> u16 { 42 }
|
||||
@ -70,38 +74,17 @@ impl Uuid {
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The timestamp can also just use the current SystemTime
|
||||
/// # References
|
||||
///
|
||||
/// ```rust
|
||||
/// # use uuid::{Timestamp, Context};
|
||||
/// # use uuid::Uuid;
|
||||
/// let context = Context::new(42);
|
||||
/// let ts = Timestamp::now(&context);
|
||||
///
|
||||
/// let _uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
/// * [Version 1 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.2)
|
||||
///
|
||||
/// [`Timestamp`]: v1/struct.Timestamp.html
|
||||
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
||||
/// [`Context`]: v1/struct.Context.html
|
||||
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 = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 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_low, time_mid, time_high_and_version, &d4)
|
||||
Builder::from_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +98,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn test_new_v1() {
|
||||
fn test_new() {
|
||||
let time: u64 = 1_496_854_535;
|
||||
let time_fraction: u32 = 812_946_000;
|
||||
let node = [1, 2, 3, 4, 5, 6];
|
||||
@ -145,7 +128,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn test_new_v1_context() {
|
||||
fn test_new_context() {
|
||||
let time: u64 = 1_496_854_535;
|
||||
let time_fraction: u32 = 812_946_000;
|
||||
let node = [1, 2, 3, 4, 5, 6];
|
||||
|
@ -25,6 +25,10 @@ impl Uuid {
|
||||
/// assert_eq!(Some(Version::Md5), uuid.get_version());
|
||||
/// ```
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// * [Version 3 and 5 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.3)
|
||||
///
|
||||
/// [`NAMESPACE_DNS`]: #associatedconstant.NAMESPACE_DNS
|
||||
/// [`NAMESPACE_OID`]: #associatedconstant.NAMESPACE_OID
|
||||
/// [`NAMESPACE_URL`]: #associatedconstant.NAMESPACE_URL
|
||||
|
@ -24,6 +24,10 @@ impl Uuid {
|
||||
/// assert_eq!(Some(Version::Random), uuid.get_version());
|
||||
/// ```
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// * [Version 4 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.4)
|
||||
///
|
||||
/// [`getrandom`]: https://crates.io/crates/getrandom
|
||||
/// [from_random_bytes]: struct.Builder.html#method.from_random_bytes
|
||||
pub fn new_v4() -> Uuid {
|
||||
@ -35,7 +39,6 @@ impl Uuid {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Variant, Version};
|
||||
use std::string::ToString;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::*;
|
||||
|
@ -24,6 +24,10 @@ impl Uuid {
|
||||
/// assert_eq!(Some(Version::Sha1), uuid.get_version());
|
||||
/// ```
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// * [Version 3 and 5 UUIDs in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.3)
|
||||
///
|
||||
/// [`NAMESPACE_DNS`]: struct.Uuid.html#associatedconst.NAMESPACE_DNS
|
||||
/// [`NAMESPACE_OID`]: struct.Uuid.html#associatedconst.NAMESPACE_OID
|
||||
/// [`NAMESPACE_URL`]: struct.Uuid.html#associatedconst.NAMESPACE_URL
|
||||
|
50
src/v6.rs
50
src/v6.rs
@ -1,18 +1,27 @@
|
||||
//! The implementation for Version 1 UUIDs.
|
||||
//! The implementation for Version 6 UUIDs.
|
||||
//!
|
||||
//! Note that you need to enable the `v6` 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;
|
||||
use crate::{Builder, Timestamp, Uuid};
|
||||
|
||||
impl Uuid {
|
||||
/// Create a new UUID (version 6) using a time value + sequence +
|
||||
/// Create a new version 6 UUID using the current time value and a node id.
|
||||
///
|
||||
/// This method is only available if the `std` feature is enabled.
|
||||
///
|
||||
/// This method is a convenient alternative to [`Uuid::new_v6`] that uses the current system time
|
||||
/// as the source timestamp.
|
||||
#[cfg(all(feature = "std", feature = "rng"))]
|
||||
pub fn now_v6(node_id: &[u8; 6]) -> Self {
|
||||
let ts = Timestamp::now(crate::timestamp::context::shared_context());
|
||||
|
||||
Self::new_v6(ts, node_id)
|
||||
}
|
||||
|
||||
/// Create a new version 6 UUID using a time value + sequence +
|
||||
/// *NodeId*.
|
||||
/// This is similar to UUIDv1, except that it is lexographically sortable by timestamp.
|
||||
/// This is similar to UUIDv1, except that it is lexicographically sortable by timestamp.
|
||||
///
|
||||
/// When generating [`Timestamp`]s using a [`ClockSequence`], this function
|
||||
/// is only guaranteed to produce unique values if the following conditions
|
||||
@ -66,42 +75,25 @@ impl Uuid {
|
||||
/// "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);
|
||||
/// # References
|
||||
///
|
||||
/// let uuid = Uuid::new_v6(ts, &context, &[1, 2, 3, 4, 5, 6]);
|
||||
/// * [Version 6 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.1)
|
||||
///
|
||||
/// [`Timestamp`]: v1/struct.Timestamp.html
|
||||
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
||||
/// [`Context`]: v1/struct.Context.html
|
||||
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);
|
||||
|
||||
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)
|
||||
Builder::from_sorted_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Variant, Version};
|
||||
use crate::{Context, Variant, Version};
|
||||
use std::string::ToString;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
88
src/v7.rs
88
src/v7.rs
@ -1,28 +1,39 @@
|
||||
//! The implementation for Version 1 UUIDs.
|
||||
//! The implementation for Version 7 UUIDs.
|
||||
//!
|
||||
//! Note that you need to enable the `v7` Cargo feature
|
||||
//! in order to use this module.
|
||||
|
||||
use crate::rng::{bytes, u16};
|
||||
use crate::timestamp::Timestamp;
|
||||
use crate::{Uuid, Version};
|
||||
use crate::{rng, timestamp::Timestamp, Builder, NoContext, Uuid};
|
||||
use core::convert::TryInto;
|
||||
|
||||
impl Uuid {
|
||||
/// Create a new UUID (version 7) using a time value + random number
|
||||
/// Create a new version 7 UUID using the current time value and random bytes.
|
||||
///
|
||||
/// This method is a convenient alternative to [`Uuid::new_v7`] that uses the current system time
|
||||
/// as the source timestamp.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn now_v7() -> Self {
|
||||
Self::new_v7(Timestamp::now(NoContext))
|
||||
}
|
||||
|
||||
/// Create a new version 7 UUID using a time value and random bytes.
|
||||
///
|
||||
/// When the `std` feature is enabled, you can also use [`Uuid::now_v7`].
|
||||
///
|
||||
/// Note that usage of this method requires the `v7` feature of this crate
|
||||
/// to be enabled.
|
||||
///
|
||||
/// Also see [`Uuid::now_v7`] for a convenient way to generate version 7
|
||||
/// UUIDs using the current system time.
|
||||
///
|
||||
/// # 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};
|
||||
/// # use uuid::v7::NullSequence;
|
||||
/// let ts = Timestamp::from_unix(NullSequence {}, 1497624119, 1234);
|
||||
/// # use uuid::{Uuid, Timestamp, NoContext};
|
||||
/// let ts = Timestamp::from_unix(NoContext, 1497624119, 1234);
|
||||
///
|
||||
/// let uuid = Uuid::new_v7(ts);
|
||||
///
|
||||
@ -31,61 +42,58 @@ impl Uuid {
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// The timestamp can also be created automatically from the current SystemTime
|
||||
/// # References
|
||||
///
|
||||
/// let ts = Timestamp::now();
|
||||
///
|
||||
/// let uuid = Uuid::new_v7(ts);
|
||||
///
|
||||
/// [`Timestamp`]: v1/struct.Timestamp.html
|
||||
/// * [Version 7 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.2)
|
||||
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)
|
||||
}
|
||||
}
|
||||
let (secs, nanos) = ts.to_unix();
|
||||
let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000);
|
||||
|
||||
/// Dummy struct and ClockSequence implementation to ease the construction of v7
|
||||
/// using a Timestamp
|
||||
#[derive(Debug)]
|
||||
pub struct NullSequence {}
|
||||
|
||||
impl super::ClockSequence for NullSequence {
|
||||
type Output = u16;
|
||||
fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
|
||||
0
|
||||
Builder::from_unix_timestamp_millis(millis, &rng::bytes()[..10].try_into().unwrap())
|
||||
.into_uuid()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Variant, Version};
|
||||
use std::string::ToString;
|
||||
use crate::{std::string::ToString, NoContext, Variant, Version};
|
||||
#[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 ts: u64 = 1645557742000;
|
||||
|
||||
let uuid = Uuid::new_v7(Timestamp::from_unix(NullSequence {}, time, time_fraction));
|
||||
let seconds = ts / 1000;
|
||||
let nanos = ((ts % 1000) * 1_000_000) as u32;
|
||||
|
||||
let uuid = Uuid::new_v7(Timestamp::from_unix(NoContext, seconds, nanos));
|
||||
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"));
|
||||
assert!(uuid.hyphenated().to_string().starts_with("017f22e2-79b0-7"));
|
||||
|
||||
// Ensure parsing the same UUID produces the same timestamp
|
||||
let parsed = Uuid::parse_str(uustr.as_str()).unwrap();
|
||||
|
||||
assert_eq!(uuid, parsed,);
|
||||
assert_eq!(uuid, parsed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn test_new_v7_timestamp_roundtrip() {
|
||||
let time: u64 = 1_496_854_535;
|
||||
let time_fraction: u32 = 812_000_000;
|
||||
|
||||
let ts = Timestamp::from_unix(NoContext, time, time_fraction);
|
||||
|
||||
let uuid = Uuid::new_v7(ts);
|
||||
|
||||
let decoded_ts = uuid.get_timestamp().unwrap();
|
||||
|
||||
assert_eq!(ts.to_unix(), decoded_ts.to_unix());
|
||||
}
|
||||
}
|
||||
|
15
src/v8.rs
15
src/v8.rs
@ -1,4 +1,4 @@
|
||||
use crate::{Builder, Uuid, Variant, Version};
|
||||
use crate::{Builder, Uuid};
|
||||
|
||||
impl Uuid {
|
||||
/// Creates a custom UUID comprised almost entirely of user-supplied bytes
|
||||
@ -18,17 +18,19 @@ impl Uuid {
|
||||
///
|
||||
/// assert_eq!(Some(Version::Custom), uuid.get_version());
|
||||
/// ```
|
||||
///
|
||||
/// # References
|
||||
///
|
||||
/// * [Version 8 UUIDs in Draft RFC: New UUID Formats, Version 4](https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.3)
|
||||
pub fn new_v8(buf: [u8; 16]) -> Uuid {
|
||||
Builder::from_bytes(buf)
|
||||
.with_variant(Variant::RFC4122)
|
||||
.with_version(Version::Custom)
|
||||
.into_uuid()
|
||||
Builder::from_custom_bytes(buf).into_uuid()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{Variant, Version};
|
||||
use std::string::ToString;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -38,8 +40,7 @@ mod tests {
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn test_new() {
|
||||
let buf: [u8; 16] = [
|
||||
0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3,
|
||||
0x2, 0x1, 0x0,
|
||||
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));
|
||||
|
Loading…
x
Reference in New Issue
Block a user