• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Generating UUIDs from timestamps.
2 //!
3 //! Timestamps are used in a few UUID versions as a source of decentralized
4 //! uniqueness (as in versions 1 and 6), and as a way to enable sorting (as
5 //! in versions 6 and 7). Timestamps aren't encoded the same way by all UUID
6 //! versions so this module provides a single [`Timestamp`] type that can
7 //! convert between them.
8 //!
9 //! # Timestamp representations in UUIDs
10 //!
11 //! Versions 1 and 6 UUIDs use a bespoke timestamp that consists of the
12 //! number of 100ns ticks since `1582-10-15 00:00:00`, along with
13 //! a counter value to avoid duplicates.
14 //!
15 //! Version 7 UUIDs use a more standard timestamp that consists of the
16 //! number of millisecond ticks since the Unix epoch (`1970-01-01 00:00:00`).
17 //!
18 //! # References
19 //!
20 //! * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
21 //! * [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)
22 
23 use crate::Uuid;
24 
25 /// The number of 100 nanosecond ticks between the RFC4122 epoch
26 /// (`1582-10-15 00:00:00`) and the Unix epoch (`1970-01-01 00:00:00`).
27 pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000;
28 
29 /// A timestamp that can be encoded into a UUID.
30 ///
31 /// This type abstracts the specific encoding, so versions 1, 6, and 7
32 /// UUIDs can both be supported through the same type, even
33 /// though they have a different representation of a timestamp.
34 ///
35 /// # References
36 ///
37 /// * [Timestamp in RFC4122](https://www.rfc-editor.org/rfc/rfc4122#section-4.1.4)
38 /// * [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)
39 /// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
40 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41 pub struct Timestamp {
42     pub(crate) seconds: u64,
43     pub(crate) nanos: u32,
44     #[cfg(any(feature = "v1", feature = "v6"))]
45     pub(crate) counter: u16,
46 }
47 
48 impl Timestamp {
49     /// Get a timestamp representing the current system time.
50     ///
51     /// This method defers to the standard library's `SystemTime` type.
52     ///
53     /// # Panics
54     ///
55     /// This method will panic if calculating the elapsed time since the Unix epoch fails.
56     #[cfg(feature = "std")]
now(context: impl ClockSequence<Output = u16>) -> Self57     pub fn now(context: impl ClockSequence<Output = u16>) -> Self {
58         #[cfg(not(any(feature = "v1", feature = "v6")))]
59         {
60             let _ = context;
61         }
62 
63         let (seconds, nanos) = now();
64 
65         Timestamp {
66             seconds,
67             nanos,
68             #[cfg(any(feature = "v1", feature = "v6"))]
69             counter: context.generate_sequence(seconds, nanos),
70         }
71     }
72 
73     /// Construct a `Timestamp` from an RFC4122 timestamp and counter, as used
74     /// in versions 1 and 6 UUIDs.
from_rfc4122(ticks: u64, counter: u16) -> Self75     pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self {
76         #[cfg(not(any(feature = "v1", feature = "v6")))]
77         {
78             let _ = counter;
79         }
80 
81         let (seconds, nanos) = Self::rfc4122_to_unix(ticks);
82 
83         Timestamp {
84             seconds,
85             nanos,
86             #[cfg(any(feature = "v1", feature = "v6"))]
87             counter,
88         }
89     }
90 
91     /// Construct a `Timestamp` from a Unix timestamp, as used in version 7 UUIDs.
from_unix(context: impl ClockSequence<Output = u16>, seconds: u64, nanos: u32) -> Self92     pub fn from_unix(context: impl ClockSequence<Output = u16>, seconds: u64, nanos: u32) -> Self {
93         #[cfg(not(any(feature = "v1", feature = "v6")))]
94         {
95             let _ = context;
96 
97             Timestamp { seconds, nanos }
98         }
99         #[cfg(any(feature = "v1", feature = "v6"))]
100         {
101             let counter = context.generate_sequence(seconds, nanos);
102 
103             Timestamp {
104                 seconds,
105                 nanos,
106                 counter,
107             }
108         }
109     }
110 
111     /// Get the value of the timestamp as an RFC4122 timestamp and counter,
112     /// as used in versions 1 and 6 UUIDs.
113     #[cfg(any(feature = "v1", feature = "v6"))]
to_rfc4122(&self) -> (u64, u16)114     pub const fn to_rfc4122(&self) -> (u64, u16) {
115         (
116             Self::unix_to_rfc4122_ticks(self.seconds, self.nanos),
117             self.counter,
118         )
119     }
120 
121     /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs.
to_unix(&self) -> (u64, u32)122     pub const fn to_unix(&self) -> (u64, u32) {
123         (self.seconds, self.nanos)
124     }
125 
126     #[cfg(any(feature = "v1", feature = "v6"))]
unix_to_rfc4122_ticks(seconds: u64, nanos: u32) -> u64127     const fn unix_to_rfc4122_ticks(seconds: u64, nanos: u32) -> u64 {
128         let ticks = UUID_TICKS_BETWEEN_EPOCHS + seconds * 10_000_000 + nanos as u64 / 100;
129 
130         ticks
131     }
132 
rfc4122_to_unix(ticks: u64) -> (u64, u32)133     const fn rfc4122_to_unix(ticks: u64) -> (u64, u32) {
134         (
135             (ticks - UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000,
136             ((ticks - UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100,
137         )
138     }
139 
140     #[deprecated(note = "use `to_unix` instead")]
141     /// Get the number of fractional nanoseconds in the Unix timestamp.
142     ///
143     /// This method is deprecated and probably doesn't do what you're expecting it to.
144     /// It doesn't return the timestamp as nanoseconds since the Unix epoch, it returns
145     /// the fractional seconds of the timestamp.
to_unix_nanos(&self) -> u32146     pub const fn to_unix_nanos(&self) -> u32 {
147         // NOTE: This method never did what it said on the tin: instead of
148         // converting the timestamp into nanos it simply returned the nanoseconds
149         // part of the timestamp.
150         //
151         // We can't fix the behavior because the return type is too small to fit
152         // a useful value for nanoseconds since the epoch.
153         self.nanos
154     }
155 }
156 
encode_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Uuid157 pub(crate) const fn encode_rfc4122_timestamp(ticks: u64, counter: u16, node_id: &[u8; 6]) -> Uuid {
158     let time_low = (ticks & 0xFFFF_FFFF) as u32;
159     let time_mid = ((ticks >> 32) & 0xFFFF) as u16;
160     let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12);
161 
162     let mut d4 = [0; 8];
163 
164     d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80;
165     d4[1] = (counter & 0xFF) as u8;
166     d4[2] = node_id[0];
167     d4[3] = node_id[1];
168     d4[4] = node_id[2];
169     d4[5] = node_id[3];
170     d4[6] = node_id[4];
171     d4[7] = node_id[5];
172 
173     Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4)
174 }
175 
decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16)176 pub(crate) const fn decode_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
177     let bytes = uuid.as_bytes();
178 
179     let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56
180         | (bytes[7] as u64) << 48
181         | (bytes[4] as u64) << 40
182         | (bytes[5] as u64) << 32
183         | (bytes[0] as u64) << 24
184         | (bytes[1] as u64) << 16
185         | (bytes[2] as u64) << 8
186         | (bytes[3] as u64);
187 
188     let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
189 
190     (ticks, counter)
191 }
192 
193 #[cfg(uuid_unstable)]
encode_sorted_rfc4122_timestamp( ticks: u64, counter: u16, node_id: &[u8; 6], ) -> Uuid194 pub(crate) const fn encode_sorted_rfc4122_timestamp(
195     ticks: u64,
196     counter: u16,
197     node_id: &[u8; 6],
198 ) -> Uuid {
199     let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32;
200     let time_mid = ((ticks >> 12) & 0xFFFF) as u16;
201     let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12);
202 
203     let mut d4 = [0; 8];
204 
205     d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80;
206     d4[1] = (counter & 0xFF) as u8;
207     d4[2] = node_id[0];
208     d4[3] = node_id[1];
209     d4[4] = node_id[2];
210     d4[5] = node_id[3];
211     d4[6] = node_id[4];
212     d4[7] = node_id[5];
213 
214     Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4)
215 }
216 
217 #[cfg(uuid_unstable)]
decode_sorted_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16)218 pub(crate) const fn decode_sorted_rfc4122_timestamp(uuid: &Uuid) -> (u64, u16) {
219     let bytes = uuid.as_bytes();
220 
221     let ticks: u64 = ((bytes[0]) as u64) << 52
222         | (bytes[1] as u64) << 44
223         | (bytes[2] as u64) << 36
224         | (bytes[3] as u64) << 28
225         | (bytes[4] as u64) << 20
226         | (bytes[5] as u64) << 12
227         | ((bytes[6] & 0xF) as u64) << 8
228         | (bytes[7] as u64);
229 
230     let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16);
231 
232     (ticks, counter)
233 }
234 
235 #[cfg(uuid_unstable)]
encode_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Uuid236 pub(crate) const fn encode_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Uuid {
237     let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
238     let millis_low = (millis & 0xFFFF) as u16;
239 
240     let random_and_version =
241         (random_bytes[0] as u16 | ((random_bytes[1] as u16) << 8) & 0x0FFF) | (0x7 << 12);
242 
243     let mut d4 = [0; 8];
244 
245     d4[0] = (random_bytes[2] & 0x3F) | 0x80;
246     d4[1] = random_bytes[3];
247     d4[2] = random_bytes[4];
248     d4[3] = random_bytes[5];
249     d4[4] = random_bytes[6];
250     d4[5] = random_bytes[7];
251     d4[6] = random_bytes[8];
252     d4[7] = random_bytes[9];
253 
254     Uuid::from_fields(millis_high, millis_low, random_and_version, &d4)
255 }
256 
257 #[cfg(uuid_unstable)]
decode_unix_timestamp_millis(uuid: &Uuid) -> u64258 pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 {
259     let bytes = uuid.as_bytes();
260 
261     let millis: u64 = (bytes[0] as u64) << 40
262         | (bytes[1] as u64) << 32
263         | (bytes[2] as u64) << 24
264         | (bytes[3] as u64) << 16
265         | (bytes[4] as u64) << 8
266         | (bytes[5] as u64);
267 
268     millis
269 }
270 
271 #[cfg(all(feature = "std", feature = "js", target_arch = "wasm32"))]
now() -> (u64, u32)272 fn now() -> (u64, u32) {
273     use wasm_bindgen::prelude::*;
274 
275     #[wasm_bindgen]
276     extern "C" {
277         #[wasm_bindgen(js_namespace = Date)]
278         fn now() -> f64;
279     }
280 
281     let now = now();
282 
283     let secs = (now / 1_000.0) as u64;
284     let nanos = ((now % 1_000.0) * 1_000_000.0) as u32;
285 
286     dbg!((secs, nanos))
287 }
288 
289 #[cfg(all(feature = "std", any(not(feature = "js"), not(target_arch = "wasm32"))))]
now() -> (u64, u32)290 fn now() -> (u64, u32) {
291     let dur = std::time::SystemTime::UNIX_EPOCH
292         .elapsed()
293         .expect("Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality");
294 
295     (dur.as_secs(), dur.subsec_nanos())
296 }
297 
298 /// A counter that can be used by version 1 and version 6 UUIDs to support
299 /// the uniqueness of timestamps.
300 ///
301 /// # References
302 ///
303 /// * [Clock Sequence in RFC4122](https://datatracker.ietf.org/doc/html/rfc4122#section-4.1.5)
304 pub trait ClockSequence {
305     /// The type of sequence returned by this counter.
306     type Output;
307 
308     /// Get the next value in the sequence to feed into a timestamp.
309     ///
310     /// This method will be called each time a [`Timestamp`] is constructed.
generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output311     fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output;
312 }
313 
314 impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T {
315     type Output = T::Output;
generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output316     fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output {
317         (**self).generate_sequence(seconds, subsec_nanos)
318     }
319 }
320 
321 /// Default implementations for the [`ClockSequence`] trait.
322 pub mod context {
323     use super::ClockSequence;
324 
325     #[cfg(any(feature = "v1", feature = "v6"))]
326     use atomic::{Atomic, Ordering};
327 
328     /// An empty counter that will always return the value `0`.
329     ///
330     /// This type should be used when constructing timestamps for version 7 UUIDs,
331     /// since they don't need a counter for uniqueness.
332     #[derive(Debug, Clone, Copy, Default)]
333     pub struct NoContext;
334 
335     impl ClockSequence for NoContext {
336         type Output = u16;
337 
generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output338         fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
339             0
340         }
341     }
342 
343     #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
344     static CONTEXT: Context = Context {
345         count: Atomic::new(0),
346     };
347 
348     #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
349     static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false);
350 
351     #[cfg(all(any(feature = "v1", feature = "v6"), feature = "std", feature = "rng"))]
shared_context() -> &'static Context352     pub(crate) fn shared_context() -> &'static Context {
353         // If the context is in its initial state then assign it to a random value
354         // It doesn't matter if multiple threads observe `false` here and initialize the context
355         if CONTEXT_INITIALIZED
356             .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
357             .is_ok()
358         {
359             CONTEXT.count.store(crate::rng::u16(), Ordering::Release);
360         }
361 
362         &CONTEXT
363     }
364 
365     /// A thread-safe, wrapping counter that produces 14-bit numbers.
366     ///
367     /// This type should be used when constructing version 1 and version 6 UUIDs.
368     #[derive(Debug)]
369     #[cfg(any(feature = "v1", feature = "v6"))]
370     pub struct Context {
371         count: Atomic<u16>,
372     }
373 
374     #[cfg(any(feature = "v1", feature = "v6"))]
375     impl Context {
376         /// Construct a new context that's initialized with the given value.
377         ///
378         /// The starting value should be a random number, so that UUIDs from
379         /// different systems with the same timestamps are less likely to collide.
380         /// When the `rng` feature is enabled, prefer the [`Context::new_random`] method.
new(count: u16) -> Self381         pub const fn new(count: u16) -> Self {
382             Self {
383                 count: Atomic::<u16>::new(count),
384             }
385         }
386 
387         /// Construct a new context that's initialized with a random value.
388         #[cfg(feature = "rng")]
new_random() -> Self389         pub fn new_random() -> Self {
390             Self {
391                 count: Atomic::<u16>::new(crate::rng::u16()),
392             }
393         }
394     }
395 
396     #[cfg(any(feature = "v1", feature = "v6"))]
397     impl ClockSequence for Context {
398         type Output = u16;
399 
generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output400         fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
401             // RFC4122 reserves 2 bits of the clock sequence so the actual
402             // maximum value is smaller than `u16::MAX`. Since we unconditionally
403             // increment the clock sequence we want to wrap once it becomes larger
404             // than what we can represent in a "u14". Otherwise there'd be patches
405             // where the clock sequence doesn't change regardless of the timestamp
406             self.count.fetch_add(1, Ordering::AcqRel) % (u16::MAX >> 2)
407         }
408     }
409 }
410