mirror of
https://github.com/uuid-rs/uuid.git
synced 2025-10-02 15:24:57 +00:00
reorganize time code a bit
This commit is contained in:
parent
9d445c7c94
commit
fbf463fdd1
@ -13,8 +13,7 @@
|
|||||||
//!
|
//!
|
||||||
//! [`Uuid`]: ../struct.Uuid.html
|
//! [`Uuid`]: ../struct.Uuid.html
|
||||||
|
|
||||||
use core::time::Duration;
|
use crate::{error::*, Bytes, Uuid, Variant, Version};
|
||||||
use crate::{error::*, Bytes, Uuid, Variant, Version, Timestamp};
|
|
||||||
|
|
||||||
/// A builder struct for creating a UUID.
|
/// A builder struct for creating a UUID.
|
||||||
///
|
///
|
||||||
@ -539,30 +538,13 @@ impl Builder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `Builder` for a version 1 UUID using the supplied timestamp and node id.
|
/// Creates a `Builder` for a version 1 UUID using the supplied timestamp and node id.
|
||||||
pub const fn from_rfc4122_timestamp(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
pub const fn from_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Self {
|
||||||
let (ticks, counter) = ts.to_rfc4122();
|
Builder(crate::v1::encode_rfc4122_timestamp(ticks, counter, node_id))
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
Self::from_fields(time_low, time_mid, time_high_and_version, &d4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `Builder` for a version 3 UUID using the supplied MD5 hashed bytes.
|
/// Creates a `Builder` for a version 3 UUID using the supplied MD5 hashed bytes.
|
||||||
pub const fn from_md5_bytes(b: Bytes) -> Self {
|
pub const fn from_md5_bytes(md5_bytes: Bytes) -> Self {
|
||||||
Builder(Uuid::from_bytes(b))
|
Builder(Uuid::from_bytes(md5_bytes))
|
||||||
.with_variant(Variant::RFC4122)
|
.with_variant(Variant::RFC4122)
|
||||||
.with_version(Version::Md5)
|
.with_version(Version::Md5)
|
||||||
}
|
}
|
||||||
@ -586,8 +568,8 @@ impl Builder {
|
|||||||
/// assert_eq!(Some(Version::Random), uuid.get_version());
|
/// assert_eq!(Some(Version::Random), uuid.get_version());
|
||||||
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn from_random_bytes(b: Bytes) -> Self {
|
pub const fn from_random_bytes(random_bytes: Bytes) -> Self {
|
||||||
Builder(Uuid::from_bytes(b))
|
Builder(Uuid::from_bytes(random_bytes))
|
||||||
.with_variant(Variant::RFC4122)
|
.with_variant(Variant::RFC4122)
|
||||||
.with_version(Version::Random)
|
.with_version(Version::Random)
|
||||||
}
|
}
|
||||||
@ -596,8 +578,8 @@ impl Builder {
|
|||||||
///
|
///
|
||||||
/// This method assumes the bytes are already a SHA1 hash, it will only set the appropriate
|
/// This method assumes the bytes are already a SHA1 hash, it will only set the appropriate
|
||||||
/// bits for the UUID version and variant.
|
/// bits for the UUID version and variant.
|
||||||
pub const fn from_sha1_bytes(b: Bytes) -> Self {
|
pub const fn from_sha1_bytes(sha1_bytes: Bytes) -> Self {
|
||||||
Builder(Uuid::from_bytes(b))
|
Builder(Uuid::from_bytes(sha1_bytes))
|
||||||
.with_variant(Variant::RFC4122)
|
.with_variant(Variant::RFC4122)
|
||||||
.with_version(Version::Sha1)
|
.with_version(Version::Sha1)
|
||||||
}
|
}
|
||||||
@ -605,25 +587,14 @@ impl Builder {
|
|||||||
/// Creates a `Builder` for a version 6 UUID using the supplied timestamp and node id.
|
/// 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.
|
/// This method will encode the ticks, counter, and node id in a sortable UUID.
|
||||||
pub const fn from_sorted_rfc4122_timestamp(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
pub const fn from_sorted_rfc4122_timestamp(
|
||||||
let (ticks, counter) = ts.to_rfc4122();
|
ticks: u64,
|
||||||
|
counter: u16,
|
||||||
let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32;
|
node_id: &[u8; 6],
|
||||||
let time_mid = ((ticks >> 12) & 0xFFFF) as u16;
|
) -> Self {
|
||||||
let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12);
|
Builder(crate::v6::encode_sorted_rfc4122_timestamp(
|
||||||
|
ticks, counter, node_id,
|
||||||
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];
|
|
||||||
|
|
||||||
Self::from_fields(time_high, time_mid, time_low_and_version, &d4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `Builder` for a version 7 UUID using the supplied Unix timestamp.
|
/// Creates a `Builder` for a version 7 UUID using the supplied Unix timestamp.
|
||||||
@ -637,48 +608,36 @@ impl Builder {
|
|||||||
/// Creating a UUID using the current system timestamp:
|
/// Creating a UUID using the current system timestamp:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use std::time::{Duration, SystemTime};
|
||||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
/// # use uuid::{Builder, Uuid, Variant, Version, Timestamp, NoContext};
|
/// # use uuid::{Builder, Uuid, Variant, Version, Timestamp, NoContext};
|
||||||
/// # let rng = || [
|
/// # let rng = || [
|
||||||
/// # 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204,
|
/// # 70, 235, 208, 238, 14, 109, 67, 201, 185, 13
|
||||||
/// # ];
|
/// # ];
|
||||||
/// let ts = Timestamp::now(NoContext);
|
/// let ts = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
|
///
|
||||||
/// let random_bytes = rng();
|
/// let random_bytes = rng();
|
||||||
///
|
///
|
||||||
/// let uuid = Builder::from_timestamp_millis(ts, &random_bytes).into_uuid();
|
/// let uuid = Builder::from_unix_timestamp(ts.as_secs(), ts.subsec_millis(), &random_bytes).into_uuid();
|
||||||
///
|
///
|
||||||
/// assert_eq!(Some(Version::SortRand), uuid.get_version());
|
/// assert_eq!(Some(Version::SortRand), uuid.get_version());
|
||||||
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
/// assert_eq!(Variant::RFC4122, uuid.get_variant());
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub const fn from_timestamp_millis(ts: Timestamp, random_bytes: &[u8; 11]) -> Self {
|
pub const fn from_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Self {
|
||||||
let millis = Duration::new(ts.seconds, ts.nanos).as_millis() as u64;
|
Builder(crate::v7::encode_unix_timestamp_millis(
|
||||||
let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
|
millis,
|
||||||
let millis_low = (millis & 0xFFFF) as u16;
|
random_bytes,
|
||||||
|
))
|
||||||
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];
|
|
||||||
|
|
||||||
Self::from_fields(millis_high, millis_low, random_and_version, &d4)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `Builder` for a version 8 UUID using the supplied user-defined 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
|
/// This method won't interpret the given bytes in any way, except to set the appropriate
|
||||||
/// bits for the UUID version and variant.
|
/// bits for the UUID version and variant.
|
||||||
pub const fn from_custom_bytes(b: Bytes) -> Self {
|
pub const fn from_custom_bytes(custom_bytes: Bytes) -> Self {
|
||||||
Builder::from_bytes(b)
|
Builder::from_bytes(custom_bytes)
|
||||||
.with_variant(Variant::RFC4122)
|
.with_variant(Variant::RFC4122)
|
||||||
.with_version(Version::Custom)
|
.with_version(Version::Custom)
|
||||||
}
|
}
|
||||||
|
52
src/lib.rs
52
src/lib.rs
@ -228,7 +228,7 @@ mod parser;
|
|||||||
pub mod fmt;
|
pub mod fmt;
|
||||||
pub mod timestamp;
|
pub mod timestamp;
|
||||||
|
|
||||||
pub use timestamp::{ClockSequence, Timestamp, context::NoContext};
|
pub use timestamp::{context::NoContext, ClockSequence, Timestamp};
|
||||||
|
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
pub use timestamp::context::Context;
|
pub use timestamp::context::Context;
|
||||||
@ -884,61 +884,31 @@ impl Uuid {
|
|||||||
/// value into more commonly-used formats, such as a unix timestamp.
|
/// value into more commonly-used formats, such as a unix timestamp.
|
||||||
///
|
///
|
||||||
/// [`Timestamp`]: v1/struct.Timestamp.html
|
/// [`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() {
|
match self.get_version() {
|
||||||
Some(Version::Mac) => {
|
Some(Version::Mac) => {
|
||||||
let bytes = self.as_bytes();
|
let (ticks, counter) = v1::decode_rfc4122_timestamp(self);
|
||||||
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(Timestamp::from_rfc4122(ticks, counter))
|
||||||
|
|
||||||
Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter))
|
|
||||||
}
|
}
|
||||||
Some(Version::SortMac) => {
|
Some(Version::SortMac) => {
|
||||||
let bytes = self.as_bytes();
|
let (ticks, counter) = v6::decode_sorted_rfc4122_timestamp(self);
|
||||||
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 counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
|
Some(Timestamp::from_rfc4122(ticks, counter))
|
||||||
|
|
||||||
Some(crate::timestamp::Timestamp::from_rfc4122(ticks, counter))
|
|
||||||
}
|
}
|
||||||
Some(Version::SortRand) => {
|
Some(Version::SortRand) => {
|
||||||
let bytes = self.as_bytes();
|
let millis = v7::decode_unix_timestamp_millis(self);
|
||||||
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 seconds = millis / 1000;
|
||||||
let nanos = ((millis % 1000) * 1_000_000) as u32;
|
let nanos = ((millis % 1000) * 1_000_000) as u32;
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
|
||||||
{
|
|
||||||
Some(Timestamp {
|
Some(Timestamp {
|
||||||
seconds,
|
seconds,
|
||||||
nanos,
|
nanos,
|
||||||
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
counter: 0,
|
counter: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
|
||||||
{
|
|
||||||
Some(Timestamp { seconds, nanos })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1168,7 +1138,7 @@ mod tests {
|
|||||||
let uuid1 = new();
|
let uuid1 = new();
|
||||||
let s = uuid1.hyphenated().to_string();
|
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 == '-'));
|
assert!(s.chars().all(|c| c.is_digit(16) || c == '-'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
220
src/timestamp.rs
220
src/timestamp.rs
@ -1,11 +1,20 @@
|
|||||||
//! Generating UUIDs from timestamps.
|
//! 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.
|
||||||
|
|
||||||
/// The number of 100 ns ticks between the UUID epoch
|
/// 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`.
|
/// (`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;
|
pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;
|
||||||
|
|
||||||
/// Stores the number of seconds since epoch,
|
/// A timestamp that can be encoded into a UUID.
|
||||||
/// as well as the fractional nanoseconds of that second
|
///
|
||||||
|
/// This type abstracts the specific encoding, so a UUID version 1 or
|
||||||
|
/// a UUID version 7 can both be supported through the same type, even
|
||||||
|
/// though they have a different representation of a timestamp.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Timestamp {
|
pub struct Timestamp {
|
||||||
pub(crate) seconds: u64,
|
pub(crate) seconds: u64,
|
||||||
@ -15,13 +24,30 @@ pub struct Timestamp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Timestamp {
|
impl Timestamp {
|
||||||
/// Construct a `Timestamp` from its raw component values: an RFC4122
|
/// Get a timestamp representing the current system time.
|
||||||
/// timestamp and counter.
|
|
||||||
///
|
///
|
||||||
/// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format
|
/// This method defers to the standard library's `SystemTime` type.
|
||||||
/// as the number of 100-nanosecond intervals elapsed since 00:00:00.00,
|
#[cfg(feature = "std")]
|
||||||
/// 15 Oct 1582, "the date of the Gregorian reform of the Christian
|
pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
|
||||||
/// calendar."
|
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||||
|
{
|
||||||
|
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 an RFC4122 timestamp and counter, as used
|
||||||
|
/// in version 1 and version 6 UUIDs.
|
||||||
pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
|
pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
|
||||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||||
{
|
{
|
||||||
@ -38,19 +64,8 @@ impl Timestamp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a `Timestamp` from a unix timestamp
|
/// Construct a `Timestamp` from a Unix timestamp.
|
||||||
///
|
pub fn from_unix(context: impl ClockSequence<Output = u16>, seconds: u64, nanos: u32) -> Self {
|
||||||
/// 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 {
|
|
||||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
||||||
{
|
{
|
||||||
let _ = context;
|
let _ = context;
|
||||||
@ -69,33 +84,8 @@ impl Timestamp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a `Timestamp` from the current time of day
|
/// Get the value of the timestamp as an RFC4122 timestamp and counter,
|
||||||
/// according to Rust's SystemTime.
|
/// as used in version 1 and version 6 UUIDs.
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
|
|
||||||
#[cfg(not(any(feature = "v1", feature = "v6")))]
|
|
||||||
{
|
|
||||||
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()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
pub const fn to_rfc4122(&self) -> (u64, u16) {
|
pub const fn to_rfc4122(&self) -> (u64, u16) {
|
||||||
(
|
(
|
||||||
@ -104,14 +94,32 @@ impl Timestamp {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the timestamp converted to the seconds and fractional
|
/// Get the value of the timestamp as a Unix timestamp, consisting of the
|
||||||
/// nanoseconds since Jan 1 1970.
|
/// number of whole and fractional seconds.
|
||||||
pub const fn to_unix(&self) -> (u64, u32) {
|
pub const fn to_unix(&self) -> (u64, u32) {
|
||||||
(self.seconds, self.nanos)
|
(self.seconds, self.nanos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
ticks
|
||||||
|
}
|
||||||
|
|
||||||
|
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")]
|
#[deprecated(note = "use `to_unix` instead")]
|
||||||
#[doc(hidden)]
|
/// 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 {
|
pub const fn to_unix_nanos(&self) -> u32 {
|
||||||
// NOTE: This method never did what it said on the tin: instead of
|
// NOTE: This method never did what it said on the tin: instead of
|
||||||
// converting the timestamp into nanos it simply returned the nanoseconds
|
// converting the timestamp into nanos it simply returned the nanoseconds
|
||||||
@ -121,64 +129,42 @@ impl Timestamp {
|
|||||||
// a useful value for nanoseconds since the epoch.
|
// a useful value for nanoseconds since the epoch.
|
||||||
self.nanos
|
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;
|
|
||||||
|
|
||||||
ticks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// convert rfc4122 ticks into unix-timestamp
|
/// A counter that can be used by version 1 and version 6 UUIDs to support
|
||||||
const fn rfc4122_to_unix(ticks: u64) -> (u64, u32) {
|
/// the uniqueness of timestamps.
|
||||||
(
|
|
||||||
(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 counter values used in UUID timestamps.
|
|
||||||
///
|
///
|
||||||
/// # References
|
/// # References
|
||||||
///
|
///
|
||||||
/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
|
/// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
|
||||||
pub trait ClockSequence {
|
pub trait ClockSequence {
|
||||||
/// The primitive type you wish out output
|
/// The type of sequence returned by this counter.
|
||||||
type 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
|
/// Get the next value in the sequence to feed into a timestamp.
|
||||||
/// the last time a clock sequence was requested.
|
///
|
||||||
fn generate_sequence(
|
/// This method will be called each time a [`Timestamp`] is constructed.
|
||||||
&self,
|
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output;
|
||||||
seconds: u64,
|
|
||||||
subsec_nanos: u32,
|
|
||||||
) -> Self::Output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
|
impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
|
||||||
type Output = T::Output;
|
type Output = T::Output;
|
||||||
fn generate_sequence(
|
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
|
||||||
&self,
|
|
||||||
seconds: u64,
|
|
||||||
subsec_nanos: u32,
|
|
||||||
) -> Self::Output {
|
|
||||||
(**self).generate_sequence(seconds, subsec_nanos)
|
(**self).generate_sequence(seconds, subsec_nanos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For features v1 and v6, constructs a `Context` struct which implements the `ClockSequence` trait.
|
/// Default implementations for the [`ClockSequence`] trait.
|
||||||
pub mod context {
|
pub mod context {
|
||||||
use super::ClockSequence;
|
use super::ClockSequence;
|
||||||
|
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
use private_atomic::{Atomic, Ordering};
|
use private_atomic::{Atomic, Ordering};
|
||||||
|
|
||||||
/// A clock sequence that never produces a counter value to deduplicate equal timestamps with.
|
/// 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)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct NoContext;
|
pub struct NoContext;
|
||||||
|
|
||||||
@ -190,8 +176,31 @@ pub mod context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A thread-safe, stateful context for the v1 generator to help ensure
|
#[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
|
||||||
/// process-wide uniqueness.
|
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)]
|
#[derive(Debug)]
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
@ -200,31 +209,18 @@ pub mod context {
|
|||||||
|
|
||||||
#[cfg(any(feature = "v1", feature = "v6"))]
|
#[cfg(any(feature = "v1", feature = "v6"))]
|
||||||
impl Context {
|
impl Context {
|
||||||
/// Creates a thread-safe, internally mutable context to help ensure
|
/// Construct a new context that's initialized with the given value.
|
||||||
/// uniqueness.
|
|
||||||
///
|
///
|
||||||
/// This is a context which can be shared across threads. It maintains an
|
/// The starting value should be a random number, so that UUIDs from
|
||||||
/// internal counter that is incremented at every request, the value ends
|
/// different systems with the same timestamps are less likely to collide.
|
||||||
/// up in the clock_seq portion of the UUID (the fourth group). This
|
/// When the `rng` feature is enabled, prefer the [`Context::new_random`] method.
|
||||||
/// will improve the probability that the UUID is unique across the
|
|
||||||
/// process.
|
|
||||||
pub const fn new(count: u16) -> Self {
|
pub const fn new(count: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
count: Atomic::<u16>::new(count),
|
count: Atomic::<u16>::new(count),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a thread-safe, internally mutable context that's seeded with a
|
/// Construct a new context that's initialized with a random value.
|
||||||
/// 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")]
|
#[cfg(feature = "rng")]
|
||||||
pub fn new_random() -> Self {
|
pub fn new_random() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -237,11 +233,7 @@ pub mod context {
|
|||||||
impl ClockSequence for Context {
|
impl ClockSequence for Context {
|
||||||
type Output = u16;
|
type Output = u16;
|
||||||
|
|
||||||
fn generate_sequence(
|
fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
|
||||||
&self,
|
|
||||||
_seconds: u64,
|
|
||||||
_nanos: u32,
|
|
||||||
) -> Self::Output {
|
|
||||||
// RFC4122 reserves 2 bits of the clock sequence so the actual
|
// RFC4122 reserves 2 bits of the clock sequence so the actual
|
||||||
// maximum value is smaller than `u16::MAX`. Since we unconditionally
|
// maximum value is smaller than `u16::MAX`. Since we unconditionally
|
||||||
// increment the clock sequence we want to wrap once it becomes larger
|
// increment the clock sequence we want to wrap once it becomes larger
|
||||||
|
68
src/v1.rs
68
src/v1.rs
@ -1,31 +1,39 @@
|
|||||||
//! The implementation for Version 1 UUIDs.
|
//! The implementation for Version 1 UUIDs.
|
||||||
//!
|
//!
|
||||||
//! Note that you need to enable the `v1` Cargo feature
|
//! This module is soft-deprecated. Instead of using the `Context` type re-exported here,
|
||||||
//! in order to use this module.
|
//! use the one from the crate root.
|
||||||
|
|
||||||
|
use crate::timestamp::context::shared_context;
|
||||||
use crate::timestamp::Timestamp;
|
use crate::timestamp::Timestamp;
|
||||||
use crate::{Builder, Uuid};
|
use crate::{Builder, Uuid};
|
||||||
|
|
||||||
pub use crate::timestamp::context::Context;
|
pub use crate::timestamp::context::Context;
|
||||||
|
|
||||||
impl Uuid {
|
impl Uuid {
|
||||||
/// Create a new UUID (version 1) using a time value + sequence +
|
/// Create a new UUID (version 1) using the current system time and a node id.
|
||||||
/// *NodeId*.
|
///
|
||||||
|
/// This method is only available if both the `std` and `rng` features are enabled.
|
||||||
|
#[cfg(all(feature = "std", feature = "rng"))]
|
||||||
|
pub fn now_v1(node_id: &[u8; 6]) -> Self {
|
||||||
|
let ts = Timestamp::now(shared_context());
|
||||||
|
|
||||||
|
Self::new_v1(ts, node_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new UUID (version 1) using the given timestamp and node id.
|
||||||
///
|
///
|
||||||
/// When generating [`Timestamp`]s using a [`ClockSequence`], this function
|
/// When generating [`Timestamp`]s using a [`ClockSequence`], this function
|
||||||
/// is only guaranteed to produce unique values if the following conditions
|
/// is only guaranteed to produce unique values if the following conditions
|
||||||
/// hold:
|
/// hold:
|
||||||
///
|
///
|
||||||
/// 1. The *NodeId* is unique for this process,
|
/// 1. The *node id* is unique for this process,
|
||||||
/// 2. The *Context* is shared across all threads which are generating v1
|
/// 2. The *context* is shared across all threads which are generating version 1
|
||||||
/// UUIDs,
|
/// UUIDs,
|
||||||
/// 3. The [`ClockSequence`] implementation reliably returns unique
|
/// 3. The [`ClockSequence`] implementation reliably returns unique
|
||||||
/// clock sequences (this crate provides [`Context`] for this
|
/// clock sequences (this crate provides [`Context`] for this
|
||||||
/// purpose. However you can create your own [`ClockSequence`]
|
/// purpose. However you can create your own [`ClockSequence`]
|
||||||
/// implementation, if [`Context`] does not meet your needs).
|
/// 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
|
/// Note that usage of this method requires the `v1` feature of this crate
|
||||||
/// to be enabled.
|
/// to be enabled.
|
||||||
///
|
///
|
||||||
@ -35,7 +43,7 @@ impl Uuid {
|
|||||||
/// [`ClockSequence`]. RFC4122 requires the clock sequence
|
/// [`ClockSequence`]. RFC4122 requires the clock sequence
|
||||||
/// is seeded with a random value:
|
/// is seeded with a random value:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```
|
||||||
/// # use uuid::{Timestamp, Context};
|
/// # use uuid::{Timestamp, Context};
|
||||||
/// # use uuid::Uuid;
|
/// # use uuid::Uuid;
|
||||||
/// # fn random_seed() -> u16 { 42 }
|
/// # fn random_seed() -> u16 { 42 }
|
||||||
@ -67,7 +75,7 @@ impl Uuid {
|
|||||||
///
|
///
|
||||||
/// The timestamp can also just use the current SystemTime
|
/// The timestamp can also just use the current SystemTime
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```
|
||||||
/// # use uuid::{Timestamp, Context};
|
/// # use uuid::{Timestamp, Context};
|
||||||
/// # use uuid::Uuid;
|
/// # use uuid::Uuid;
|
||||||
/// let context = Context::new(42);
|
/// let context = Context::new(42);
|
||||||
@ -80,10 +88,48 @@ impl Uuid {
|
|||||||
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
||||||
/// [`Context`]: v1/struct.Context.html
|
/// [`Context`]: v1/struct.Context.html
|
||||||
pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
||||||
Builder::from_rfc4122_timestamp(ts, node_id).into_uuid()
|
let (ticks, counter) = ts.to_rfc4122();
|
||||||
|
|
||||||
|
Builder::from_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
57
src/v6.rs
57
src/v6.rs
@ -3,10 +3,21 @@
|
|||||||
//! Note that you need to enable the `v6` Cargo feature
|
//! Note that you need to enable the `v6` Cargo feature
|
||||||
//! in order to use this module.
|
//! in order to use this module.
|
||||||
|
|
||||||
|
use crate::timestamp::context::shared_context;
|
||||||
use crate::timestamp::Timestamp;
|
use crate::timestamp::Timestamp;
|
||||||
use crate::{Builder, Uuid};
|
use crate::{Builder, Uuid};
|
||||||
|
|
||||||
impl Uuid {
|
impl Uuid {
|
||||||
|
/// Create a new UUID (version 6) using the current time value and a node id.
|
||||||
|
///
|
||||||
|
/// This method is only available if the `std` feature is enabled.
|
||||||
|
#[cfg(all(feature = "std", feature = "rng"))]
|
||||||
|
pub fn now_v6(node_id: &[u8; 6]) -> Self {
|
||||||
|
let ts = Timestamp::now(shared_context());
|
||||||
|
|
||||||
|
Self::new_v6(ts, node_id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new UUID (version 6) using a time value + sequence +
|
/// Create a new UUID (version 6) using a time value + sequence +
|
||||||
/// *NodeId*.
|
/// *NodeId*.
|
||||||
/// This is similar to UUIDv1, except that it is lexographically sortable by timestamp.
|
/// This is similar to UUIDv1, except that it is lexographically sortable by timestamp.
|
||||||
@ -75,14 +86,56 @@ impl Uuid {
|
|||||||
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
/// [`ClockSequence`]: v1/trait.ClockSequence.html
|
||||||
/// [`Context`]: v1/struct.Context.html
|
/// [`Context`]: v1/struct.Context.html
|
||||||
pub fn new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
pub fn new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self {
|
||||||
Builder::from_sorted_rfc4122_timestamp(ts, node_id).into_uuid()
|
let (ticks, counter) = ts.to_rfc4122();
|
||||||
|
|
||||||
|
Builder::from_sorted_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Variant, Version, Context};
|
use crate::{Context, Variant, Version};
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
73
src/v7.rs
73
src/v7.rs
@ -3,7 +3,7 @@
|
|||||||
//! Note that you need to enable the `v7` Cargo feature
|
//! Note that you need to enable the `v7` Cargo feature
|
||||||
//! in order to use this module.
|
//! in order to use this module.
|
||||||
|
|
||||||
use crate::{Builder, NoContext, rng, timestamp::Timestamp, Uuid};
|
use crate::{rng, timestamp::Timestamp, Builder, NoContext, Uuid};
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
|
|
||||||
impl Uuid {
|
impl Uuid {
|
||||||
@ -23,9 +23,6 @@ impl Uuid {
|
|||||||
/// Note that usage of this method requires the `v7` feature of this crate
|
/// Note that usage of this method requires the `v7` feature of this crate
|
||||||
/// to be enabled.
|
/// to be enabled.
|
||||||
///
|
///
|
||||||
/// This method will use millisecond precision for the timestamp and fill the
|
|
||||||
/// rest with random data.
|
|
||||||
///
|
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit
|
/// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit
|
||||||
@ -41,26 +38,53 @@ impl Uuid {
|
|||||||
/// uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7")
|
/// uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7")
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// The timestamp can also be created automatically from the current SystemTime
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// # use uuid::{Uuid, Timestamp, NoContext};
|
|
||||||
/// let ts = Timestamp::now(NoContext);
|
|
||||||
///
|
|
||||||
/// let uuid = Uuid::new_v7(ts);
|
|
||||||
/// ```
|
|
||||||
pub fn new_v7(ts: Timestamp) -> Self {
|
pub fn new_v7(ts: Timestamp) -> Self {
|
||||||
let buf: &[u8] = &rng::bytes()[0..11];
|
let (secs, nanos) = ts.to_unix();
|
||||||
|
let millis = secs.saturating_add(nanos as u64 / 1_000_000);
|
||||||
|
|
||||||
Builder::from_timestamp_millis(ts, buf.try_into().unwrap()).into_uuid()
|
Builder::from_unix_timestamp_millis(millis, &rng::bytes()[..10].try_into().unwrap())
|
||||||
|
.into_uuid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Variant, Version, NoContext};
|
use crate::{NoContext, Variant, Version};
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen_test::*;
|
use wasm_bindgen_test::*;
|
||||||
@ -81,6 +105,21 @@ mod tests {
|
|||||||
// Ensure parsing the same UUID produces the same timestamp
|
// Ensure parsing the same UUID produces the same timestamp
|
||||||
let parsed = Uuid::parse_str(uustr.as_str()).unwrap();
|
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_946_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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ impl Uuid {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{Version, Variant};
|
use crate::{Variant, Version};
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@ -36,8 +36,7 @@ mod tests {
|
|||||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||||
fn test_new() {
|
fn test_new() {
|
||||||
let buf: [u8; 16] = [
|
let buf: [u8; 16] = [
|
||||||
0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3,
|
0xf, 0xe, 0xd, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0,
|
||||||
0x2, 0x1, 0x0,
|
|
||||||
];
|
];
|
||||||
let uuid = Uuid::new_v8(buf);
|
let uuid = Uuid::new_v8(buf);
|
||||||
assert_eq!(uuid.get_version(), Some(Version::Custom));
|
assert_eq!(uuid.get_version(), Some(Version::Custom));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user