/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/src/timestamp.rs
Line | Count | Source |
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 | | //! * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1) |
21 | | //! * [UUID Version 7 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.7) |
22 | | //! * [Timestamp Considerations in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.1) |
23 | | |
24 | | use core::cmp; |
25 | | |
26 | | use crate::Uuid; |
27 | | |
28 | | /// The number of 100 nanosecond ticks between the RFC 9562 epoch |
29 | | /// (`1582-10-15 00:00:00`) and the Unix epoch (`1970-01-01 00:00:00`). |
30 | | pub const UUID_TICKS_BETWEEN_EPOCHS: u64 = 0x01B2_1DD2_1381_4000; |
31 | | |
32 | | /// A timestamp that can be encoded into a UUID. |
33 | | /// |
34 | | /// This type abstracts the specific encoding, so versions 1, 6, and 7 |
35 | | /// UUIDs can both be supported through the same type, even |
36 | | /// though they have a different representation of a timestamp. |
37 | | /// |
38 | | /// # References |
39 | | /// |
40 | | /// * [Timestamp Considerations in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.1) |
41 | | /// * [UUID Generator States in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.3) |
42 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
43 | | pub struct Timestamp { |
44 | | seconds: u64, |
45 | | subsec_nanos: u32, |
46 | | counter: u128, |
47 | | usable_counter_bits: u8, |
48 | | } |
49 | | |
50 | | impl Timestamp { |
51 | | /// Get a timestamp representing the current system time and up to a 128-bit counter. |
52 | | /// |
53 | | /// This method defers to the standard library's `SystemTime` type. |
54 | | #[cfg(feature = "std")] |
55 | 0 | pub fn now(context: impl ClockSequence<Output = impl Into<u128>>) -> Self { |
56 | 0 | let (seconds, subsec_nanos) = now(); |
57 | | |
58 | 0 | let (counter, seconds, subsec_nanos) = |
59 | 0 | context.generate_timestamp_sequence(seconds, subsec_nanos); |
60 | 0 | let counter = counter.into(); |
61 | 0 | let usable_counter_bits = context.usable_bits() as u8; |
62 | | |
63 | 0 | Timestamp { |
64 | 0 | seconds, |
65 | 0 | subsec_nanos, |
66 | 0 | counter, |
67 | 0 | usable_counter_bits, |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | | /// Construct a `Timestamp` from the number of 100 nanosecond ticks since 00:00:00.00, |
72 | | /// 15 October 1582 (the date of Gregorian reform to the Christian calendar) and a 14-bit |
73 | | /// counter, as used in versions 1 and 6 UUIDs. |
74 | | /// |
75 | | /// # Overflow |
76 | | /// |
77 | | /// If conversion from RFC 9562 ticks to the internal timestamp format would overflow |
78 | | /// it will wrap. |
79 | 0 | pub const fn from_gregorian(ticks: u64, counter: u16) -> Self { |
80 | 0 | let (seconds, subsec_nanos) = Self::gregorian_to_unix(ticks); |
81 | | |
82 | 0 | Timestamp { |
83 | 0 | seconds, |
84 | 0 | subsec_nanos, |
85 | 0 | counter: counter as u128, |
86 | 0 | usable_counter_bits: 14, |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs. |
91 | 0 | pub const fn from_unix_time( |
92 | 0 | seconds: u64, |
93 | 0 | subsec_nanos: u32, |
94 | 0 | counter: u128, |
95 | 0 | usable_counter_bits: u8, |
96 | 0 | ) -> Self { |
97 | 0 | Timestamp { |
98 | 0 | seconds, |
99 | 0 | subsec_nanos, |
100 | 0 | counter, |
101 | 0 | usable_counter_bits, |
102 | 0 | } |
103 | 0 | } |
104 | | |
105 | | /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs. |
106 | 0 | pub fn from_unix( |
107 | 0 | context: impl ClockSequence<Output = impl Into<u128>>, |
108 | 0 | seconds: u64, |
109 | 0 | subsec_nanos: u32, |
110 | 0 | ) -> Self { |
111 | 0 | let (counter, seconds, subsec_nanos) = |
112 | 0 | context.generate_timestamp_sequence(seconds, subsec_nanos); |
113 | 0 | let counter = counter.into(); |
114 | 0 | let usable_counter_bits = context.usable_bits() as u8; |
115 | | |
116 | 0 | Timestamp { |
117 | 0 | seconds, |
118 | 0 | subsec_nanos, |
119 | 0 | counter, |
120 | 0 | usable_counter_bits, |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | | /// Get the value of the timestamp as the number of 100 nanosecond ticks since 00:00:00.00, |
125 | | /// 15 October 1582 and a 14-bit counter, as used in versions 1 and 6 UUIDs. |
126 | | /// |
127 | | /// # Overflow |
128 | | /// |
129 | | /// If conversion from the internal timestamp format to ticks would overflow |
130 | | /// then it will wrap. |
131 | | /// |
132 | | /// If the internal counter is wider than 14 bits then it will be truncated to 14 bits. |
133 | 0 | pub const fn to_gregorian(&self) -> (u64, u16) { |
134 | 0 | ( |
135 | 0 | Self::unix_to_gregorian_ticks(self.seconds, self.subsec_nanos), |
136 | 0 | (self.counter as u16) & 0x3FFF, |
137 | 0 | ) |
138 | 0 | } |
139 | | |
140 | | // NOTE: This method is not public; the usable counter bits are lost in a version 7 UUID |
141 | | // so can't be reliably recovered. |
142 | | #[cfg(feature = "v7")] |
143 | | pub(crate) const fn counter(&self) -> (u128, u8) { |
144 | | (self.counter, self.usable_counter_bits) |
145 | | } |
146 | | |
147 | | /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs. |
148 | 0 | pub const fn to_unix(&self) -> (u64, u32) { |
149 | 0 | (self.seconds, self.subsec_nanos) |
150 | 0 | } |
151 | | |
152 | 0 | const fn unix_to_gregorian_ticks(seconds: u64, nanos: u32) -> u64 { |
153 | | UUID_TICKS_BETWEEN_EPOCHS |
154 | 0 | .wrapping_add(seconds.wrapping_mul(10_000_000)) |
155 | 0 | .wrapping_add(nanos as u64 / 100) |
156 | 0 | } |
157 | | |
158 | 0 | const fn gregorian_to_unix(ticks: u64) -> (u64, u32) { |
159 | 0 | ( |
160 | 0 | ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, |
161 | 0 | (ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100, |
162 | 0 | ) |
163 | 0 | } |
164 | | } |
165 | | |
166 | | #[doc(hidden)] |
167 | | impl Timestamp { |
168 | | #[deprecated( |
169 | | since = "1.10.0", |
170 | | note = "use `Timestamp::from_gregorian(ticks, counter)`" |
171 | | )] |
172 | 0 | pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { |
173 | 0 | Timestamp::from_gregorian(ticks, counter) |
174 | 0 | } |
175 | | |
176 | | #[deprecated(since = "1.10.0", note = "use `Timestamp::to_gregorian()`")] |
177 | 0 | pub const fn to_rfc4122(&self) -> (u64, u16) { |
178 | 0 | self.to_gregorian() |
179 | 0 | } |
180 | | |
181 | | #[deprecated( |
182 | | since = "1.2.0", |
183 | | note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`" |
184 | | )] |
185 | 0 | pub const fn to_unix_nanos(&self) -> u32 { |
186 | 0 | panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`") |
187 | | } |
188 | | } |
189 | | |
190 | | #[cfg(feature = "std")] |
191 | | impl std::convert::TryFrom<std::time::SystemTime> for Timestamp { |
192 | | type Error = crate::Error; |
193 | | |
194 | | /// Perform the conversion. |
195 | | /// |
196 | | /// This method will fail if the system time is earlier than the Unix Epoch. |
197 | | /// On some platforms it may panic instead. |
198 | 0 | fn try_from(st: std::time::SystemTime) -> Result<Self, Self::Error> { |
199 | 0 | let dur = st.duration_since(std::time::UNIX_EPOCH).map_err(|_| crate::Error(crate::error::ErrorKind::InvalidSystemTime("unable to convert the system tie into a Unix timestamp")))?; |
200 | | |
201 | 0 | Ok(Self::from_unix_time( |
202 | 0 | dur.as_secs(), |
203 | 0 | dur.subsec_nanos(), |
204 | 0 | 0, |
205 | 0 | 0, |
206 | 0 | )) |
207 | 0 | } |
208 | | } |
209 | | |
210 | | #[cfg(feature = "std")] |
211 | | impl From<Timestamp> for std::time::SystemTime { |
212 | 0 | fn from(ts: Timestamp) -> Self { |
213 | 0 | let (seconds, subsec_nanos) = ts.to_unix(); |
214 | | |
215 | 0 | Self::UNIX_EPOCH + std::time::Duration::new(seconds, subsec_nanos) |
216 | 0 | } |
217 | | } |
218 | | |
219 | 0 | pub(crate) const fn encode_gregorian_timestamp( |
220 | 0 | ticks: u64, |
221 | 0 | counter: u16, |
222 | 0 | node_id: &[u8; 6], |
223 | 0 | ) -> Uuid { |
224 | 0 | let time_low = (ticks & 0xFFFF_FFFF) as u32; |
225 | 0 | let time_mid = ((ticks >> 32) & 0xFFFF) as u16; |
226 | 0 | let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12); |
227 | | |
228 | 0 | let mut d4 = [0; 8]; |
229 | | |
230 | 0 | d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; |
231 | 0 | d4[1] = (counter & 0xFF) as u8; |
232 | 0 | d4[2] = node_id[0]; |
233 | 0 | d4[3] = node_id[1]; |
234 | 0 | d4[4] = node_id[2]; |
235 | 0 | d4[5] = node_id[3]; |
236 | 0 | d4[6] = node_id[4]; |
237 | 0 | d4[7] = node_id[5]; |
238 | | |
239 | 0 | Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4) |
240 | 0 | } |
241 | | |
242 | 0 | pub(crate) const fn decode_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) { |
243 | 0 | let bytes = uuid.as_bytes(); |
244 | | |
245 | 0 | let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56 |
246 | 0 | | (bytes[7] as u64) << 48 |
247 | 0 | | (bytes[4] as u64) << 40 |
248 | 0 | | (bytes[5] as u64) << 32 |
249 | 0 | | (bytes[0] as u64) << 24 |
250 | 0 | | (bytes[1] as u64) << 16 |
251 | 0 | | (bytes[2] as u64) << 8 |
252 | 0 | | (bytes[3] as u64); |
253 | | |
254 | 0 | let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); |
255 | | |
256 | 0 | (ticks, counter) |
257 | 0 | } |
258 | | |
259 | 0 | pub(crate) const fn encode_sorted_gregorian_timestamp( |
260 | 0 | ticks: u64, |
261 | 0 | counter: u16, |
262 | 0 | node_id: &[u8; 6], |
263 | 0 | ) -> Uuid { |
264 | 0 | let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32; |
265 | 0 | let time_mid = ((ticks >> 12) & 0xFFFF) as u16; |
266 | 0 | let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12); |
267 | | |
268 | 0 | let mut d4 = [0; 8]; |
269 | | |
270 | 0 | d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; |
271 | 0 | d4[1] = (counter & 0xFF) as u8; |
272 | 0 | d4[2] = node_id[0]; |
273 | 0 | d4[3] = node_id[1]; |
274 | 0 | d4[4] = node_id[2]; |
275 | 0 | d4[5] = node_id[3]; |
276 | 0 | d4[6] = node_id[4]; |
277 | 0 | d4[7] = node_id[5]; |
278 | | |
279 | 0 | Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4) |
280 | 0 | } |
281 | | |
282 | 0 | pub(crate) const fn decode_sorted_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) { |
283 | 0 | let bytes = uuid.as_bytes(); |
284 | | |
285 | 0 | let ticks: u64 = ((bytes[0]) as u64) << 52 |
286 | 0 | | (bytes[1] as u64) << 44 |
287 | 0 | | (bytes[2] as u64) << 36 |
288 | 0 | | (bytes[3] as u64) << 28 |
289 | 0 | | (bytes[4] as u64) << 20 |
290 | 0 | | (bytes[5] as u64) << 12 |
291 | 0 | | ((bytes[6] & 0xF) as u64) << 8 |
292 | 0 | | (bytes[7] as u64); |
293 | | |
294 | 0 | let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); |
295 | | |
296 | 0 | (ticks, counter) |
297 | 0 | } |
298 | | |
299 | 0 | pub(crate) const fn encode_unix_timestamp_millis( |
300 | 0 | millis: u64, |
301 | 0 | counter_random_bytes: &[u8; 10], |
302 | 0 | ) -> Uuid { |
303 | 0 | let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32; |
304 | 0 | let millis_low = (millis & 0xFFFF) as u16; |
305 | | |
306 | 0 | let counter_random_version = (counter_random_bytes[1] as u16 |
307 | 0 | | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF) |
308 | 0 | | (0x7 << 12); |
309 | | |
310 | 0 | let mut d4 = [0; 8]; |
311 | | |
312 | 0 | d4[0] = (counter_random_bytes[2] & 0x3F) | 0x80; |
313 | 0 | d4[1] = counter_random_bytes[3]; |
314 | 0 | d4[2] = counter_random_bytes[4]; |
315 | 0 | d4[3] = counter_random_bytes[5]; |
316 | 0 | d4[4] = counter_random_bytes[6]; |
317 | 0 | d4[5] = counter_random_bytes[7]; |
318 | 0 | d4[6] = counter_random_bytes[8]; |
319 | 0 | d4[7] = counter_random_bytes[9]; |
320 | | |
321 | 0 | Uuid::from_fields(millis_high, millis_low, counter_random_version, &d4) |
322 | 0 | } |
323 | | |
324 | 0 | pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 { |
325 | 0 | let bytes = uuid.as_bytes(); |
326 | | |
327 | 0 | let millis: u64 = (bytes[0] as u64) << 40 |
328 | 0 | | (bytes[1] as u64) << 32 |
329 | 0 | | (bytes[2] as u64) << 24 |
330 | 0 | | (bytes[3] as u64) << 16 |
331 | 0 | | (bytes[4] as u64) << 8 |
332 | 0 | | (bytes[5] as u64); |
333 | | |
334 | 0 | millis |
335 | 0 | } |
336 | | |
337 | | #[cfg(all( |
338 | | feature = "std", |
339 | | feature = "js", |
340 | | all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")) |
341 | | ))] |
342 | | fn now() -> (u64, u32) { |
343 | | use wasm_bindgen::prelude::*; |
344 | | |
345 | | #[wasm_bindgen] |
346 | | extern "C" { |
347 | | // NOTE: This signature works around https://bugzilla.mozilla.org/show_bug.cgi?id=1787770 |
348 | | #[wasm_bindgen(js_namespace = Date, catch)] |
349 | | fn now() -> Result<f64, JsValue>; |
350 | | } |
351 | | |
352 | | let now = now().unwrap_throw(); |
353 | | |
354 | | let secs = (now / 1_000.0) as u64; |
355 | | let nanos = ((now % 1_000.0) * 1_000_000.0) as u32; |
356 | | |
357 | | (secs, nanos) |
358 | | } |
359 | | |
360 | | #[cfg(all( |
361 | | feature = "std", |
362 | | not(miri), |
363 | | any( |
364 | | not(feature = "js"), |
365 | | not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))) |
366 | | ) |
367 | | ))] |
368 | 0 | fn now() -> (u64, u32) { |
369 | 0 | let dur = std::time::SystemTime::UNIX_EPOCH.elapsed().expect( |
370 | 0 | "Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality", |
371 | | ); |
372 | | |
373 | 0 | (dur.as_secs(), dur.subsec_nanos()) |
374 | 0 | } |
375 | | |
376 | | #[cfg(all(feature = "std", miri))] |
377 | | fn now() -> (u64, u32) { |
378 | | use std::{sync::Mutex, time::Duration}; |
379 | | |
380 | | static TS: Mutex<u64> = Mutex::new(0); |
381 | | |
382 | | let ts = Duration::from_nanos({ |
383 | | let mut ts = TS.lock().unwrap(); |
384 | | *ts += 1; |
385 | | *ts |
386 | | }); |
387 | | |
388 | | (ts.as_secs(), ts.subsec_nanos()) |
389 | | } |
390 | | |
391 | | /// A counter that can be used by versions 1 and 6 UUIDs to support |
392 | | /// the uniqueness of timestamps. |
393 | | /// |
394 | | /// # References |
395 | | /// |
396 | | /// * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1) |
397 | | /// * [UUID Version 6 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.6) |
398 | | /// * [UUID Generator States in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.3) |
399 | | pub trait ClockSequence { |
400 | | /// The type of sequence returned by this counter. |
401 | | type Output; |
402 | | |
403 | | /// Get the next value in the sequence to feed into a timestamp. |
404 | | /// |
405 | | /// This method will be called each time a [`Timestamp`] is constructed. |
406 | | /// |
407 | | /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset. |
408 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output; |
409 | | |
410 | | /// Get the next value in the sequence, potentially also adjusting the timestamp. |
411 | | /// |
412 | | /// This method should be preferred over `generate_sequence`. |
413 | | /// |
414 | | /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset. |
415 | 0 | fn generate_timestamp_sequence( |
416 | 0 | &self, |
417 | 0 | seconds: u64, |
418 | 0 | subsec_nanos: u32, |
419 | 0 | ) -> (Self::Output, u64, u32) { |
420 | 0 | ( |
421 | 0 | self.generate_sequence(seconds, subsec_nanos), |
422 | 0 | seconds, |
423 | 0 | subsec_nanos, |
424 | 0 | ) |
425 | 0 | } |
426 | | |
427 | | /// The number of usable bits from the least significant bit in the result of [`ClockSequence::generate_sequence`] |
428 | | /// or [`ClockSequence::generate_timestamp_sequence`]. |
429 | | /// |
430 | | /// The number of usable bits must not exceed 128. |
431 | | /// |
432 | | /// The number of usable bits is not expected to change between calls. An implementation of `ClockSequence` should |
433 | | /// always return the same value from this method. |
434 | 0 | fn usable_bits(&self) -> usize |
435 | 0 | where |
436 | 0 | Self::Output: Sized, |
437 | | { |
438 | 0 | cmp::min(128, core::mem::size_of::<Self::Output>()) |
439 | 0 | } |
440 | | } |
441 | | |
442 | | impl<'a, T: ClockSequence + ?Sized> ClockSequence for &'a T { |
443 | | type Output = T::Output; |
444 | | |
445 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
446 | 0 | (**self).generate_sequence(seconds, subsec_nanos) |
447 | 0 | } |
448 | | |
449 | 0 | fn generate_timestamp_sequence( |
450 | 0 | &self, |
451 | 0 | seconds: u64, |
452 | 0 | subsec_nanos: u32, |
453 | 0 | ) -> (Self::Output, u64, u32) { |
454 | 0 | (**self).generate_timestamp_sequence(seconds, subsec_nanos) |
455 | 0 | } |
456 | | |
457 | 0 | fn usable_bits(&self) -> usize |
458 | 0 | where |
459 | 0 | Self::Output: Sized, |
460 | | { |
461 | 0 | (**self).usable_bits() |
462 | 0 | } |
463 | | } |
464 | | |
465 | | /// Default implementations for the [`ClockSequence`] trait. |
466 | | pub mod context { |
467 | | use super::ClockSequence; |
468 | | |
469 | | #[cfg(any(feature = "v1", feature = "v6"))] |
470 | | mod v1_support { |
471 | | use super::*; |
472 | | |
473 | | use atomic::{Atomic, Ordering}; |
474 | | |
475 | | #[cfg(all(feature = "std", feature = "rng"))] |
476 | | static CONTEXT: Context = Context { |
477 | | count: Atomic::new(0), |
478 | | }; |
479 | | |
480 | | #[cfg(all(feature = "std", feature = "rng"))] |
481 | | static CONTEXT_INITIALIZED: Atomic<bool> = Atomic::new(false); |
482 | | |
483 | | #[cfg(all(feature = "std", feature = "rng"))] |
484 | | pub(crate) fn shared_context() -> &'static Context { |
485 | | // If the context is in its initial state then assign it to a random value |
486 | | // It doesn't matter if multiple threads observe `false` here and initialize the context |
487 | | if CONTEXT_INITIALIZED |
488 | | .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) |
489 | | .is_ok() |
490 | | { |
491 | | CONTEXT.count.store(crate::rng::u16(), Ordering::Release); |
492 | | } |
493 | | |
494 | | &CONTEXT |
495 | | } |
496 | | |
497 | | /// A thread-safe, wrapping counter that produces 14-bit values. |
498 | | /// |
499 | | /// This type works by: |
500 | | /// |
501 | | /// 1. Atomically incrementing the counter value for each timestamp. |
502 | | /// 2. Wrapping the counter back to zero if it overflows its 14-bit storage. |
503 | | /// |
504 | | /// This type should be used when constructing versions 1 and 6 UUIDs. |
505 | | /// |
506 | | /// This type should not be used when constructing version 7 UUIDs. When used to |
507 | | /// construct a version 7 UUID, the 14-bit counter will be padded with random data. |
508 | | /// Counter overflows are more likely with a 14-bit counter than they are with a |
509 | | /// 42-bit counter when working at millisecond precision. This type doesn't attempt |
510 | | /// to adjust the timestamp on overflow. |
511 | | #[derive(Debug)] |
512 | | pub struct Context { |
513 | | count: Atomic<u16>, |
514 | | } |
515 | | |
516 | | impl Context { |
517 | | /// Construct a new context that's initialized with the given value. |
518 | | /// |
519 | | /// The starting value should be a random number, so that UUIDs from |
520 | | /// different systems with the same timestamps are less likely to collide. |
521 | | /// When the `rng` feature is enabled, prefer the [`Context::new_random`] method. |
522 | | pub const fn new(count: u16) -> Self { |
523 | | Self { |
524 | | count: Atomic::<u16>::new(count), |
525 | | } |
526 | | } |
527 | | |
528 | | /// Construct a new context that's initialized with a random value. |
529 | | #[cfg(feature = "rng")] |
530 | | pub fn new_random() -> Self { |
531 | | Self { |
532 | | count: Atomic::<u16>::new(crate::rng::u16()), |
533 | | } |
534 | | } |
535 | | } |
536 | | |
537 | | impl ClockSequence for Context { |
538 | | type Output = u16; |
539 | | |
540 | | fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output { |
541 | | // RFC 9562 reserves 2 bits of the clock sequence so the actual |
542 | | // maximum value is smaller than `u16::MAX`. Since we unconditionally |
543 | | // increment the clock sequence we want to wrap once it becomes larger |
544 | | // than what we can represent in a "u14". Otherwise there'd be patches |
545 | | // where the clock sequence doesn't change regardless of the timestamp |
546 | | self.count.fetch_add(1, Ordering::AcqRel) & (u16::MAX >> 2) |
547 | | } |
548 | | |
549 | | fn usable_bits(&self) -> usize { |
550 | | 14 |
551 | | } |
552 | | } |
553 | | |
554 | | #[cfg(test)] |
555 | | mod tests { |
556 | | use crate::Timestamp; |
557 | | |
558 | | use super::*; |
559 | | |
560 | | #[test] |
561 | | fn context() { |
562 | | let seconds = 1_496_854_535; |
563 | | let subsec_nanos = 812_946_000; |
564 | | |
565 | | let context = Context::new(u16::MAX >> 2); |
566 | | |
567 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
568 | | assert_eq!(16383, ts.counter); |
569 | | assert_eq!(14, ts.usable_counter_bits); |
570 | | |
571 | | let seconds = 1_496_854_536; |
572 | | |
573 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
574 | | assert_eq!(0, ts.counter); |
575 | | |
576 | | let seconds = 1_496_854_535; |
577 | | |
578 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
579 | | assert_eq!(1, ts.counter); |
580 | | } |
581 | | |
582 | | #[test] |
583 | | fn context_overflow() { |
584 | | let seconds = u64::MAX; |
585 | | let subsec_nanos = u32::MAX; |
586 | | |
587 | | let context = Context::new(u16::MAX); |
588 | | |
589 | | // Ensure we don't panic |
590 | | Timestamp::from_unix(&context, seconds, subsec_nanos); |
591 | | } |
592 | | } |
593 | | } |
594 | | |
595 | | #[cfg(any(feature = "v1", feature = "v6"))] |
596 | | pub use v1_support::*; |
597 | | |
598 | | #[cfg(feature = "std")] |
599 | | mod std_support { |
600 | | use super::*; |
601 | | |
602 | | use core::panic::{AssertUnwindSafe, RefUnwindSafe}; |
603 | | use std::{sync::Mutex, thread::LocalKey}; |
604 | | |
605 | | /// A wrapper for a context that uses thread-local storage. |
606 | | pub struct ThreadLocalContext<C: 'static>(&'static LocalKey<C>); |
607 | | |
608 | | impl<C> std::fmt::Debug for ThreadLocalContext<C> { |
609 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
610 | 0 | f.debug_struct("ThreadLocalContext").finish_non_exhaustive() |
611 | 0 | } |
612 | | } |
613 | | |
614 | | impl<C: 'static> ThreadLocalContext<C> { |
615 | | /// Wrap a thread-local container with a context. |
616 | 0 | pub const fn new(local_key: &'static LocalKey<C>) -> Self { |
617 | 0 | ThreadLocalContext(local_key) |
618 | 0 | } |
619 | | } |
620 | | |
621 | | impl<C: ClockSequence + 'static> ClockSequence for ThreadLocalContext<C> { |
622 | | type Output = C::Output; |
623 | | |
624 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
625 | 0 | self.0 |
626 | 0 | .with(|ctxt| ctxt.generate_sequence(seconds, subsec_nanos)) |
627 | 0 | } |
628 | | |
629 | 0 | fn generate_timestamp_sequence( |
630 | 0 | &self, |
631 | 0 | seconds: u64, |
632 | 0 | subsec_nanos: u32, |
633 | 0 | ) -> (Self::Output, u64, u32) { |
634 | 0 | self.0 |
635 | 0 | .with(|ctxt| ctxt.generate_timestamp_sequence(seconds, subsec_nanos)) |
636 | 0 | } |
637 | | |
638 | 0 | fn usable_bits(&self) -> usize { |
639 | 0 | self.0.with(|ctxt| ctxt.usable_bits()) |
640 | 0 | } |
641 | | } |
642 | | |
643 | | impl<C: ClockSequence> ClockSequence for AssertUnwindSafe<C> { |
644 | | type Output = C::Output; |
645 | | |
646 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
647 | 0 | self.0.generate_sequence(seconds, subsec_nanos) |
648 | 0 | } |
649 | | |
650 | 0 | fn generate_timestamp_sequence( |
651 | 0 | &self, |
652 | 0 | seconds: u64, |
653 | 0 | subsec_nanos: u32, |
654 | 0 | ) -> (Self::Output, u64, u32) { |
655 | 0 | self.0.generate_timestamp_sequence(seconds, subsec_nanos) |
656 | 0 | } |
657 | | |
658 | 0 | fn usable_bits(&self) -> usize |
659 | 0 | where |
660 | 0 | Self::Output: Sized, |
661 | | { |
662 | 0 | self.0.usable_bits() |
663 | 0 | } |
664 | | } |
665 | | |
666 | | impl<C: ClockSequence + RefUnwindSafe> ClockSequence for Mutex<C> { |
667 | | type Output = C::Output; |
668 | | |
669 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
670 | 0 | self.lock() |
671 | 0 | .unwrap_or_else(|err| err.into_inner()) |
672 | 0 | .generate_sequence(seconds, subsec_nanos) |
673 | 0 | } |
674 | | |
675 | 0 | fn generate_timestamp_sequence( |
676 | 0 | &self, |
677 | 0 | seconds: u64, |
678 | 0 | subsec_nanos: u32, |
679 | 0 | ) -> (Self::Output, u64, u32) { |
680 | 0 | self.lock() |
681 | 0 | .unwrap_or_else(|err| err.into_inner()) |
682 | 0 | .generate_timestamp_sequence(seconds, subsec_nanos) |
683 | 0 | } |
684 | | |
685 | 0 | fn usable_bits(&self) -> usize |
686 | 0 | where |
687 | 0 | Self::Output: Sized, |
688 | | { |
689 | 0 | self.lock() |
690 | 0 | .unwrap_or_else(|err| err.into_inner()) |
691 | 0 | .usable_bits() |
692 | 0 | } |
693 | | } |
694 | | } |
695 | | |
696 | | #[cfg(feature = "std")] |
697 | | pub use std_support::*; |
698 | | |
699 | | #[cfg(feature = "v7")] |
700 | | mod v7_support { |
701 | | use super::*; |
702 | | |
703 | | use core::{cell::Cell, cmp, panic::RefUnwindSafe}; |
704 | | |
705 | | #[cfg(feature = "std")] |
706 | | static CONTEXT_V7: SharedContextV7 = |
707 | | SharedContextV7(std::sync::Mutex::new(ContextV7::new())); |
708 | | |
709 | | #[cfg(feature = "std")] |
710 | | pub(crate) fn shared_context_v7() -> &'static SharedContextV7 { |
711 | | &CONTEXT_V7 |
712 | | } |
713 | | |
714 | | const USABLE_BITS: usize = 42; |
715 | | |
716 | | // Leave the most significant bit unset |
717 | | // This guarantees the counter has at least 2,199,023,255,552 |
718 | | // values before it will overflow, which is exceptionally unlikely |
719 | | // even in the worst case |
720 | | const RESEED_MASK: u64 = u64::MAX >> 23; |
721 | | const MAX_COUNTER: u64 = u64::MAX >> 22; |
722 | | |
723 | | /// An unsynchronized, reseeding counter that produces 42-bit values. |
724 | | /// |
725 | | /// This type works by: |
726 | | /// |
727 | | /// 1. Reseeding the counter each millisecond with a random 41-bit value. The 42nd bit |
728 | | /// is left unset so the counter can safely increment over the millisecond. |
729 | | /// 2. Wrapping the counter back to zero if it overflows its 42-bit storage and adding a |
730 | | /// millisecond to the timestamp. |
731 | | /// |
732 | | /// The counter can use additional sub-millisecond precision from the timestamp to better |
733 | | /// synchronize UUID sorting in distributed systems. In these cases, the additional precision |
734 | | /// is masked into the left-most 12 bits of the counter. The counter is still reseeded on |
735 | | /// each new millisecond, and incremented within the millisecond. This behavior may change |
736 | | /// in the future. The only guarantee is monotonicity. |
737 | | /// |
738 | | /// This type can be used when constructing version 7 UUIDs. When used to construct a |
739 | | /// version 7 UUID, the 42-bit counter will be padded with random data. This type can |
740 | | /// be used to maintain ordering of UUIDs within the same millisecond. |
741 | | /// |
742 | | /// This type should not be used when constructing version 1 or version 6 UUIDs. |
743 | | /// When used to construct a version 1 or version 6 UUID, only the 14 least significant |
744 | | /// bits of the counter will be used. |
745 | | #[derive(Debug)] |
746 | | pub struct ContextV7 { |
747 | | timestamp: Cell<ReseedingTimestamp>, |
748 | | counter: Cell<Counter>, |
749 | | adjust: Adjust, |
750 | | precision: Precision, |
751 | | } |
752 | | |
753 | | impl RefUnwindSafe for ContextV7 {} |
754 | | |
755 | | impl ContextV7 { |
756 | | /// Construct a new context that will reseed its counter on the first |
757 | | /// non-zero timestamp it receives. |
758 | | pub const fn new() -> Self { |
759 | | ContextV7 { |
760 | | timestamp: Cell::new(ReseedingTimestamp { |
761 | | last_seed: 0, |
762 | | seconds: 0, |
763 | | subsec_nanos: 0, |
764 | | }), |
765 | | counter: Cell::new(Counter { value: 0 }), |
766 | | adjust: Adjust { by_ns: 0 }, |
767 | | precision: Precision { |
768 | | bits: 0, |
769 | | mask: 0, |
770 | | factor: 0, |
771 | | shift: 0, |
772 | | }, |
773 | | } |
774 | | } |
775 | | |
776 | | /// Specify an amount to shift timestamps by to obfuscate their actual generation time. |
777 | | pub fn with_adjust_by_millis(mut self, millis: u32) -> Self { |
778 | | self.adjust = Adjust::by_millis(millis); |
779 | | self |
780 | | } |
781 | | |
782 | | /// Use the leftmost 12 bits of the counter for additional timestamp precision. |
783 | | /// |
784 | | /// This method can provide better sorting for distributed applications that generate frequent UUIDs |
785 | | /// by trading a small amount of entropy for better counter synchronization. Note that the counter |
786 | | /// will still be reseeded on millisecond boundaries, even though some of its storage will be |
787 | | /// dedicated to the timestamp. |
788 | | pub fn with_additional_precision(mut self) -> Self { |
789 | | self.precision = Precision::new(12); |
790 | | self |
791 | | } |
792 | | } |
793 | | |
794 | | impl ClockSequence for ContextV7 { |
795 | | type Output = u64; |
796 | | |
797 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
798 | | self.generate_timestamp_sequence(seconds, subsec_nanos).0 |
799 | | } |
800 | | |
801 | | fn generate_timestamp_sequence( |
802 | | &self, |
803 | | seconds: u64, |
804 | | subsec_nanos: u32, |
805 | | ) -> (Self::Output, u64, u32) { |
806 | | let (seconds, subsec_nanos) = self.adjust.apply(seconds, subsec_nanos); |
807 | | |
808 | | let mut counter; |
809 | | let (mut timestamp, should_reseed) = |
810 | | self.timestamp.get().advance(seconds, subsec_nanos); |
811 | | |
812 | | if should_reseed { |
813 | | // If the observed system time has shifted forwards then regenerate the counter |
814 | | counter = Counter::reseed(&self.precision, ×tamp); |
815 | | } else { |
816 | | // If the observed system time has not shifted forwards then increment the counter |
817 | | |
818 | | // If the incoming timestamp is earlier than the last observed one then |
819 | | // use it instead. This may happen if the system clock jitters, or if the counter |
820 | | // has wrapped and the timestamp is artificially incremented |
821 | | |
822 | | counter = self.counter.get().increment(&self.precision, ×tamp); |
823 | | |
824 | | // Unlikely: If the counter has overflowed its 42-bit storage then wrap it |
825 | | // and increment the timestamp. Until the observed system time shifts past |
826 | | // this incremented value, all timestamps will use it to maintain monotonicity |
827 | | if counter.has_overflowed() { |
828 | | // Increment the timestamp by 1 milli and reseed the counter |
829 | | timestamp = timestamp.increment(); |
830 | | counter = Counter::reseed(&self.precision, ×tamp); |
831 | | } |
832 | | }; |
833 | | |
834 | | self.timestamp.set(timestamp); |
835 | | self.counter.set(counter); |
836 | | |
837 | | (counter.value, timestamp.seconds, timestamp.subsec_nanos) |
838 | | } |
839 | | |
840 | | fn usable_bits(&self) -> usize { |
841 | | USABLE_BITS |
842 | | } |
843 | | } |
844 | | |
845 | | #[derive(Debug)] |
846 | | struct Adjust { |
847 | | by_ns: u128, |
848 | | } |
849 | | |
850 | | impl Adjust { |
851 | | #[inline] |
852 | | fn by_millis(millis: u32) -> Self { |
853 | | Adjust { |
854 | | by_ns: (millis as u128).saturating_mul(1_000_000), |
855 | | } |
856 | | } |
857 | | |
858 | | #[inline] |
859 | | fn apply(&self, seconds: u64, subsec_nanos: u32) -> (u64, u32) { |
860 | | if self.by_ns == 0 { |
861 | | // No shift applied |
862 | | return (seconds, subsec_nanos); |
863 | | } |
864 | | |
865 | | let ts = (seconds as u128) |
866 | | .saturating_mul(1_000_000_000) |
867 | | .saturating_add(subsec_nanos as u128) |
868 | | .saturating_add(self.by_ns as u128); |
869 | | |
870 | | ((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32) |
871 | | } |
872 | | } |
873 | | |
874 | | #[derive(Debug, Default, Clone, Copy)] |
875 | | struct ReseedingTimestamp { |
876 | | last_seed: u64, |
877 | | seconds: u64, |
878 | | subsec_nanos: u32, |
879 | | } |
880 | | |
881 | | impl ReseedingTimestamp { |
882 | | #[inline] |
883 | | fn advance(&self, seconds: u64, subsec_nanos: u32) -> (Self, bool) { |
884 | | let incoming = ReseedingTimestamp::from_ts(seconds, subsec_nanos); |
885 | | |
886 | | if incoming.last_seed > self.last_seed { |
887 | | // The incoming value is part of a new millisecond |
888 | | (incoming, true) |
889 | | } else { |
890 | | // The incoming value is part of the same or an earlier millisecond |
891 | | // We may still have advanced the subsecond portion, so use the larger value |
892 | | let mut value = *self; |
893 | | value.subsec_nanos = cmp::max(self.subsec_nanos, subsec_nanos); |
894 | | |
895 | | (value, false) |
896 | | } |
897 | | } |
898 | | |
899 | | #[inline] |
900 | | fn from_ts(seconds: u64, subsec_nanos: u32) -> Self { |
901 | | // Reseed when the millisecond advances |
902 | | let last_seed = seconds |
903 | | .saturating_mul(1_000) |
904 | | .saturating_add((subsec_nanos / 1_000_000) as u64); |
905 | | |
906 | | ReseedingTimestamp { |
907 | | last_seed, |
908 | | seconds, |
909 | | subsec_nanos, |
910 | | } |
911 | | } |
912 | | |
913 | | #[inline] |
914 | | fn increment(&self) -> Self { |
915 | | let (seconds, subsec_nanos) = |
916 | | Adjust::by_millis(1).apply(self.seconds, self.subsec_nanos); |
917 | | |
918 | | ReseedingTimestamp::from_ts(seconds, subsec_nanos) |
919 | | } |
920 | | |
921 | | #[inline] |
922 | | fn submilli_nanos(&self) -> u32 { |
923 | | self.subsec_nanos % 1_000_000 |
924 | | } |
925 | | } |
926 | | |
927 | | #[derive(Debug)] |
928 | | struct Precision { |
929 | | bits: usize, |
930 | | factor: u64, |
931 | | mask: u64, |
932 | | shift: u64, |
933 | | } |
934 | | |
935 | | impl Precision { |
936 | | fn new(bits: usize) -> Self { |
937 | | // The mask and shift are used to paste the sub-millisecond precision |
938 | | // into the most significant bits of the counter |
939 | | let mask = u64::MAX >> (64 - USABLE_BITS + bits); |
940 | | let shift = (USABLE_BITS - bits) as u64; |
941 | | |
942 | | // The factor reduces the size of the sub-millisecond precision to |
943 | | // fit into the specified number of bits |
944 | | let factor = (999_999 / u64::pow(2, bits as u32)) + 1; |
945 | | |
946 | | Precision { |
947 | | bits, |
948 | | factor, |
949 | | mask, |
950 | | shift, |
951 | | } |
952 | | } |
953 | | |
954 | | #[inline] |
955 | | fn apply(&self, value: u64, timestamp: &ReseedingTimestamp) -> u64 { |
956 | | if self.bits == 0 { |
957 | | // No additional precision is being used |
958 | | return value; |
959 | | } |
960 | | |
961 | | let additional = timestamp.submilli_nanos() as u64 / self.factor; |
962 | | |
963 | | (value & self.mask) | (additional << self.shift) |
964 | | } |
965 | | } |
966 | | |
967 | | #[derive(Debug, Clone, Copy)] |
968 | | struct Counter { |
969 | | value: u64, |
970 | | } |
971 | | |
972 | | impl Counter { |
973 | | #[inline] |
974 | | fn reseed(precision: &Precision, timestamp: &ReseedingTimestamp) -> Self { |
975 | | Counter { |
976 | | value: precision.apply(crate::rng::u64() & RESEED_MASK, timestamp), |
977 | | } |
978 | | } |
979 | | |
980 | | #[inline] |
981 | | fn increment(&self, precision: &Precision, timestamp: &ReseedingTimestamp) -> Self { |
982 | | let mut counter = Counter { |
983 | | value: precision.apply(self.value, timestamp), |
984 | | }; |
985 | | |
986 | | // We unconditionally increment the counter even though the precision |
987 | | // may have set higher bits already. This could technically be avoided, |
988 | | // but the higher bits are a coarse approximation so we just avoid the |
989 | | // `if` branch and increment it either way |
990 | | |
991 | | // Guaranteed to never overflow u64 |
992 | | counter.value += 1; |
993 | | |
994 | | counter |
995 | | } |
996 | | |
997 | | #[inline] |
998 | | fn has_overflowed(&self) -> bool { |
999 | | self.value > MAX_COUNTER |
1000 | | } |
1001 | | } |
1002 | | |
1003 | | #[cfg(feature = "std")] |
1004 | | pub(crate) struct SharedContextV7(std::sync::Mutex<ContextV7>); |
1005 | | |
1006 | | #[cfg(feature = "std")] |
1007 | | impl ClockSequence for SharedContextV7 { |
1008 | | type Output = u64; |
1009 | | |
1010 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
1011 | | self.0.generate_sequence(seconds, subsec_nanos) |
1012 | | } |
1013 | | |
1014 | | fn generate_timestamp_sequence( |
1015 | | &self, |
1016 | | seconds: u64, |
1017 | | subsec_nanos: u32, |
1018 | | ) -> (Self::Output, u64, u32) { |
1019 | | self.0.generate_timestamp_sequence(seconds, subsec_nanos) |
1020 | | } |
1021 | | |
1022 | | fn usable_bits(&self) -> usize |
1023 | | where |
1024 | | Self::Output: Sized, |
1025 | | { |
1026 | | USABLE_BITS |
1027 | | } |
1028 | | } |
1029 | | |
1030 | | #[cfg(test)] |
1031 | | mod tests { |
1032 | | use core::time::Duration; |
1033 | | |
1034 | | use super::*; |
1035 | | |
1036 | | use crate::{Timestamp, Uuid}; |
1037 | | |
1038 | | #[test] |
1039 | | fn context() { |
1040 | | let seconds = 1_496_854_535; |
1041 | | let subsec_nanos = 812_946_000; |
1042 | | |
1043 | | let context = ContextV7::new(); |
1044 | | |
1045 | | let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1046 | | assert_eq!(42, ts1.usable_counter_bits); |
1047 | | |
1048 | | // Backwards second |
1049 | | let seconds = 1_496_854_534; |
1050 | | |
1051 | | let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1052 | | |
1053 | | // The backwards time should be ignored |
1054 | | // The counter should still increment |
1055 | | assert_eq!(ts1.seconds, ts2.seconds); |
1056 | | assert_eq!(ts1.subsec_nanos, ts2.subsec_nanos); |
1057 | | assert_eq!(ts1.counter + 1, ts2.counter); |
1058 | | |
1059 | | // Forwards second |
1060 | | let seconds = 1_496_854_536; |
1061 | | |
1062 | | let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1063 | | |
1064 | | // The counter should have reseeded |
1065 | | assert_ne!(ts2.counter + 1, ts3.counter); |
1066 | | assert_ne!(0, ts3.counter); |
1067 | | } |
1068 | | |
1069 | | #[test] |
1070 | | fn context_wrap() { |
1071 | | let seconds = 1_496_854_535u64; |
1072 | | let subsec_nanos = 812_946_000u32; |
1073 | | |
1074 | | // This context will wrap |
1075 | | let context = ContextV7 { |
1076 | | timestamp: Cell::new(ReseedingTimestamp::from_ts(seconds, subsec_nanos)), |
1077 | | adjust: Adjust::by_millis(0), |
1078 | | precision: Precision { |
1079 | | bits: 0, |
1080 | | mask: 0, |
1081 | | factor: 0, |
1082 | | shift: 0, |
1083 | | }, |
1084 | | counter: Cell::new(Counter { |
1085 | | value: u64::MAX >> 22, |
1086 | | }), |
1087 | | }; |
1088 | | |
1089 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1090 | | |
1091 | | // The timestamp should be incremented by 1ms |
1092 | | let expected_ts = Duration::new(seconds, subsec_nanos) + Duration::from_millis(1); |
1093 | | assert_eq!(expected_ts.as_secs(), ts.seconds); |
1094 | | assert_eq!(expected_ts.subsec_nanos(), ts.subsec_nanos); |
1095 | | |
1096 | | // The counter should have reseeded |
1097 | | assert!(ts.counter < (u64::MAX >> 22) as u128); |
1098 | | assert_ne!(0, ts.counter); |
1099 | | } |
1100 | | |
1101 | | #[test] |
1102 | | fn context_shift() { |
1103 | | let seconds = 1_496_854_535; |
1104 | | let subsec_nanos = 812_946_000; |
1105 | | |
1106 | | let context = ContextV7::new().with_adjust_by_millis(1); |
1107 | | |
1108 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1109 | | |
1110 | | assert_eq!((1_496_854_535, 813_946_000), ts.to_unix()); |
1111 | | } |
1112 | | |
1113 | | #[test] |
1114 | | fn context_additional_precision() { |
1115 | | let seconds = 1_496_854_535; |
1116 | | let subsec_nanos = 812_946_000; |
1117 | | |
1118 | | let context = ContextV7::new().with_additional_precision(); |
1119 | | |
1120 | | let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1121 | | |
1122 | | // NOTE: Future changes in rounding may change this value slightly |
1123 | | assert_eq!(3861, ts1.counter >> 30); |
1124 | | |
1125 | | assert!(ts1.counter < (u64::MAX >> 22) as u128); |
1126 | | |
1127 | | // Generate another timestamp; it should continue to sort |
1128 | | let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1129 | | |
1130 | | assert!(Uuid::new_v7(ts2) > Uuid::new_v7(ts1)); |
1131 | | |
1132 | | // Generate another timestamp with an extra nanosecond |
1133 | | let subsec_nanos = subsec_nanos + 1; |
1134 | | |
1135 | | let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1136 | | |
1137 | | assert!(Uuid::new_v7(ts3) > Uuid::new_v7(ts2)); |
1138 | | } |
1139 | | |
1140 | | #[test] |
1141 | | fn context_overflow() { |
1142 | | let seconds = u64::MAX; |
1143 | | let subsec_nanos = u32::MAX; |
1144 | | |
1145 | | // Ensure we don't panic |
1146 | | for context in [ |
1147 | | ContextV7::new(), |
1148 | | ContextV7::new().with_additional_precision(), |
1149 | | ContextV7::new().with_adjust_by_millis(u32::MAX), |
1150 | | ] { |
1151 | | Timestamp::from_unix(&context, seconds, subsec_nanos); |
1152 | | } |
1153 | | } |
1154 | | } |
1155 | | } |
1156 | | |
1157 | | #[cfg(feature = "v7")] |
1158 | | pub use v7_support::*; |
1159 | | |
1160 | | /// An empty counter that will always return the value `0`. |
1161 | | /// |
1162 | | /// This type can be used when constructing version 7 UUIDs. When used to |
1163 | | /// construct a version 7 UUID, the entire counter segment of the UUID will be |
1164 | | /// filled with a random value. This type does not maintain ordering of UUIDs |
1165 | | /// within a millisecond but is efficient. |
1166 | | /// |
1167 | | /// This type should not be used when constructing version 1 or version 6 UUIDs. |
1168 | | /// When used to construct a version 1 or version 6 UUID, the counter |
1169 | | /// segment will remain zero. |
1170 | | #[derive(Debug, Clone, Copy, Default)] |
1171 | | pub struct NoContext; |
1172 | | |
1173 | | impl ClockSequence for NoContext { |
1174 | | type Output = u16; |
1175 | | |
1176 | 0 | fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output { |
1177 | 0 | 0 |
1178 | 0 | } |
1179 | | |
1180 | 0 | fn usable_bits(&self) -> usize { |
1181 | 0 | 0 |
1182 | 0 | } |
1183 | | } |
1184 | | } |
1185 | | |
1186 | | #[cfg(all(test, any(feature = "v1", feature = "v6")))] |
1187 | | mod tests { |
1188 | | use super::*; |
1189 | | |
1190 | | #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))] |
1191 | | use wasm_bindgen_test::*; |
1192 | | |
1193 | | #[test] |
1194 | | #[cfg_attr( |
1195 | | all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), |
1196 | | wasm_bindgen_test |
1197 | | )] |
1198 | | fn gregorian_unix_does_not_panic() { |
1199 | | // Ensure timestamp conversions never panic |
1200 | | Timestamp::unix_to_gregorian_ticks(u64::MAX, 0); |
1201 | | Timestamp::unix_to_gregorian_ticks(0, u32::MAX); |
1202 | | Timestamp::unix_to_gregorian_ticks(u64::MAX, u32::MAX); |
1203 | | |
1204 | | Timestamp::gregorian_to_unix(u64::MAX); |
1205 | | } |
1206 | | |
1207 | | #[test] |
1208 | | #[cfg_attr( |
1209 | | all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")), |
1210 | | wasm_bindgen_test |
1211 | | )] |
1212 | | fn to_gregorian_truncates_to_usable_bits() { |
1213 | | let ts = Timestamp::from_gregorian(123, u16::MAX); |
1214 | | |
1215 | | assert_eq!((123, u16::MAX >> 2), ts.to_gregorian()); |
1216 | | } |
1217 | | } |
1218 | | |
1219 | | /// Tests for conversion between `std::time::SystemTime` and `Timestamp`. |
1220 | | #[cfg(all(test, feature = "std", not(miri)))] |
1221 | | mod test_conversion { |
1222 | | use std::{ |
1223 | | convert::{TryFrom, TryInto}, |
1224 | | time::{Duration, SystemTime}, |
1225 | | }; |
1226 | | |
1227 | | use super::Timestamp; |
1228 | | |
1229 | | // Components of an arbitrary timestamp with non-zero nanoseconds. |
1230 | | const KNOWN_SECONDS: u64 = 1_501_520_400; |
1231 | | const KNOWN_NANOS: u32 = 1_000; |
1232 | | |
1233 | | fn known_system_time() -> SystemTime { |
1234 | | SystemTime::UNIX_EPOCH |
1235 | | .checked_add(Duration::new(KNOWN_SECONDS, KNOWN_NANOS)) |
1236 | | .unwrap() |
1237 | | } |
1238 | | |
1239 | | fn known_timestamp() -> Timestamp { |
1240 | | Timestamp::from_unix_time(KNOWN_SECONDS, KNOWN_NANOS, 0, 0) |
1241 | | } |
1242 | | |
1243 | | #[test] |
1244 | | fn to_system_time() { |
1245 | | let st: SystemTime = known_timestamp().into(); |
1246 | | |
1247 | | assert_eq!(known_system_time(), st); |
1248 | | } |
1249 | | |
1250 | | #[test] |
1251 | | fn from_system_time() { |
1252 | | let ts: Timestamp = known_system_time().try_into().unwrap(); |
1253 | | |
1254 | | assert_eq!(known_timestamp(), ts); |
1255 | | } |
1256 | | |
1257 | | #[test] |
1258 | | fn from_system_time_before_epoch() { |
1259 | | let before_epoch = match SystemTime::UNIX_EPOCH.checked_sub(Duration::from_nanos(1_000)) { |
1260 | | Some(st) => st, |
1261 | | None => return, |
1262 | | }; |
1263 | | |
1264 | | Timestamp::try_from(before_epoch) |
1265 | | .expect_err("Timestamp should not be created from before epoch"); |
1266 | | } |
1267 | | } |