1 //! The implementation for Version 6 UUIDs.
2 //!
3 //! Note that you need to enable the `v6` Cargo feature
4 //! in order to use this module.
5 
6 use crate::{Builder, Timestamp, Uuid};
7 
8 impl Uuid {
9     /// Create a new version 6 UUID using the current system time and node ID.
10     ///
11     /// This method is only available if the `std` feature is enabled.
12     ///
13     /// This method is a convenient alternative to [`Uuid::new_v6`] that uses the current system time
14     /// as the source timestamp.
15     ///
16     /// Note that usage of this method requires the `v6`, `std`, and `rng` features of this crate
17     /// to be enabled.
18     #[cfg(all(feature = "std", feature = "rng"))]
now_v6(node_id: &[u8; 6]) -> Self19     pub fn now_v6(node_id: &[u8; 6]) -> Self {
20         let ts = Timestamp::now(crate::timestamp::context::shared_context());
21 
22         Self::new_v6(ts, node_id)
23     }
24 
25     /// Create a new version 6 UUID using the given timestamp and a node ID.
26     ///
27     /// This is similar to version 1 UUIDs, except that it is lexicographically sortable by timestamp.
28     ///
29     /// Also see [`Uuid::now_v6`] for a convenient way to generate version 6
30     /// UUIDs using the current system time.
31     ///
32     /// When generating [`Timestamp`]s using a [`ClockSequence`], this function
33     /// is only guaranteed to produce unique values if the following conditions
34     /// hold:
35     ///
36     /// 1. The *node ID* is unique for this process,
37     /// 2. The *context* is shared across all threads which are generating version 6
38     ///    UUIDs,
39     /// 3. The [`ClockSequence`] implementation reliably returns unique
40     ///    clock sequences (this crate provides [`Context`] for this
41     ///    purpose. However you can create your own [`ClockSequence`]
42     ///    implementation, if [`Context`] does not meet your needs).
43     ///
44     /// The NodeID must be exactly 6 bytes long.
45     ///
46     /// Note that usage of this method requires the `v6` feature of this crate
47     /// to be enabled.
48     ///
49     /// # Examples
50     ///
51     /// A UUID can be created from a unix [`Timestamp`] with a
52     /// [`ClockSequence`]. RFC4122 requires the clock sequence
53     /// is seeded with a random value:
54     ///
55     /// ```rust
56     /// # use uuid::{Uuid, Timestamp, Context};
57     /// # fn random_seed() -> u16 { 42 }
58     /// let context = Context::new(random_seed());
59     /// let ts = Timestamp::from_unix(context, 1497624119, 1234);
60     ///
61     /// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]);
62     ///
63     /// assert_eq!(
64     ///     uuid.hyphenated().to_string(),
65     ///     "1e752a1f-3b49-658c-802a-010203040506"
66     /// );
67     /// ```
68     ///
69     /// The timestamp can also be created manually as per RFC4122:
70     ///
71     /// ```
72     /// # use uuid::{Uuid, Timestamp, Context, ClockSequence};
73     /// # fn random_seed() -> u16 { 42 }
74     /// let context = Context::new(random_seed());
75     /// let ts = Timestamp::from_rfc4122(14976241191231231313, context.generate_sequence(0, 0) );
76     ///
77     /// let uuid = Uuid::new_v6(ts, &[1, 2, 3, 4, 5, 6]);
78     ///
79     /// assert_eq!(
80     ///     uuid.hyphenated().to_string(),
81     ///     "fd64c041-1e91-6551-802a-010203040506"
82     /// );
83     /// ```
84     ///
85     /// # References
86     ///
87     /// * [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)
88     ///
89     /// [`Timestamp`]: timestamp/struct.Timestamp.html
90     /// [`ClockSequence`]: timestamp/trait.ClockSequence.html
91     /// [`Context`]: timestamp/context/struct.Context.html
new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self92     pub fn new_v6(ts: Timestamp, node_id: &[u8; 6]) -> Self {
93         let (ticks, counter) = ts.to_rfc4122();
94 
95         Builder::from_sorted_rfc4122_timestamp(ticks, counter, node_id).into_uuid()
96     }
97 }
98 
99 #[cfg(test)]
100 mod tests {
101     use super::*;
102     use crate::{Context, Variant, Version};
103     use std::string::ToString;
104 
105     #[cfg(all(
106         target_arch = "wasm32",
107         target_vendor = "unknown",
108         target_os = "unknown"
109     ))]
110     use wasm_bindgen_test::*;
111 
112     #[test]
113     #[cfg_attr(
114         all(
115             target_arch = "wasm32",
116             target_vendor = "unknown",
117             target_os = "unknown"
118         ),
119         wasm_bindgen_test
120     )]
test_new()121     fn test_new() {
122         let time: u64 = 1_496_854_535;
123         let time_fraction: u32 = 812_946_000;
124         let node = [1, 2, 3, 4, 5, 6];
125         let context = Context::new(0);
126 
127         let uuid = Uuid::new_v6(Timestamp::from_unix(context, time, time_fraction), &node);
128 
129         assert_eq!(uuid.get_version(), Some(Version::SortMac));
130         assert_eq!(uuid.get_variant(), Variant::RFC4122);
131         assert_eq!(
132             uuid.hyphenated().to_string(),
133             "1e74ba22-0616-6934-8000-010203040506"
134         );
135 
136         let ts = uuid.get_timestamp().unwrap().to_rfc4122();
137 
138         assert_eq!(ts.0 - 0x01B2_1DD2_1381_4000, 14_968_545_358_129_460);
139 
140         // Ensure parsing the same UUID produces the same timestamp
141         let parsed = Uuid::parse_str("1e74ba22-0616-6934-8000-010203040506").unwrap();
142 
143         assert_eq!(
144             uuid.get_timestamp().unwrap(),
145             parsed.get_timestamp().unwrap()
146         );
147     }
148 
149     #[test]
150     #[cfg_attr(
151         all(
152             target_arch = "wasm32",
153             target_vendor = "unknown",
154             target_os = "unknown"
155         ),
156         wasm_bindgen_test
157     )]
158     #[cfg(all(feature = "std", feature = "rng"))]
test_now()159     fn test_now() {
160         let node = [1, 2, 3, 4, 5, 6];
161 
162         let uuid = Uuid::now_v6(&node);
163 
164         assert_eq!(uuid.get_version(), Some(Version::SortMac));
165         assert_eq!(uuid.get_variant(), Variant::RFC4122);
166     }
167 
168     #[test]
169     #[cfg_attr(
170         all(
171             target_arch = "wasm32",
172             target_vendor = "unknown",
173             target_os = "unknown"
174         ),
175         wasm_bindgen_test
176     )]
test_new_context()177     fn test_new_context() {
178         let time: u64 = 1_496_854_535;
179         let time_fraction: u32 = 812_946_000;
180         let node = [1, 2, 3, 4, 5, 6];
181 
182         // This context will wrap
183         let context = Context::new(u16::MAX >> 2);
184 
185         let uuid1 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
186 
187         let time: u64 = 1_496_854_536;
188 
189         let uuid2 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
190 
191         assert_eq!(uuid1.get_timestamp().unwrap().to_rfc4122().1, 16383);
192         assert_eq!(uuid2.get_timestamp().unwrap().to_rfc4122().1, 0);
193 
194         let time = 1_496_854_535;
195 
196         let uuid3 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
197         let uuid4 = Uuid::new_v6(Timestamp::from_unix(&context, time, time_fraction), &node);
198 
199         assert_eq!(uuid3.get_timestamp().unwrap().counter, 1);
200         assert_eq!(uuid4.get_timestamp().unwrap().counter, 2);
201     }
202 }
203