blob: dc5a057927a79c4ad7ed929f095f79531a7aa0ea [file] [log] [blame]
//! The implementation for Version 1 [`Uuid`]s.
//!
//! Note that you need feature `v1` in order to use these features.
//!
//! [`Uuid`]: ../struct.Uuid.html
use core::sync::atomic;
use prelude::*;
/// A thread-safe, stateful context for the v1 generator to help ensure
/// process-wide uniqueness.
#[derive(Debug)]
pub struct Context {
count: atomic::AtomicUsize,
}
/// 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, nano_seconds: u32) -> u16;
}
impl Uuid {
/// Create a new [`Uuid`] (version 1) using a time value + sequence +
/// *NodeId*.
///
/// This expects two values representing a monotonically increasing value
/// as well as a unique 6 byte NodeId, and an implementation of
/// [`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
/// [`Uuid`]s,
/// 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. If the NodeID is not a valid
/// length this will return a [`ParseError`]`::InvalidLength`.
///
/// 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).
///
/// Note that usage of this method requires the `v1` feature of this crate
/// to be enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// use uuid::v1::Context;
/// use uuid::Uuid;
///
/// let context = Context::new(42);
/// if let Ok(uuid) =
/// Uuid::new_v1(&context, 1497624119, 1234, &[1, 2, 3, 4, 5, 6])
/// {
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "f3b4958c-52a1-11e7-802a-010203040506"
/// )
/// } else {
/// panic!()
/// }
/// ```
///
/// [`ParseError`]: ../enum.ParseError.html
/// [`Uuid`]: ../struct.Uuid.html
/// [`ClockSequence`]: struct.ClockSequence.html
/// [`Context`]: struct.Context.html
pub fn new_v1<T>(
context: &T,
seconds: u64,
nano_seconds: u32,
node_id: &[u8],
) -> Result<Self, ::BytesError>
where
T: ClockSequence,
{
const NODE_ID_LEN: usize = 6;
let len = node_id.len();
if len != NODE_ID_LEN {
return Err(::BytesError::new(NODE_ID_LEN, len));
}
let time_low;
let time_mid;
let time_high_and_version;
{
/// 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;
let timestamp =
seconds * 10_000_000 + u64::from(nano_seconds / 100);
let uuid_time = timestamp + UUID_TICKS_BETWEEN_EPOCHS;
time_low = (uuid_time & 0xFFFF_FFFF) as u32;
time_mid = ((uuid_time >> 32) & 0xFFFF) as u16;
time_high_and_version =
(((uuid_time >> 48) & 0x0FFF) as u16) | (1 << 12);
}
let mut d4 = [0; 8];
{
let count = context.generate_sequence(seconds, nano_seconds);
d4[0] = (((count & 0x3F00) >> 8) as u8) | 0x80;
d4[1] = (count & 0xFF) as u8;
}
d4[2..].copy_from_slice(node_id);
Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
}
}
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.
///
/// [`Uuid`]: ../struct.Uuid.html
pub 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 {
#[test]
fn test_new_v1() {
use super::Context;
use prelude::*;
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(&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();
assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
assert_eq!(ts.1, 0);
};
{
let uuid2 =
Uuid::new_v1(&context, time, time_fraction, &node).unwrap();
assert_eq!(
uuid2.to_hyphenated().to_string(),
"20616934-4ba2-11e7-8001-010203040506"
);
assert_eq!(uuid2.to_timestamp().unwrap().1, 1)
};
}
}