//! The implementation for Version 1 UUIDs. | |
//! | |
//! Note that you need feature `v1` in order to use these features. | |
use crate::prelude::*; | |
use core::sync::atomic; | |
/// The number of 100 ns ticks between the UUID epoch | |
/// `1582-10-15 00:00:00` and the Unix epoch `1970-01-01 00:00:00`. | |
const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; | |
/// A thread-safe, stateful context for the v1 generator to help ensure | |
/// process-wide uniqueness. | |
#[derive(Debug)] | |
pub struct Context { | |
count: atomic::AtomicUsize, | |
} | |
/// Stores the number of nanoseconds from an epoch and a counter for ensuring | |
/// V1 ids generated on the same host are unique. | |
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | |
pub struct Timestamp { | |
ticks: u64, | |
counter: u16, | |
} | |
impl Timestamp { | |
/// Construct a `Timestamp` from its raw component values: an RFC4122 | |
/// timestamp and counter. | |
/// | |
/// RFC4122, which defines the V1 UUID, specifies a 60-byte timestamp format | |
/// as the number of 100-nanosecond intervals elapsed since 00:00:00.00, | |
/// 15 Oct 1582, "the date of the Gregorian reform of the Christian | |
/// calendar." | |
/// | |
/// The counter value is used to differentiate between ids generated by | |
/// the same host computer in rapid succession (i.e. with the same observed | |
/// time). See the [`ClockSequence`] trait for a generic interface to any | |
/// counter generators that might be used. | |
/// | |
/// Internally, the timestamp is stored as a `u64`. For this reason, dates | |
/// prior to October 1582 are not supported. | |
/// | |
/// [`ClockSequence`]: trait.ClockSequence.html | |
pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { | |
Timestamp { ticks, counter } | |
} | |
/// Construct a `Timestamp` from a unix timestamp and sequence-generating | |
/// `context`. | |
/// | |
/// A unix timestamp represents the elapsed time since Jan 1 1970. Libc's | |
/// `clock_gettime` and other popular implementations traditionally | |
/// represent this duration as a `timespec`: a struct with `u64` and | |
/// `u32` fields representing the seconds, and "subsecond" or fractional | |
/// nanoseconds elapsed since the timestamp's second began, | |
/// respectively. | |
/// | |
/// This constructs a `Timestamp` from the seconds and fractional | |
/// nanoseconds of a unix timestamp, converting the duration since 1970 | |
/// into the number of 100-nanosecond intervals since 00:00:00.00, 15 | |
/// Oct 1582 specified by RFC4122 and used internally by `Timestamp`. | |
/// | |
/// The function is not guaranteed to produce monotonically increasing | |
/// values however. There is a slight possibility that two successive | |
/// equal time values could be supplied and the sequence counter wraps back | |
/// over to 0. | |
/// | |
/// If uniqueness and monotonicity is required, the user is responsible for | |
/// ensuring that the time value always increases between calls (including | |
/// between restarts of the process and device). | |
pub fn from_unix( | |
context: impl ClockSequence, | |
seconds: u64, | |
subsec_nanos: u32, | |
) -> Self { | |
let counter = context.generate_sequence(seconds, subsec_nanos); | |
let ticks = UUID_TICKS_BETWEEN_EPOCHS | |
+ seconds * 10_000_000 | |
+ u64::from(subsec_nanos) / 100; | |
Timestamp { ticks, counter } | |
} | |
/// Returns the raw RFC4122 timestamp and counter values stored by the | |
/// `Timestamp`. | |
/// | |
/// The timestamp (the first, `u64` element in the tuple) represents the | |
/// number of 100-nanosecond intervals since 00:00:00.00, 15 Oct 1582. | |
/// The counter is used to differentiate between ids generated on the | |
/// same host computer with the same observed time. | |
pub const fn to_rfc4122(&self) -> (u64, u16) { | |
(self.ticks, self.counter) | |
} | |
/// Returns the timestamp converted to the seconds and fractional | |
/// nanoseconds since Jan 1 1970. | |
/// | |
/// Internally, the time is stored in 100-nanosecond intervals, | |
/// thus the maximum precision represented by the fractional nanoseconds | |
/// value is less than its unit size (100 ns vs. 1 ns). | |
pub const fn to_unix(&self) -> (u64, u32) { | |
( | |
(self.ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, | |
((self.ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 | |
* 100, | |
) | |
} | |
/// Returns the timestamp converted into nanoseconds elapsed since Jan 1 | |
/// 1970. Internally, the time is stored in 100-nanosecond intervals, | |
/// thus the maximum precision represented is less than the units it is | |
/// measured in (100 ns vs. 1 ns). The value returned represents the | |
/// same duration as [`Timestamp::to_unix`]; this provides it in nanosecond | |
/// units for convenience. | |
pub const fn to_unix_nanos(&self) -> u64 { | |
(self.ticks - UUID_TICKS_BETWEEN_EPOCHS) * 100 | |
} | |
} | |
/// A trait that abstracts over generation of UUID v1 "Clock Sequence" values. | |
pub trait ClockSequence { | |
/// Return a 16-bit number that will be used as the "clock sequence" in | |
/// the UUID. The number must be different if the time has changed since | |
/// the last time a clock sequence was requested. | |
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16; | |
} | |
impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { | |
fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> u16 { | |
(**self).generate_sequence(seconds, subsec_nanos) | |
} | |
} | |
impl Uuid { | |
/// Create a new UUID (version 1) using a time value + sequence + | |
/// *NodeId*. | |
/// | |
/// When generating [`Timestamp`]s using a [`ClockSequence`], this function | |
/// is only guaranteed to produce unique values if the following conditions | |
/// hold: | |
/// | |
/// 1. The *NodeId* is unique for this process, | |
/// 2. The *Context* is shared across all threads which are generating v1 | |
/// UUIDs, | |
/// 3. The [`ClockSequence`] implementation reliably returns unique | |
/// clock sequences (this crate provides [`Context`] for this | |
/// purpose. However you can create your own [`ClockSequence`] | |
/// implementation, if [`Context`] does not meet your needs). | |
/// | |
/// The NodeID must be exactly 6 bytes long. | |
/// | |
/// Note that usage of this method requires the `v1` feature of this crate | |
/// to be enabled. | |
/// | |
/// # Examples | |
/// | |
/// A UUID can be created from a unix [`Timestamp`] with a | |
/// [`ClockSequence`]: | |
/// | |
/// ```rust | |
/// use uuid::v1::{Timestamp, Context}; | |
/// use uuid::Uuid; | |
/// | |
/// let context = Context::new(42); | |
/// let ts = Timestamp::from_unix(&context, 1497624119, 1234); | |
/// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]).expect("failed to generate UUID"); | |
/// | |
/// assert_eq!( | |
/// uuid.to_hyphenated().to_string(), | |
/// "f3b4958c-52a1-11e7-802a-010203040506" | |
/// ); | |
/// ``` | |
/// | |
/// The timestamp can also be created manually as per RFC4122: | |
/// | |
/// ``` | |
/// use uuid::v1::{Timestamp, Context}; | |
/// use uuid::Uuid; | |
/// | |
/// let context = Context::new(42); | |
/// let ts = Timestamp::from_rfc4122(1497624119, 0); | |
/// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]).expect("failed to generate UUID"); | |
/// | |
/// assert_eq!( | |
/// uuid.to_hyphenated().to_string(), | |
/// "5943ee37-0000-1000-8000-010203040506" | |
/// ); | |
/// ``` | |
/// | |
/// [`Timestamp`]: v1/struct.Timestamp.html | |
/// [`ClockSequence`]: v1/struct.ClockSequence.html | |
/// [`Context`]: v1/struct.Context.html | |
pub fn new_v1(ts: Timestamp, node_id: &[u8]) -> Result<Self, crate::Error> { | |
const NODE_ID_LEN: usize = 6; | |
let len = node_id.len(); | |
if len != NODE_ID_LEN { | |
Err(crate::builder::Error::new(NODE_ID_LEN, len))?; | |
} | |
let time_low = (ts.ticks & 0xFFFF_FFFF) as u32; | |
let time_mid = ((ts.ticks >> 32) & 0xFFFF) as u16; | |
let time_high_and_version = | |
(((ts.ticks >> 48) & 0x0FFF) as u16) | (1 << 12); | |
let mut d4 = [0; 8]; | |
{ | |
d4[0] = (((ts.counter & 0x3F00) >> 8) as u8) | 0x80; | |
d4[1] = (ts.counter & 0xFF) as u8; | |
} | |
d4[2..].copy_from_slice(node_id); | |
Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4) | |
} | |
/// Returns an optional [`Timestamp`] storing the timestamp and | |
/// counter portion parsed from a V1 UUID. | |
/// | |
/// Returns `None` if the supplied UUID is not V1. | |
/// | |
/// The V1 timestamp format defined in RFC4122 specifies a 60-bit | |
/// integer representing the number of 100-nanosecond intervals | |
/// since 00:00:00.00, 15 Oct 1582. | |
/// | |
/// [`Timestamp`] offers several options for converting the raw RFC4122 | |
/// value into more commonly-used formats, such as a unix timestamp. | |
/// | |
/// [`Timestamp`]: v1/struct.Timestamp.html | |
pub fn to_timestamp(&self) -> Option<Timestamp> { | |
if self | |
.get_version() | |
.map(|v| v != Version::Mac) | |
.unwrap_or(true) | |
{ | |
return None; | |
} | |
let ticks: u64 = u64::from(self.as_bytes()[6] & 0x0F) << 56 | |
| u64::from(self.as_bytes()[7]) << 48 | |
| u64::from(self.as_bytes()[4]) << 40 | |
| u64::from(self.as_bytes()[5]) << 32 | |
| u64::from(self.as_bytes()[0]) << 24 | |
| u64::from(self.as_bytes()[1]) << 16 | |
| u64::from(self.as_bytes()[2]) << 8 | |
| u64::from(self.as_bytes()[3]); | |
let counter: u16 = u16::from(self.as_bytes()[8] & 0x3F) << 8 | |
| u16::from(self.as_bytes()[9]); | |
Some(Timestamp::from_rfc4122(ticks, counter)) | |
} | |
} | |
impl Context { | |
/// Creates a thread-safe, internally mutable context to help ensure | |
/// uniqueness. | |
/// | |
/// This is a context which can be shared across threads. It maintains an | |
/// internal counter that is incremented at every request, the value ends | |
/// up in the clock_seq portion of the UUID (the fourth group). This | |
/// will improve the probability that the UUID is unique across the | |
/// process. | |
pub const fn new(count: u16) -> Self { | |
Self { | |
count: atomic::AtomicUsize::new(count as usize), | |
} | |
} | |
} | |
impl ClockSequence for Context { | |
fn generate_sequence(&self, _: u64, _: u32) -> u16 { | |
(self.count.fetch_add(1, atomic::Ordering::SeqCst) & 0xffff) as u16 | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
use crate::std::string::ToString; | |
#[test] | |
fn test_new_v1() { | |
let time: u64 = 1_496_854_535; | |
let time_fraction: u32 = 812_946_000; | |
let node = [1, 2, 3, 4, 5, 6]; | |
let context = Context::new(0); | |
{ | |
let uuid = Uuid::new_v1( | |
Timestamp::from_unix(&context, time, time_fraction), | |
&node, | |
) | |
.unwrap(); | |
assert_eq!(uuid.get_version(), Some(Version::Mac)); | |
assert_eq!(uuid.get_variant(), Some(Variant::RFC4122)); | |
assert_eq!( | |
uuid.to_hyphenated().to_string(), | |
"20616934-4ba2-11e7-8000-010203040506" | |
); | |
let ts = uuid.to_timestamp().unwrap().to_rfc4122(); | |
assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460); | |
assert_eq!(ts.1, 0); | |
}; | |
{ | |
let uuid2 = Uuid::new_v1( | |
Timestamp::from_unix(&context, time, time_fraction), | |
&node, | |
) | |
.unwrap(); | |
assert_eq!( | |
uuid2.to_hyphenated().to_string(), | |
"20616934-4ba2-11e7-8001-010203040506" | |
); | |
assert_eq!(uuid2.to_timestamp().unwrap().to_rfc4122().1, 1) | |
}; | |
} | |
} |