/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.29.2/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 | | unix_nanos: u128, |
45 | | counter: u128, |
46 | | usable_counter_bits: u8, |
47 | | } |
48 | | |
49 | | impl Timestamp { |
50 | | /// Get a timestamp representing the current system time and up to a 128-bit counter. |
51 | | /// |
52 | | /// This method defers to the standard library's `SystemTime` type. |
53 | | #[cfg(feature = "std")] |
54 | 0 | pub fn now(context: impl ClockSequence<Output = impl Into<u128>>) -> Self { |
55 | 0 | let (seconds, subsec_nanos) = nanos_to_parts(now()); |
56 | | |
57 | 0 | let (counter, seconds, subsec_nanos) = |
58 | 0 | context.generate_timestamp_sequence(seconds, subsec_nanos); |
59 | 0 | let counter = counter.into(); |
60 | 0 | let usable_counter_bits = context.usable_bits() as u8; |
61 | | |
62 | 0 | Timestamp { |
63 | 0 | unix_nanos: parts_to_nanos(seconds, subsec_nanos), |
64 | 0 | counter, |
65 | 0 | usable_counter_bits, |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | /// Construct a `Timestamp` from the number of 100 nanosecond ticks since 00:00:00.00, |
70 | | /// 15 October 1582 (the date of Gregorian reform to the Christian calendar) and a 14-bit |
71 | | /// counter, as used in versions 1 and 6 UUIDs. |
72 | | /// |
73 | | /// # Overflow |
74 | | /// |
75 | | /// If conversion from RFC 9562 ticks to the internal timestamp format would overflow |
76 | | /// it will wrap. |
77 | 0 | pub const fn from_gregorian_time(ticks: u64, counter: u16) -> Self { |
78 | 0 | let (seconds, subsec_nanos) = Self::gregorian_to_unix(ticks); |
79 | | |
80 | 0 | Timestamp { |
81 | 0 | unix_nanos: parts_to_nanos(seconds, subsec_nanos), |
82 | 0 | counter: counter as u128, |
83 | 0 | usable_counter_bits: 14, |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs. |
88 | 0 | pub const fn from_unix_time( |
89 | 0 | seconds: u64, |
90 | 0 | subsec_nanos: u32, |
91 | 0 | counter: u128, |
92 | 0 | usable_counter_bits: u8, |
93 | 0 | ) -> Self { |
94 | 0 | Timestamp { |
95 | 0 | unix_nanos: parts_to_nanos(seconds, subsec_nanos), |
96 | 0 | counter, |
97 | 0 | usable_counter_bits, |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | | /// Construct a `Timestamp` from a Unix timestamp and up to a 128-bit counter, as used in version 7 UUIDs. |
102 | 0 | pub fn from_unix( |
103 | 0 | context: impl ClockSequence<Output = impl Into<u128>>, |
104 | 0 | seconds: u64, |
105 | 0 | subsec_nanos: u32, |
106 | 0 | ) -> Self { |
107 | 0 | let (counter, seconds, subsec_nanos) = |
108 | 0 | context.generate_timestamp_sequence(seconds, subsec_nanos); |
109 | 0 | let counter = counter.into(); |
110 | 0 | let usable_counter_bits = context.usable_bits() as u8; |
111 | | |
112 | 0 | Timestamp { |
113 | 0 | unix_nanos: parts_to_nanos(seconds, subsec_nanos), |
114 | 0 | counter, |
115 | 0 | usable_counter_bits, |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | | /// Get the value of the timestamp as the number of 100 nanosecond ticks since 00:00:00.00, |
120 | | /// 15 October 1582 and a 14-bit counter, as used in versions 1 and 6 UUIDs. |
121 | | /// |
122 | | /// # Overflow |
123 | | /// |
124 | | /// If conversion from the internal timestamp format to ticks would overflow |
125 | | /// then it will wrap. |
126 | | /// |
127 | | /// If the internal counter is wider than 14 bits then it will be truncated to 14 bits. |
128 | 0 | pub const fn to_gregorian(&self) -> (u64, u16) { |
129 | 0 | let (seconds, subsec_nanos) = nanos_to_parts(self.unix_nanos); |
130 | | |
131 | 0 | ( |
132 | 0 | Self::unix_to_gregorian_ticks(seconds, subsec_nanos), |
133 | 0 | (self.counter as u16) & 0x3FFF, |
134 | 0 | ) |
135 | 0 | } |
136 | | |
137 | | // NOTE: This method is not public; the usable counter bits are lost in a version 7 UUID |
138 | | // so can't be reliably recovered. |
139 | | #[cfg(feature = "v7")] |
140 | | pub(crate) const fn counter(&self) -> (u128, u8) { |
141 | | (self.counter, self.usable_counter_bits) |
142 | | } |
143 | | |
144 | | /// Get the value of the timestamp as a Unix timestamp, as used in version 7 UUIDs. |
145 | 0 | pub const fn to_unix(&self) -> (u64, u32) { |
146 | 0 | nanos_to_parts(self.unix_nanos) |
147 | 0 | } |
148 | | |
149 | 0 | const fn unix_to_gregorian_ticks(seconds: u64, nanos: u32) -> u64 { |
150 | | UUID_TICKS_BETWEEN_EPOCHS |
151 | 0 | .wrapping_add(seconds.wrapping_mul(10_000_000)) |
152 | 0 | .wrapping_add(nanos as u64 / 100) |
153 | 0 | } |
154 | | |
155 | 0 | const fn gregorian_to_unix(ticks: u64) -> (u64, u32) { |
156 | 0 | ( |
157 | 0 | ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) / 10_000_000, |
158 | 0 | (ticks.wrapping_sub(UUID_TICKS_BETWEEN_EPOCHS) % 10_000_000) as u32 * 100, |
159 | 0 | ) |
160 | 0 | } |
161 | | } |
162 | | |
163 | | #[doc(hidden)] |
164 | | impl Timestamp { |
165 | | #[deprecated( |
166 | | since = "1.10.0", |
167 | | note = "use `Timestamp::from_gregorian_time(ticks, counter)`" |
168 | | )] |
169 | 0 | pub const fn from_rfc4122(ticks: u64, counter: u16) -> Self { |
170 | 0 | Timestamp::from_gregorian_time(ticks, counter) |
171 | 0 | } |
172 | | |
173 | | #[deprecated(since = "1.10.0", note = "use `Timestamp::to_gregorian()`")] |
174 | 0 | pub const fn to_rfc4122(&self) -> (u64, u16) { |
175 | 0 | self.to_gregorian() |
176 | 0 | } |
177 | | |
178 | | #[deprecated( |
179 | | since = "1.2.0", |
180 | | note = "`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`" |
181 | | )] |
182 | 0 | pub const fn to_unix_nanos(&self) -> u32 { |
183 | 0 | panic!("`Timestamp::to_unix_nanos()` is deprecated and will be removed: use `Timestamp::to_unix()`") |
184 | | } |
185 | | |
186 | | #[deprecated( |
187 | | since = "1.23.0", |
188 | | note = "use `Timestamp::from_gregorian_time(ticks, counter)`" |
189 | | )] |
190 | 0 | pub const fn from_gregorian(ticks: u64, counter: u16) -> Self { |
191 | 0 | Timestamp::from_gregorian_time(ticks, counter) |
192 | 0 | } |
193 | | } |
194 | | |
195 | | #[cfg(feature = "std")] |
196 | | impl TryFrom<std::time::SystemTime> for Timestamp { |
197 | | type Error = crate::Error; |
198 | | |
199 | | /// Perform the conversion. |
200 | | /// |
201 | | /// This method will fail if the system time is earlier than the Unix Epoch. |
202 | | /// On some platforms it may panic instead. |
203 | 0 | fn try_from(st: std::time::SystemTime) -> Result<Self, Self::Error> { |
204 | 0 | let dur = st.duration_since(std::time::UNIX_EPOCH).map_err(|_| { |
205 | 0 | crate::Error(crate::error::ErrorKind::InvalidSystemTime( |
206 | 0 | "unable to convert the system tie into a Unix timestamp", |
207 | 0 | )) |
208 | 0 | })?; |
209 | | |
210 | 0 | Ok(Self::from_unix_time( |
211 | 0 | dur.as_secs(), |
212 | 0 | dur.subsec_nanos(), |
213 | 0 | 0, |
214 | 0 | 0, |
215 | 0 | )) |
216 | 0 | } |
217 | | } |
218 | | |
219 | | #[cfg(feature = "std")] |
220 | | impl From<Timestamp> for std::time::SystemTime { |
221 | | /// Perform the conversion. |
222 | | /// |
223 | | /// If the conversion would fail, an undefined `SystemTime` will be returned instead. |
224 | | /// This can happen if the `Timestamp` would overflow the max value allowed by `SystemTime` on the target platform. |
225 | | /// Use `TryFrom` to catch conversion failures and handle them explicitly. |
226 | 0 | fn from(ts: Timestamp) -> Self { |
227 | 0 | let (seconds, subsec_nanos) = ts.to_unix(); |
228 | | |
229 | | // NOTE: The actual value on overflow is undefined and may change |
230 | | // See: https://github.com/rust-lang/rust/issues/151199 |
231 | 0 | std::time::Duration::from_secs(seconds) |
232 | 0 | .checked_add(std::time::Duration::from_nanos(subsec_nanos as u64)) |
233 | 0 | .and_then(|dur| Self::UNIX_EPOCH.checked_add(dur)) |
234 | 0 | .unwrap_or(std::time::SystemTime::UNIX_EPOCH) |
235 | 0 | } |
236 | | } |
237 | | |
238 | 0 | pub(crate) const fn encode_gregorian_timestamp( |
239 | 0 | ticks: u64, |
240 | 0 | counter: u16, |
241 | 0 | node_id: &[u8; 6], |
242 | 0 | ) -> Uuid { |
243 | 0 | let time_low = (ticks & 0xFFFF_FFFF) as u32; |
244 | 0 | let time_mid = ((ticks >> 32) & 0xFFFF) as u16; |
245 | 0 | let time_high_and_version = (((ticks >> 48) & 0x0FFF) as u16) | (1 << 12); |
246 | | |
247 | 0 | let mut d4 = [0; 8]; |
248 | | |
249 | 0 | d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; |
250 | 0 | d4[1] = (counter & 0xFF) as u8; |
251 | 0 | d4[2] = node_id[0]; |
252 | 0 | d4[3] = node_id[1]; |
253 | 0 | d4[4] = node_id[2]; |
254 | 0 | d4[5] = node_id[3]; |
255 | 0 | d4[6] = node_id[4]; |
256 | 0 | d4[7] = node_id[5]; |
257 | | |
258 | 0 | Uuid::from_fields(time_low, time_mid, time_high_and_version, &d4) |
259 | 0 | } |
260 | | |
261 | 0 | pub(crate) const fn decode_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) { |
262 | 0 | let bytes = uuid.as_bytes(); |
263 | | |
264 | 0 | let ticks: u64 = ((bytes[6] & 0x0F) as u64) << 56 |
265 | 0 | | (bytes[7] as u64) << 48 |
266 | 0 | | (bytes[4] as u64) << 40 |
267 | 0 | | (bytes[5] as u64) << 32 |
268 | 0 | | (bytes[0] as u64) << 24 |
269 | 0 | | (bytes[1] as u64) << 16 |
270 | 0 | | (bytes[2] as u64) << 8 |
271 | 0 | | (bytes[3] as u64); |
272 | | |
273 | 0 | let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); |
274 | | |
275 | 0 | (ticks, counter) |
276 | 0 | } |
277 | | |
278 | 0 | pub(crate) const fn encode_sorted_gregorian_timestamp( |
279 | 0 | ticks: u64, |
280 | 0 | counter: u16, |
281 | 0 | node_id: &[u8; 6], |
282 | 0 | ) -> Uuid { |
283 | 0 | let time_high = ((ticks >> 28) & 0xFFFF_FFFF) as u32; |
284 | 0 | let time_mid = ((ticks >> 12) & 0xFFFF) as u16; |
285 | 0 | let time_low_and_version = ((ticks & 0x0FFF) as u16) | (0x6 << 12); |
286 | | |
287 | 0 | let mut d4 = [0; 8]; |
288 | | |
289 | 0 | d4[0] = (((counter & 0x3F00) >> 8) as u8) | 0x80; |
290 | 0 | d4[1] = (counter & 0xFF) as u8; |
291 | 0 | d4[2] = node_id[0]; |
292 | 0 | d4[3] = node_id[1]; |
293 | 0 | d4[4] = node_id[2]; |
294 | 0 | d4[5] = node_id[3]; |
295 | 0 | d4[6] = node_id[4]; |
296 | 0 | d4[7] = node_id[5]; |
297 | | |
298 | 0 | Uuid::from_fields(time_high, time_mid, time_low_and_version, &d4) |
299 | 0 | } |
300 | | |
301 | 0 | pub(crate) const fn decode_sorted_gregorian_timestamp(uuid: &Uuid) -> (u64, u16) { |
302 | 0 | let bytes = uuid.as_bytes(); |
303 | | |
304 | 0 | let ticks: u64 = ((bytes[0]) as u64) << 52 |
305 | 0 | | (bytes[1] as u64) << 44 |
306 | 0 | | (bytes[2] as u64) << 36 |
307 | 0 | | (bytes[3] as u64) << 28 |
308 | 0 | | (bytes[4] as u64) << 20 |
309 | 0 | | (bytes[5] as u64) << 12 |
310 | 0 | | ((bytes[6] & 0xF) as u64) << 8 |
311 | 0 | | (bytes[7] as u64); |
312 | | |
313 | 0 | let counter: u16 = ((bytes[8] & 0x3F) as u16) << 8 | (bytes[9] as u16); |
314 | | |
315 | 0 | (ticks, counter) |
316 | 0 | } |
317 | | |
318 | 0 | pub(crate) const fn encode_unix_timestamp_millis( |
319 | 0 | millis: u64, |
320 | 0 | counter_random_bytes: &[u8; 10], |
321 | 0 | ) -> Uuid { |
322 | 0 | let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32; |
323 | 0 | let millis_low = (millis & 0xFFFF) as u16; |
324 | | |
325 | 0 | let counter_random_version = (counter_random_bytes[1] as u16 |
326 | 0 | | ((counter_random_bytes[0] as u16) << 8) & 0x0FFF) |
327 | 0 | | (0x7 << 12); |
328 | | |
329 | 0 | let mut d4 = [0; 8]; |
330 | | |
331 | 0 | d4[0] = (counter_random_bytes[2] & 0x3F) | 0x80; |
332 | 0 | d4[1] = counter_random_bytes[3]; |
333 | 0 | d4[2] = counter_random_bytes[4]; |
334 | 0 | d4[3] = counter_random_bytes[5]; |
335 | 0 | d4[4] = counter_random_bytes[6]; |
336 | 0 | d4[5] = counter_random_bytes[7]; |
337 | 0 | d4[6] = counter_random_bytes[8]; |
338 | 0 | d4[7] = counter_random_bytes[9]; |
339 | | |
340 | 0 | Uuid::from_fields(millis_high, millis_low, counter_random_version, &d4) |
341 | 0 | } |
342 | | |
343 | 0 | pub(crate) const fn decode_unix_timestamp_millis(uuid: &Uuid) -> u64 { |
344 | 0 | let bytes = uuid.as_bytes(); |
345 | | |
346 | 0 | let millis: u64 = (bytes[0] as u64) << 40 |
347 | 0 | | (bytes[1] as u64) << 32 |
348 | 0 | | (bytes[2] as u64) << 24 |
349 | 0 | | (bytes[3] as u64) << 16 |
350 | 0 | | (bytes[4] as u64) << 8 |
351 | 0 | | (bytes[5] as u64); |
352 | | |
353 | 0 | millis |
354 | 0 | } |
355 | | |
356 | | #[cfg(all( |
357 | | feature = "std", |
358 | | feature = "js", |
359 | | all(target_family = "wasm", any(target_os = "unknown", target_os = "none")) |
360 | | ))] |
361 | | fn now() -> u128 { |
362 | | use wasm_bindgen::prelude::*; |
363 | | |
364 | | #[wasm_bindgen] |
365 | | extern "C" { |
366 | | // NOTE: This signature works around https://bugzilla.mozilla.org/show_bug.cgi?id=1787770 |
367 | | #[wasm_bindgen(js_namespace = Date, catch)] |
368 | | fn now() -> Result<f64, JsValue>; |
369 | | } |
370 | | |
371 | | let now = now().unwrap_throw(); |
372 | | |
373 | | (now * 1_000_000.0) as u128 |
374 | | } |
375 | | |
376 | | #[cfg(all( |
377 | | feature = "std", |
378 | | not(miri), |
379 | | any( |
380 | | not(feature = "js"), |
381 | | not(all(target_family = "wasm", any(target_os = "unknown", target_os = "none"))) |
382 | | ) |
383 | | ))] |
384 | 0 | fn now() -> u128 { |
385 | 0 | let dur = std::time::SystemTime::UNIX_EPOCH.elapsed().expect( |
386 | 0 | "Getting elapsed time since UNIX_EPOCH. If this fails, we've somehow violated causality", |
387 | | ); |
388 | | |
389 | 0 | dur.as_nanos() |
390 | 0 | } |
391 | | |
392 | | #[cfg(all(feature = "std", miri))] |
393 | | fn now() -> u128 { |
394 | | use std::{sync::Mutex, time::Duration}; |
395 | | |
396 | | static TS: Mutex<u128> = Mutex::new(0); |
397 | | |
398 | | let mut ts = TS.lock().unwrap(); |
399 | | *ts += 1; |
400 | | *ts |
401 | | } |
402 | | |
403 | | /** |
404 | | Convert a 128bit nanosecond value into a pair of 64bit seconds, and 32bit subsecond nanos. |
405 | | |
406 | | The 32bit subsecond nano value is normalized to be less than `1_000_000_000` wherever the 64bit |
407 | | seconds can hold the carry. Above `(u64::MAX, 999_999_999)` the remainder is left in the subsecond |
408 | | nanos instead, so every value produced by `parts_to_nanos` converts back exactly. |
409 | | |
410 | | Only values beyond `(u64::MAX, u32::MAX)` saturate to it. A `Timestamp` never holds one. |
411 | | */ |
412 | 0 | const fn nanos_to_parts(nanos: u128) -> (u64, u32) { |
413 | | const MAX_NANOS: u128 = parts_to_nanos(u64::MAX, u32::MAX); |
414 | | |
415 | 0 | if nanos > MAX_NANOS { |
416 | 0 | return (u64::MAX, u32::MAX); |
417 | 0 | } |
418 | | |
419 | 0 | let secs = nanos / 1_000_000_000; |
420 | | |
421 | 0 | if secs > u64::MAX as u128 { |
422 | | // Seconds can't take the carry, so leave the rest in the subsecond nanos (at most `u32::MAX`) |
423 | 0 | (u64::MAX, (nanos - u64::MAX as u128 * 1_000_000_000) as u32) |
424 | | } else { |
425 | 0 | (secs as u64, (nanos % 1_000_000_000) as u32) |
426 | | } |
427 | 0 | } |
428 | | |
429 | | /** |
430 | | Convert a potentially un-normalized part of 64bit seconds and 32bit subsecond nanos to a single |
431 | | 128bit number of nanoseconds. |
432 | | |
433 | | This function will never overflow. `subsec_nanos` isn't required to be less than `1_000_000_000`. |
434 | | */ |
435 | 0 | const fn parts_to_nanos(secs: u64, subsec_nanos: u32) -> u128 { |
436 | 0 | (secs as u128 * 1_000_000_000) + subsec_nanos as u128 |
437 | 0 | } |
438 | | |
439 | | /// A counter that can be used by versions 1 and 6 UUIDs to support |
440 | | /// the uniqueness of timestamps. |
441 | | /// |
442 | | /// # References |
443 | | /// |
444 | | /// * [UUID Version 1 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.1) |
445 | | /// * [UUID Version 6 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.6) |
446 | | /// * [UUID Generator States in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-6.3) |
447 | | pub trait ClockSequence { |
448 | | /// The type of sequence returned by this counter. |
449 | | type Output; |
450 | | |
451 | | /// Get the next value in the sequence to feed into a timestamp. |
452 | | /// |
453 | | /// This method will be called each time a [`Timestamp`] is constructed. |
454 | | /// |
455 | | /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset. |
456 | | /// |
457 | | /// Note that a caller is not required to pass normalized values, so `subsec_nanos` may |
458 | | /// be greater than the actual number of nanoseconds in a second (1,000,000,000). |
459 | | /// Implementations are also not required to return normalized values, but values |
460 | | /// outside the range supported by a given UUID variant may weaken uniqueness or monotonicity. |
461 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output; |
462 | | |
463 | | /// Get the next value in the sequence, potentially also adjusting the timestamp. |
464 | | /// |
465 | | /// This method should be preferred over `generate_sequence`. |
466 | | /// |
467 | | /// Any bits beyond [`ClockSequence::usable_bits`] in the output must be unset. |
468 | | /// |
469 | | /// Note that a caller is not required to pass normalized values, so `subsec_nanos` may |
470 | | /// be greater than the actual number of nanoseconds in a second (1,000,000,000). |
471 | | /// Implementations are also not required to return normalized values, but values |
472 | | /// outside the range supported by a given UUID variant may weaken uniqueness or monotonicity. |
473 | | /// |
474 | | /// The returned timestamp isn't required to be normalized either. Any `(seconds, subsec_nanos)` |
475 | | /// value is preserved exactly by [`Timestamp`], and [`Timestamp::to_unix`] normalizes it where possible. |
476 | 0 | fn generate_timestamp_sequence( |
477 | 0 | &self, |
478 | 0 | seconds: u64, |
479 | 0 | subsec_nanos: u32, |
480 | 0 | ) -> (Self::Output, u64, u32) { |
481 | 0 | ( |
482 | 0 | self.generate_sequence(seconds, subsec_nanos), |
483 | 0 | seconds, |
484 | 0 | subsec_nanos, |
485 | 0 | ) |
486 | 0 | } |
487 | | |
488 | | /// The number of usable bits from the least significant bit in the result of [`ClockSequence::generate_sequence`] |
489 | | /// or [`ClockSequence::generate_timestamp_sequence`]. |
490 | | /// |
491 | | /// The number of usable bits must not exceed 128. |
492 | | /// |
493 | | /// The number of usable bits is not expected to change between calls. An implementation of `ClockSequence` should |
494 | | /// always return the same value from this method. |
495 | 0 | fn usable_bits(&self) -> usize |
496 | 0 | where |
497 | 0 | Self::Output: Sized, |
498 | | { |
499 | 0 | cmp::min(128, core::mem::size_of::<Self::Output>() * 8) |
500 | 0 | } |
501 | | } |
502 | | |
503 | | impl<T: ClockSequence + ?Sized> ClockSequence for &T { |
504 | | type Output = T::Output; |
505 | | |
506 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
507 | 0 | (**self).generate_sequence(seconds, subsec_nanos) |
508 | 0 | } |
509 | | |
510 | 0 | fn generate_timestamp_sequence( |
511 | 0 | &self, |
512 | 0 | seconds: u64, |
513 | 0 | subsec_nanos: u32, |
514 | 0 | ) -> (Self::Output, u64, u32) { |
515 | 0 | (**self).generate_timestamp_sequence(seconds, subsec_nanos) |
516 | 0 | } |
517 | | |
518 | 0 | fn usable_bits(&self) -> usize |
519 | 0 | where |
520 | 0 | Self::Output: Sized, |
521 | | { |
522 | 0 | (**self).usable_bits() |
523 | 0 | } |
524 | | } |
525 | | |
526 | | /// Default implementations for the [`ClockSequence`] trait. |
527 | | pub mod context { |
528 | | use super::ClockSequence; |
529 | | |
530 | | #[cfg(any(feature = "v1", feature = "v6"))] |
531 | | mod v1_support { |
532 | | use super::*; |
533 | | |
534 | | #[cfg(all(feature = "std", feature = "rng"))] |
535 | | use crate::std::sync::LazyLock; |
536 | | |
537 | | use atomic::{Atomic, Ordering}; |
538 | | |
539 | | #[cfg(all(feature = "std", feature = "rng"))] |
540 | | static CONTEXT: LazyLock<ContextV1> = LazyLock::new(ContextV1::new_random); |
541 | | |
542 | | #[cfg(all(feature = "std", feature = "rng"))] |
543 | | pub(crate) fn shared_context_v1() -> &'static ContextV1 { |
544 | | &CONTEXT |
545 | | } |
546 | | |
547 | | /// An internally synchronized, wrapping counter that produces 14-bit values for version 1 and version 6 UUIDs. |
548 | | /// |
549 | | /// This type is: |
550 | | /// |
551 | | /// - **Non-reseeding:** The counter is not reseeded on each time interval (100ns). |
552 | | /// - **Non-adjusting:** The timestamp is not incremented when the counter wraps within a time interval (100ns). |
553 | | /// - **Thread-safe:** The underlying counter is atomic, so can be shared across threads. |
554 | | /// |
555 | | /// This type should be used when constructing versions 1 and 6 UUIDs. |
556 | | /// |
557 | | /// This type should not be used when constructing version 7 UUIDs. When used to |
558 | | /// construct a version 7 UUID, the 14-bit counter will be padded with random data. |
559 | | /// Counter overflows are more likely with a 14-bit counter than they are with a |
560 | | /// 42-bit counter when working at millisecond precision. This type doesn't attempt |
561 | | /// to adjust the timestamp on overflow. |
562 | | #[derive(Debug)] |
563 | | pub struct ContextV1 { |
564 | | count: Atomic<u16>, |
565 | | } |
566 | | |
567 | | impl ContextV1 { |
568 | | /// Construct a new context that's initialized with the given value. |
569 | | /// |
570 | | /// The starting value should be a random number, so that UUIDs from |
571 | | /// different systems with the same timestamps are less likely to collide. |
572 | | /// When the `rng` feature is enabled, prefer the [`ContextV1::new_random`] method. |
573 | | pub const fn new(count: u16) -> Self { |
574 | | Self { |
575 | | count: Atomic::<u16>::new(count), |
576 | | } |
577 | | } |
578 | | |
579 | | /// Construct a new context that's initialized with a random value. |
580 | | #[cfg(feature = "rng")] |
581 | | pub fn new_random() -> Self { |
582 | | Self { |
583 | | count: Atomic::<u16>::new(crate::rng::u16()), |
584 | | } |
585 | | } |
586 | | } |
587 | | |
588 | | impl ClockSequence for ContextV1 { |
589 | | type Output = u16; |
590 | | |
591 | | fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output { |
592 | | // RFC 9562 reserves 2 bits of the clock sequence so the actual |
593 | | // maximum value is smaller than `u16::MAX`. Since we unconditionally |
594 | | // increment the clock sequence we want to wrap once it becomes larger |
595 | | // than what we can represent in a "u14". Otherwise there'd be patches |
596 | | // where the clock sequence doesn't change regardless of the timestamp |
597 | | self.count.fetch_add(1, Ordering::AcqRel) & (u16::MAX >> 2) |
598 | | } |
599 | | |
600 | | fn usable_bits(&self) -> usize { |
601 | | 14 |
602 | | } |
603 | | } |
604 | | |
605 | | #[deprecated(since = "1.23.0", note = "renamed to `ContextV1`")] |
606 | | #[doc(hidden)] |
607 | | pub type Context = ContextV1; |
608 | | |
609 | | #[cfg(test)] |
610 | | mod tests { |
611 | | use crate::Timestamp; |
612 | | |
613 | | use super::*; |
614 | | |
615 | | #[test] |
616 | | fn context() { |
617 | | let seconds = 1_496_854_535; |
618 | | let subsec_nanos = 812_946_000; |
619 | | |
620 | | let context = ContextV1::new(u16::MAX >> 2); |
621 | | |
622 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
623 | | assert_eq!(16383, ts.counter); |
624 | | assert_eq!(14, ts.usable_counter_bits); |
625 | | |
626 | | let seconds = 1_496_854_536; |
627 | | |
628 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
629 | | assert_eq!(0, ts.counter); |
630 | | |
631 | | let seconds = 1_496_854_535; |
632 | | |
633 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
634 | | assert_eq!(1, ts.counter); |
635 | | } |
636 | | |
637 | | #[test] |
638 | | fn context_overflow() { |
639 | | let seconds = u64::MAX; |
640 | | let subsec_nanos = u32::MAX; |
641 | | |
642 | | let context = ContextV1::new(u16::MAX); |
643 | | |
644 | | // Ensure we don't panic |
645 | | Timestamp::from_unix(&context, seconds, subsec_nanos); |
646 | | } |
647 | | } |
648 | | } |
649 | | |
650 | | #[cfg(any(feature = "v1", feature = "v6"))] |
651 | | pub use v1_support::*; |
652 | | |
653 | | #[cfg(feature = "std")] |
654 | | mod std_support { |
655 | | use super::*; |
656 | | |
657 | | use core::panic::{AssertUnwindSafe, RefUnwindSafe}; |
658 | | use std::{sync::Mutex, thread::LocalKey}; |
659 | | |
660 | | /// A wrapper for a context that uses thread-local storage. |
661 | | pub struct ThreadLocalContext<C: 'static>(&'static LocalKey<C>); |
662 | | |
663 | | impl<C> std::fmt::Debug for ThreadLocalContext<C> { |
664 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
665 | 0 | f.debug_struct("ThreadLocalContext").finish_non_exhaustive() |
666 | 0 | } |
667 | | } |
668 | | |
669 | | impl<C: 'static> ThreadLocalContext<C> { |
670 | | /// Wrap a thread-local container with a context. |
671 | 0 | pub const fn new(local_key: &'static LocalKey<C>) -> Self { |
672 | 0 | ThreadLocalContext(local_key) |
673 | 0 | } |
674 | | } |
675 | | |
676 | | impl<C: ClockSequence + 'static> ClockSequence for ThreadLocalContext<C> { |
677 | | type Output = C::Output; |
678 | | |
679 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
680 | 0 | self.0 |
681 | 0 | .with(|ctxt| ctxt.generate_sequence(seconds, subsec_nanos)) |
682 | 0 | } |
683 | | |
684 | 0 | fn generate_timestamp_sequence( |
685 | 0 | &self, |
686 | 0 | seconds: u64, |
687 | 0 | subsec_nanos: u32, |
688 | 0 | ) -> (Self::Output, u64, u32) { |
689 | 0 | self.0 |
690 | 0 | .with(|ctxt| ctxt.generate_timestamp_sequence(seconds, subsec_nanos)) |
691 | 0 | } |
692 | | |
693 | 0 | fn usable_bits(&self) -> usize { |
694 | 0 | self.0.with(|ctxt| ctxt.usable_bits()) |
695 | 0 | } |
696 | | } |
697 | | |
698 | | impl<C: ClockSequence> ClockSequence for AssertUnwindSafe<C> { |
699 | | type Output = C::Output; |
700 | | |
701 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
702 | 0 | self.0.generate_sequence(seconds, subsec_nanos) |
703 | 0 | } |
704 | | |
705 | 0 | fn generate_timestamp_sequence( |
706 | 0 | &self, |
707 | 0 | seconds: u64, |
708 | 0 | subsec_nanos: u32, |
709 | 0 | ) -> (Self::Output, u64, u32) { |
710 | 0 | self.0.generate_timestamp_sequence(seconds, subsec_nanos) |
711 | 0 | } |
712 | | |
713 | 0 | fn usable_bits(&self) -> usize |
714 | 0 | where |
715 | 0 | Self::Output: Sized, |
716 | | { |
717 | 0 | self.0.usable_bits() |
718 | 0 | } |
719 | | } |
720 | | |
721 | | impl<C: ClockSequence + RefUnwindSafe> ClockSequence for Mutex<C> { |
722 | | type Output = C::Output; |
723 | | |
724 | 0 | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
725 | 0 | self.lock() |
726 | 0 | .unwrap_or_else(|err| err.into_inner()) |
727 | 0 | .generate_sequence(seconds, subsec_nanos) |
728 | 0 | } |
729 | | |
730 | 0 | fn generate_timestamp_sequence( |
731 | 0 | &self, |
732 | 0 | seconds: u64, |
733 | 0 | subsec_nanos: u32, |
734 | 0 | ) -> (Self::Output, u64, u32) { |
735 | 0 | self.lock() |
736 | 0 | .unwrap_or_else(|err| err.into_inner()) |
737 | 0 | .generate_timestamp_sequence(seconds, subsec_nanos) |
738 | 0 | } |
739 | | |
740 | 0 | fn usable_bits(&self) -> usize |
741 | 0 | where |
742 | 0 | Self::Output: Sized, |
743 | | { |
744 | 0 | self.lock() |
745 | 0 | .unwrap_or_else(|err| err.into_inner()) |
746 | 0 | .usable_bits() |
747 | 0 | } |
748 | | } |
749 | | } |
750 | | |
751 | | #[cfg(feature = "std")] |
752 | | pub use std_support::*; |
753 | | |
754 | | #[cfg(feature = "v7")] |
755 | | mod v7_support { |
756 | | use super::*; |
757 | | |
758 | | use crate::timestamp::parts_to_nanos; |
759 | | use core::{cell::Cell, cmp, panic::RefUnwindSafe}; |
760 | | |
761 | | #[cfg(feature = "std")] |
762 | | static CONTEXT_V7: SharedContextV7 = |
763 | | SharedContextV7(std::sync::Mutex::new(ContextV7::new())); |
764 | | |
765 | | #[cfg(feature = "std")] |
766 | | pub(crate) fn shared_context_v7() -> &'static SharedContextV7 { |
767 | | &CONTEXT_V7 |
768 | | } |
769 | | |
770 | | const USABLE_BITS: usize = 42; |
771 | | |
772 | | // Leave the most significant bit unset |
773 | | // This guarantees the counter has at least 2,199,023,255,552 |
774 | | // values before it will overflow, which is exceptionally unlikely |
775 | | // even in the worst case |
776 | | const RESEED_MASK: u64 = u64::MAX >> 23; |
777 | | const MAX_COUNTER: u64 = u64::MAX >> 22; |
778 | | |
779 | | /// An unsynchronized, reseeding counter that produces 42-bit values for version 7 UUIDs. |
780 | | /// |
781 | | /// This type is: |
782 | | /// |
783 | | /// - **Reseeding:** The counter is reseeded on each time interval (1ms) with a random 41-bit value. |
784 | | /// The 42nd bit is left unset so the counter can safely increment over the millisecond. |
785 | | /// - **Adjusting:** The timestamp is incremented when the counter wraps within a time interval (1ms). |
786 | | /// All subsequent timestamps in that same interval will also be incremented until it changes and |
787 | | /// the counter is reseeded. |
788 | | /// - **Non-thread-safe:** The underlying counter uses unsynchronized cells, so needs to be wrapped in |
789 | | /// a mutex to share. |
790 | | /// |
791 | | /// The counter can use additional sub-millisecond precision from the timestamp to better |
792 | | /// synchronize UUID sorting in distributed systems. In these cases, the additional precision |
793 | | /// is masked into the left-most 12 bits of the counter. The counter is still reseeded on |
794 | | /// each new millisecond, and incremented within the millisecond. This behavior may change |
795 | | /// in the future. The only guarantee is monotonicity. |
796 | | /// |
797 | | /// This type can be used when constructing version 7 UUIDs. When used to construct a |
798 | | /// version 7 UUID, the 42-bit counter will be padded with random data. This type can |
799 | | /// be used to maintain ordering of UUIDs within the same millisecond. |
800 | | /// |
801 | | /// This type should not be used when constructing version 1 or version 6 UUIDs. |
802 | | /// When used to construct a version 1 or version 6 UUID, only the 14 least significant |
803 | | /// bits of the counter will be used. |
804 | | #[derive(Debug, Default)] |
805 | | pub struct ContextV7 { |
806 | | timestamp: Cell<ReseedingTimestamp>, |
807 | | counter: Cell<Counter>, |
808 | | adjust: Adjust, |
809 | | precision: Precision, |
810 | | } |
811 | | |
812 | | impl RefUnwindSafe for ContextV7 {} |
813 | | |
814 | | impl ContextV7 { |
815 | | /// Construct a new context that will reseed its counter on the first |
816 | | /// non-zero timestamp it receives. |
817 | | pub const fn new() -> Self { |
818 | | ContextV7 { |
819 | | timestamp: Cell::new(ReseedingTimestamp { |
820 | | last_seed: 0, |
821 | | unix_nanos: 0, |
822 | | }), |
823 | | counter: Cell::new(Counter { value: 0 }), |
824 | | adjust: Adjust { by_ns: 0 }, |
825 | | precision: Precision { |
826 | | bits: 0, |
827 | | mask: 0, |
828 | | factor: 0, |
829 | | shift: 0, |
830 | | }, |
831 | | } |
832 | | } |
833 | | |
834 | | /// Specify an amount to shift timestamps by to obfuscate their actual generation time. |
835 | | pub fn with_adjust_by_millis(mut self, millis: u32) -> Self { |
836 | | self.adjust = Adjust::by_millis(millis); |
837 | | self |
838 | | } |
839 | | |
840 | | /// Use the leftmost 12 bits of the counter for additional timestamp precision. |
841 | | /// |
842 | | /// This method can provide better sorting for distributed applications that generate frequent UUIDs |
843 | | /// by trading a small amount of entropy for better counter synchronization. Note that the counter |
844 | | /// will still be reseeded on millisecond boundaries, even though some of its storage will be |
845 | | /// dedicated to the timestamp. |
846 | | pub fn with_additional_precision(self) -> Self { |
847 | | self.with_additional_precision_bits(12) |
848 | | } |
849 | | |
850 | | /// Use the leftmost `bits` bits of the counter for additional timestamp precision. |
851 | | /// |
852 | | /// This is a more general form of [`ContextV7::with_additional_precision`] for platforms |
853 | | /// whose clocks don't have the resolution to make use of the full 12 bits. A platform with |
854 | | /// microsecond precision can set 10 bits here, leaving the remaining 2 bits of the `rand_a` |
855 | | /// field free for random data. |
856 | | /// |
857 | | /// `bits` is capped at 12, matching the size of the `rand_a` field in a version 7 UUID. |
858 | | /// Passing 0 disables additional precision, the same as never calling this method. |
859 | | pub fn with_additional_precision_bits(mut self, bits: usize) -> Self { |
860 | | self.precision = Precision::new(cmp::min(bits, 12)); |
861 | | self |
862 | | } |
863 | | } |
864 | | |
865 | | impl ClockSequence for ContextV7 { |
866 | | type Output = u64; |
867 | | |
868 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
869 | | self.generate_timestamp_sequence(seconds, subsec_nanos).0 |
870 | | } |
871 | | |
872 | | fn generate_timestamp_sequence( |
873 | | &self, |
874 | | seconds: u64, |
875 | | subsec_nanos: u32, |
876 | | ) -> (Self::Output, u64, u32) { |
877 | | let unix_nanos = self.adjust.apply(parts_to_nanos(seconds, subsec_nanos)); |
878 | | |
879 | | let mut counter; |
880 | | let (mut timestamp, should_reseed) = self.timestamp.get().advance(unix_nanos); |
881 | | |
882 | | if should_reseed { |
883 | | // If the observed system time has shifted forwards then regenerate the counter |
884 | | counter = Counter::reseed(&self.precision, ×tamp); |
885 | | } else { |
886 | | // If the observed system time has not shifted forwards then increment the counter |
887 | | |
888 | | // If the incoming timestamp is earlier than the last observed one then |
889 | | // use it instead. This may happen if the system clock jitters, or if the counter |
890 | | // has wrapped and the timestamp is artificially incremented |
891 | | |
892 | | counter = self.counter.get().increment(&self.precision, ×tamp); |
893 | | |
894 | | // Unlikely: If the counter has overflowed its 42-bit storage then wrap it |
895 | | // and increment the timestamp. Until the observed system time shifts past |
896 | | // this incremented value, all timestamps will use it to maintain monotonicity |
897 | | if counter.has_overflowed() { |
898 | | // Increment the timestamp by 1 milli and reseed the counter |
899 | | timestamp = timestamp.increment(); |
900 | | counter = Counter::reseed(&self.precision, ×tamp); |
901 | | } |
902 | | }; |
903 | | |
904 | | self.timestamp.set(timestamp); |
905 | | self.counter.set(counter); |
906 | | |
907 | | let (seconds, subsec_nanos) = timestamp.to_parts(); |
908 | | |
909 | | (counter.value, seconds, subsec_nanos) |
910 | | } |
911 | | |
912 | | fn usable_bits(&self) -> usize { |
913 | | USABLE_BITS |
914 | | } |
915 | | } |
916 | | |
917 | | /// A timestamp that keeps track of whether a reseed is necessary. |
918 | | #[derive(Debug, Default, Clone, Copy)] |
919 | | struct ReseedingTimestamp { |
920 | | last_seed: u64, |
921 | | unix_nanos: u128, |
922 | | } |
923 | | |
924 | | impl ReseedingTimestamp { |
925 | | #[inline] |
926 | | fn saturating_from_unix_nanos(unix_nanos: u128) -> Self { |
927 | | const MAX_UNIX_NANOS: u128 = (u64::MAX as u128) * 1_000_000 + 999_999; |
928 | | |
929 | | let unix_nanos = cmp::min(unix_nanos, MAX_UNIX_NANOS); |
930 | | |
931 | | // Reseed when the millisecond advances |
932 | | let last_seed = (unix_nanos / 1_000_000) as u64; |
933 | | |
934 | | ReseedingTimestamp { |
935 | | last_seed, |
936 | | unix_nanos, |
937 | | } |
938 | | } |
939 | | |
940 | | #[inline] |
941 | | fn to_parts(&self) -> (u64, u32) { |
942 | | // Guaranteed never to overflow `(u64::MAX, 999_999_999)` |
943 | | ( |
944 | | (self.unix_nanos / 1_000_000_000) as u64, |
945 | | (self.unix_nanos % 1_000_000_000) as u32, |
946 | | ) |
947 | | } |
948 | | |
949 | | /// Advance the timestamp to a new value, returning whether a reseed is necessary. |
950 | | #[inline] |
951 | | fn advance(&self, unix_nanos: u128) -> (Self, bool) { |
952 | | let incoming = ReseedingTimestamp::saturating_from_unix_nanos(unix_nanos); |
953 | | |
954 | | ( |
955 | | ReseedingTimestamp::saturating_from_unix_nanos(cmp::max( |
956 | | self.unix_nanos, |
957 | | unix_nanos, |
958 | | )), |
959 | | incoming.last_seed > self.last_seed, |
960 | | ) |
961 | | } |
962 | | |
963 | | /// Advance the timestamp by a millisecond. |
964 | | #[inline] |
965 | | fn increment(&self) -> Self { |
966 | | ReseedingTimestamp::saturating_from_unix_nanos( |
967 | | Adjust::by_millis(1).apply(self.unix_nanos), |
968 | | ) |
969 | | } |
970 | | } |
971 | | |
972 | | /// A counter that initializes to a safe random seed and tracks overflow. |
973 | | #[derive(Debug, Default, Clone, Copy)] |
974 | | struct Counter { |
975 | | value: u64, |
976 | | } |
977 | | |
978 | | impl Counter { |
979 | | #[inline] |
980 | | fn reseed(precision: &Precision, timestamp: &ReseedingTimestamp) -> Self { |
981 | | Counter { |
982 | | value: precision.apply(crate::rng::u64() & RESEED_MASK, timestamp), |
983 | | } |
984 | | } |
985 | | |
986 | | /// Advance the counter. |
987 | | /// |
988 | | /// This method is guaranteed to return a counter with a higher value than the current one. |
989 | | /// Additional precision from the timestamp will be used, unless it cause the counter |
990 | | /// to shift backwards. |
991 | | #[inline] |
992 | | fn increment(&self, precision: &Precision, timestamp: &ReseedingTimestamp) -> Self { |
993 | | // We unconditionally increment the counter even though the precision |
994 | | // may have set higher bits anyway. This could technically be avoided, |
995 | | // but the higher bits are a coarse approximation so we just avoid the |
996 | | // `if` branch and increment it either way |
997 | | |
998 | | // Guaranteed to never overflow u64 |
999 | | let next = self.value + 1; |
1000 | | |
1001 | | Counter { |
1002 | | value: cmp::max(next, precision.apply(next, timestamp)), |
1003 | | } |
1004 | | } |
1005 | | |
1006 | | #[inline] |
1007 | | fn has_overflowed(&self) -> bool { |
1008 | | self.value > MAX_COUNTER |
1009 | | } |
1010 | | } |
1011 | | |
1012 | | /// A utility that adjusts an input timestamp by a given number of nanoseconds. |
1013 | | #[derive(Debug, Default)] |
1014 | | struct Adjust { |
1015 | | by_ns: u128, |
1016 | | } |
1017 | | |
1018 | | impl Adjust { |
1019 | | #[inline] |
1020 | | fn by_millis(millis: u32) -> Self { |
1021 | | Adjust { |
1022 | | by_ns: (millis as u128).saturating_mul(1_000_000), |
1023 | | } |
1024 | | } |
1025 | | |
1026 | | /// Apply the adjustment, returning the adjusted timestamp. |
1027 | | #[inline] |
1028 | | fn apply(&self, nanos: u128) -> u128 { |
1029 | | nanos.saturating_add(self.by_ns) |
1030 | | } |
1031 | | } |
1032 | | |
1033 | | /// A utility that overwrites some number of counter bits with additional timestamp precision. |
1034 | | #[derive(Debug, Default)] |
1035 | | struct Precision { |
1036 | | bits: usize, |
1037 | | factor: u64, |
1038 | | mask: u64, |
1039 | | shift: u64, |
1040 | | } |
1041 | | |
1042 | | impl Precision { |
1043 | | fn new(bits: usize) -> Self { |
1044 | | // The mask and shift are used to paste the sub-millisecond precision |
1045 | | // into the most significant bits of the counter |
1046 | | let mask = u64::MAX >> (64 - USABLE_BITS + bits); |
1047 | | let shift = (USABLE_BITS - bits) as u64; |
1048 | | |
1049 | | // The factor reduces the size of the sub-millisecond precision to |
1050 | | // fit into the specified number of bits |
1051 | | let factor = (999_999 / u64::pow(2, bits as u32)) + 1; |
1052 | | |
1053 | | Precision { |
1054 | | bits, |
1055 | | factor, |
1056 | | mask, |
1057 | | shift, |
1058 | | } |
1059 | | } |
1060 | | |
1061 | | /// Apply additional precision from the given timestamp to the counter. |
1062 | | #[inline] |
1063 | | fn apply(&self, counter: u64, timestamp: &ReseedingTimestamp) -> u64 { |
1064 | | if self.bits == 0 { |
1065 | | // No additional precision is being used |
1066 | | return counter; |
1067 | | } |
1068 | | |
1069 | | let additional = (timestamp.unix_nanos % 1_000_000) as u64 / self.factor; |
1070 | | |
1071 | | (counter & self.mask) | (additional << self.shift) |
1072 | | } |
1073 | | } |
1074 | | |
1075 | | #[cfg(feature = "std")] |
1076 | | pub(crate) struct SharedContextV7(std::sync::Mutex<ContextV7>); |
1077 | | |
1078 | | #[cfg(feature = "std")] |
1079 | | impl ClockSequence for SharedContextV7 { |
1080 | | type Output = u64; |
1081 | | |
1082 | | fn generate_sequence(&self, seconds: u64, subsec_nanos: u32) -> Self::Output { |
1083 | | self.0.generate_sequence(seconds, subsec_nanos) |
1084 | | } |
1085 | | |
1086 | | fn generate_timestamp_sequence( |
1087 | | &self, |
1088 | | seconds: u64, |
1089 | | subsec_nanos: u32, |
1090 | | ) -> (Self::Output, u64, u32) { |
1091 | | self.0.generate_timestamp_sequence(seconds, subsec_nanos) |
1092 | | } |
1093 | | |
1094 | | fn usable_bits(&self) -> usize |
1095 | | where |
1096 | | Self::Output: Sized, |
1097 | | { |
1098 | | USABLE_BITS |
1099 | | } |
1100 | | } |
1101 | | |
1102 | | #[cfg(test)] |
1103 | | mod tests { |
1104 | | use core::time::Duration; |
1105 | | use std::boxed::Box; |
1106 | | |
1107 | | use super::*; |
1108 | | |
1109 | | use crate::{Timestamp, Uuid}; |
1110 | | |
1111 | | fn builder(f: impl Fn() -> ContextV7 + 'static) -> Box<dyn Fn() -> ContextV7> { |
1112 | | Box::new(f) |
1113 | | } |
1114 | | |
1115 | | #[test] |
1116 | | fn context() { |
1117 | | let seconds = 1_496_854_535; |
1118 | | let subsec_nanos = 812_946_000; |
1119 | | |
1120 | | let context = ContextV7::new(); |
1121 | | |
1122 | | let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1123 | | assert_eq!(42, ts1.usable_counter_bits); |
1124 | | |
1125 | | // Backwards second |
1126 | | let seconds = 1_496_854_534; |
1127 | | |
1128 | | let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1129 | | |
1130 | | // The backwards time should be ignored |
1131 | | // The counter should still increment |
1132 | | assert_eq!(ts1.unix_nanos, ts2.unix_nanos); |
1133 | | assert_eq!(ts1.counter + 1, ts2.counter); |
1134 | | |
1135 | | // Forwards second |
1136 | | let seconds = 1_496_854_536; |
1137 | | |
1138 | | let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1139 | | |
1140 | | // The counter should have reseeded |
1141 | | assert_ne!(ts2.counter + 1, ts3.counter); |
1142 | | assert_ne!(0, ts3.counter); |
1143 | | } |
1144 | | |
1145 | | #[test] |
1146 | | fn context_wrap() { |
1147 | | let seconds = 1_496_854_535u64; |
1148 | | let subsec_nanos = 812_946_000u32; |
1149 | | |
1150 | | // This context will wrap |
1151 | | let context = ContextV7 { |
1152 | | timestamp: Cell::new(ReseedingTimestamp::saturating_from_unix_nanos( |
1153 | | parts_to_nanos(seconds, subsec_nanos), |
1154 | | )), |
1155 | | adjust: Adjust::by_millis(0), |
1156 | | precision: Precision { |
1157 | | bits: 0, |
1158 | | mask: 0, |
1159 | | factor: 0, |
1160 | | shift: 0, |
1161 | | }, |
1162 | | counter: Cell::new(Counter { |
1163 | | value: u64::MAX >> 22, |
1164 | | }), |
1165 | | }; |
1166 | | |
1167 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1168 | | |
1169 | | // The timestamp should be incremented by 1ms |
1170 | | let expected_ts = Duration::new(seconds, subsec_nanos) + Duration::from_millis(1); |
1171 | | assert_eq!(expected_ts.as_secs(), ts.to_unix().0); |
1172 | | assert_eq!(expected_ts.subsec_nanos(), ts.to_unix().1); |
1173 | | |
1174 | | // The counter should have reseeded |
1175 | | assert!(ts.counter < (u64::MAX >> 22) as u128); |
1176 | | assert_ne!(0, ts.counter); |
1177 | | } |
1178 | | |
1179 | | #[test] |
1180 | | fn context_shift() { |
1181 | | let seconds = 1_496_854_535; |
1182 | | let subsec_nanos = 812_946_000; |
1183 | | |
1184 | | let context = ContextV7::new().with_adjust_by_millis(1); |
1185 | | |
1186 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1187 | | |
1188 | | assert_eq!((1_496_854_535, 813_946_000), ts.to_unix()); |
1189 | | } |
1190 | | |
1191 | | #[test] |
1192 | | fn context_additional_precision() { |
1193 | | let seconds = 1_496_854_535; |
1194 | | let subsec_nanos = 812_946_000; |
1195 | | |
1196 | | let context = ContextV7::new().with_additional_precision(); |
1197 | | |
1198 | | let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1199 | | |
1200 | | // NOTE: Future changes in rounding may change this value slightly |
1201 | | assert_eq!(3861, ts1.counter >> 30); |
1202 | | |
1203 | | assert!(ts1.counter < (u64::MAX >> 22) as u128); |
1204 | | |
1205 | | // Generate another timestamp; it should continue to sort |
1206 | | let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1207 | | |
1208 | | assert!(Uuid::new_v7(ts2) > Uuid::new_v7(ts1)); |
1209 | | |
1210 | | // Generate another timestamp with an extra nanosecond |
1211 | | let subsec_nanos = subsec_nanos + 1; |
1212 | | |
1213 | | let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1214 | | |
1215 | | assert!(Uuid::new_v7(ts3) > Uuid::new_v7(ts2)); |
1216 | | } |
1217 | | |
1218 | | #[test] |
1219 | | fn context_additional_precision_bits() { |
1220 | | let seconds = 1_496_854_535; |
1221 | | let subsec_nanos = 812_946_000; |
1222 | | |
1223 | | // 10 bits leaves the low 2 bits of `rand_a` for random data, which suits |
1224 | | // platforms with microsecond precision |
1225 | | let context = ContextV7::new().with_additional_precision_bits(10); |
1226 | | |
1227 | | let ts = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1228 | | |
1229 | | // The submillisecond precision occupies the leftmost 10 of the 42 counter bits |
1230 | | // NOTE: Future changes in rounding may change this value slightly |
1231 | | assert_eq!(968, ts.counter >> 32); |
1232 | | |
1233 | | assert!(ts.counter < (u64::MAX >> 22) as u128); |
1234 | | |
1235 | | // The full method is just the 12-bit form of this one |
1236 | | let full = Timestamp::from_unix( |
1237 | | &ContextV7::new().with_additional_precision(), |
1238 | | seconds, |
1239 | | subsec_nanos, |
1240 | | ); |
1241 | | let bits12 = Timestamp::from_unix( |
1242 | | &ContextV7::new().with_additional_precision_bits(12), |
1243 | | seconds, |
1244 | | subsec_nanos, |
1245 | | ); |
1246 | | assert_eq!(full.counter >> 30, bits12.counter >> 30); |
1247 | | |
1248 | | // Values above 12 are capped at 12 |
1249 | | let capped = Timestamp::from_unix( |
1250 | | &ContextV7::new().with_additional_precision_bits(64), |
1251 | | seconds, |
1252 | | subsec_nanos, |
1253 | | ); |
1254 | | assert_eq!(bits12.counter >> 30, capped.counter >> 30); |
1255 | | |
1256 | | // Zero bits disables additional precision entirely |
1257 | | let none = ContextV7::new().with_additional_precision_bits(0); |
1258 | | assert_eq!(0, none.precision.bits); |
1259 | | } |
1260 | | |
1261 | | #[test] |
1262 | | fn context_additional_precision_carry() { |
1263 | | let seconds = 1_496_854_535; |
1264 | | let subsec_nanos = 812_946_000; |
1265 | | |
1266 | | for bits in 1..=12 { |
1267 | | let precision = Precision::new(bits); |
1268 | | let timestamp = ReseedingTimestamp::saturating_from_unix_nanos(parts_to_nanos( |
1269 | | seconds, |
1270 | | subsec_nanos, |
1271 | | )); |
1272 | | |
1273 | | // This context will carry into the additional precision |
1274 | | let context = ContextV7 { |
1275 | | counter: Cell::new(Counter { |
1276 | | value: precision.apply(u64::MAX, ×tamp), |
1277 | | }), |
1278 | | timestamp: Cell::new(timestamp), |
1279 | | adjust: Adjust::by_millis(0), |
1280 | | precision, |
1281 | | }; |
1282 | | |
1283 | | let a = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1284 | | let b = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1285 | | let c = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1286 | | |
1287 | | assert!(Uuid::new_v7(a) < Uuid::new_v7(b), "a: {a:?} < b: {b:?}"); |
1288 | | assert!(Uuid::new_v7(b) < Uuid::new_v7(c), "b: {b:?} < c: {c:?}"); |
1289 | | } |
1290 | | } |
1291 | | |
1292 | | #[test] |
1293 | | fn context_overflow() { |
1294 | | let seconds = u64::MAX; |
1295 | | let subsec_nanos = u32::MAX; |
1296 | | |
1297 | | // Ensure we don't panic |
1298 | | for context in [ |
1299 | | builder(|| ContextV7::new()), |
1300 | | builder(|| ContextV7::new().with_additional_precision()), |
1301 | | builder(|| ContextV7::new().with_adjust_by_millis(u32::MAX)), |
1302 | | builder(|| { |
1303 | | ContextV7::new() |
1304 | | .with_additional_precision() |
1305 | | .with_adjust_by_millis(u32::MAX) |
1306 | | }), |
1307 | | ] { |
1308 | | Timestamp::from_unix(&context(), seconds, subsec_nanos); |
1309 | | } |
1310 | | } |
1311 | | |
1312 | | #[test] |
1313 | | fn context_subsec_ordering() { |
1314 | | for context in [ |
1315 | | builder(|| ContextV7::new()), |
1316 | | builder(|| ContextV7::new().with_additional_precision()), |
1317 | | builder(|| ContextV7::new().with_adjust_by_millis(u32::MAX)), |
1318 | | builder(|| { |
1319 | | ContextV7::new() |
1320 | | .with_additional_precision() |
1321 | | .with_adjust_by_millis(u32::MAX) |
1322 | | }), |
1323 | | ] { |
1324 | | for (a, b, c) in [ |
1325 | | // Wall-clock order of incoming timestamps is b, a, c |
1326 | | ((100, 0), (99, 2_000_000), (100, 1_000_000)), |
1327 | | ((100, 0), (99, 1_000_900_000), (100, 1_000_000)), |
1328 | | ] { |
1329 | | let context = context(); |
1330 | | |
1331 | | // UUID order is a, b, c |
1332 | | let a = Timestamp::from_unix(&context, a.0, a.1); |
1333 | | let b = Timestamp::from_unix(&context, b.0, b.1); |
1334 | | let c = Timestamp::from_unix(&context, c.0, c.1); |
1335 | | |
1336 | | assert!(Uuid::new_v7(a) < Uuid::new_v7(b), "a: {a:?} < b: {b:?}"); |
1337 | | assert!(Uuid::new_v7(b) < Uuid::new_v7(c), "b: {b:?} < c: {c:?}"); |
1338 | | } |
1339 | | } |
1340 | | } |
1341 | | |
1342 | | #[test] |
1343 | | fn context_subsec_saturated_ordering() { |
1344 | | for context in [ |
1345 | | builder(|| ContextV7::new()), |
1346 | | builder(|| ContextV7::new().with_additional_precision()), |
1347 | | builder(|| ContextV7::new().with_adjust_by_millis(u32::MAX)), |
1348 | | builder(|| { |
1349 | | ContextV7::new() |
1350 | | .with_additional_precision() |
1351 | | .with_adjust_by_millis(u32::MAX) |
1352 | | }), |
1353 | | ] { |
1354 | | let context = context(); |
1355 | | |
1356 | | let secs = (u64::MAX / 1_000) + 1; |
1357 | | |
1358 | | let a = Timestamp::from_unix(&context, secs, 900_000); |
1359 | | let b = Timestamp::from_unix(&context, secs, 1_000_000); |
1360 | | |
1361 | | assert!(Uuid::new_v7(a) < Uuid::new_v7(b), "a: {a:?} < b: {b:?}"); |
1362 | | } |
1363 | | } |
1364 | | |
1365 | | #[test] |
1366 | | fn context_additional_precision_within_millisecond() { |
1367 | | let seconds = 1_496_854_535; |
1368 | | let subsec_nanos = 812_100_000; |
1369 | | |
1370 | | let context = ContextV7::new().with_additional_precision(); |
1371 | | |
1372 | | let ts1 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1373 | | |
1374 | | // Later in the same millisecond |
1375 | | let ts2 = Timestamp::from_unix(&context, seconds, subsec_nanos + 500_000); |
1376 | | |
1377 | | assert!( |
1378 | | ts2.counter >> 30 > ts1.counter >> 30, |
1379 | | "ts1: {ts1:?}, ts2: {ts2:?}" |
1380 | | ); |
1381 | | |
1382 | | // Earlier in the same millisecond |
1383 | | let ts3 = Timestamp::from_unix(&context, seconds, subsec_nanos); |
1384 | | |
1385 | | assert_eq!( |
1386 | | ts2.counter >> 30, |
1387 | | ts3.counter >> 30, |
1388 | | "ts2: {ts2:?}, ts3: {ts3:?}" |
1389 | | ); |
1390 | | assert_eq!(ts2.to_unix(), ts3.to_unix()); |
1391 | | |
1392 | | assert!(Uuid::new_v7(ts1) < Uuid::new_v7(ts2)); |
1393 | | assert!(Uuid::new_v7(ts2) < Uuid::new_v7(ts3)); |
1394 | | } |
1395 | | |
1396 | | #[test] |
1397 | | fn reseeding_timestamp_saturating() { |
1398 | | const MAX_UNIX_NANOS: u128 = (u64::MAX as u128) * 1_000_000 + 999_999; |
1399 | | const MAX_PARTS: (u64, u32) = (u64::MAX / 1_000, 615_999_999); |
1400 | | |
1401 | | // Values at or below the ceiling pass through |
1402 | | let below = |
1403 | | ReseedingTimestamp::saturating_from_unix_nanos(MAX_UNIX_NANOS - 1_000_000); |
1404 | | assert_eq!(MAX_UNIX_NANOS - 1_000_000, below.unix_nanos); |
1405 | | assert_eq!(u64::MAX - 1, below.last_seed); |
1406 | | assert_eq!((u64::MAX / 1_000, 614_999_999), below.to_parts()); |
1407 | | |
1408 | | // Values beyond the ceiling are clamped to it |
1409 | | for unix_nanos in [ |
1410 | | MAX_UNIX_NANOS, |
1411 | | MAX_UNIX_NANOS + 1, |
1412 | | parts_to_nanos(u64::MAX / 1_000 + 1, 0), |
1413 | | parts_to_nanos(u64::MAX, 999_999_999), |
1414 | | parts_to_nanos(u64::MAX, u32::MAX), |
1415 | | u128::MAX, |
1416 | | ] { |
1417 | | let ts = ReseedingTimestamp::saturating_from_unix_nanos(unix_nanos); |
1418 | | |
1419 | | assert_eq!(MAX_UNIX_NANOS, ts.unix_nanos, "{unix_nanos}"); |
1420 | | assert_eq!(u64::MAX, ts.last_seed, "{unix_nanos}"); |
1421 | | assert_eq!(MAX_PARTS, ts.to_parts(), "{unix_nanos}"); |
1422 | | } |
1423 | | |
1424 | | // Incrementing at the ceiling stays there instead of wrapping |
1425 | | let max = ReseedingTimestamp::saturating_from_unix_nanos(MAX_UNIX_NANOS); |
1426 | | let incremented = max.increment(); |
1427 | | assert_eq!(MAX_UNIX_NANOS, incremented.unix_nanos); |
1428 | | assert_eq!(u64::MAX, incremented.last_seed); |
1429 | | } |
1430 | | |
1431 | | #[test] |
1432 | | fn context_saturated_parts() { |
1433 | | const MAX_PARTS: (u64, u32) = (u64::MAX / 1_000, 615_999_999); |
1434 | | |
1435 | | for context in [ |
1436 | | builder(|| ContextV7::new()), |
1437 | | builder(|| ContextV7::new().with_adjust_by_millis(u32::MAX)), |
1438 | | ] { |
1439 | | for (seconds, subsec_nanos) in [ |
1440 | | MAX_PARTS, |
1441 | | (u64::MAX / 1_000 + 1, 0), |
1442 | | (u64::MAX, 999_999_999), |
1443 | | (u64::MAX, u32::MAX), |
1444 | | ] { |
1445 | | let ts = Timestamp::from_unix(&context(), seconds, subsec_nanos); |
1446 | | |
1447 | | assert_eq!(MAX_PARTS, ts.to_unix(), "{seconds}, {subsec_nanos}"); |
1448 | | } |
1449 | | } |
1450 | | |
1451 | | // A counter overflow at the ceiling can't advance the timestamp, but it mustn't wrap it either |
1452 | | let context = ContextV7 { |
1453 | | timestamp: Cell::new(ReseedingTimestamp::saturating_from_unix_nanos(u128::MAX)), |
1454 | | adjust: Adjust::by_millis(0), |
1455 | | precision: Precision::new(0), |
1456 | | counter: Cell::new(Counter { |
1457 | | value: u64::MAX >> 22, |
1458 | | }), |
1459 | | }; |
1460 | | |
1461 | | let ts = Timestamp::from_unix(&context, u64::MAX, u32::MAX); |
1462 | | |
1463 | | assert_eq!(MAX_PARTS, ts.to_unix()); |
1464 | | } |
1465 | | } |
1466 | | } |
1467 | | |
1468 | | #[cfg(feature = "v7")] |
1469 | | pub use v7_support::*; |
1470 | | |
1471 | | /// An empty counter that will always return the value `0`. |
1472 | | /// |
1473 | | /// This type can be used when constructing version 7 UUIDs. When used to |
1474 | | /// construct a version 7 UUID, the entire counter segment of the UUID will be |
1475 | | /// filled with a random value. This type does not maintain ordering of UUIDs |
1476 | | /// within a millisecond but is efficient. |
1477 | | /// |
1478 | | /// This type should not be used when constructing version 1 or version 6 UUIDs. |
1479 | | /// When used to construct a version 1 or version 6 UUID, the counter |
1480 | | /// segment will remain zero. |
1481 | | #[derive(Debug, Clone, Copy, Default)] |
1482 | | pub struct NoContext; |
1483 | | |
1484 | | impl ClockSequence for NoContext { |
1485 | | type Output = u16; |
1486 | | |
1487 | 0 | fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output { |
1488 | 0 | 0 |
1489 | 0 | } |
1490 | | |
1491 | 0 | fn usable_bits(&self) -> usize { |
1492 | 0 | 0 |
1493 | 0 | } |
1494 | | } |
1495 | | } |
1496 | | |
1497 | | #[cfg(all(test, any(feature = "v1", feature = "v6")))] |
1498 | | mod tests { |
1499 | | use super::*; |
1500 | | |
1501 | | #[cfg(all(target_family = "wasm", any(target_os = "unknown", target_os = "none")))] |
1502 | | use wasm_bindgen_test::*; |
1503 | | |
1504 | | #[test] |
1505 | | #[cfg_attr( |
1506 | | all(target_family = "wasm", any(target_os = "unknown", target_os = "none")), |
1507 | | wasm_bindgen_test |
1508 | | )] |
1509 | | fn gregorian_unix_does_not_panic() { |
1510 | | // Ensure timestamp conversions never panic |
1511 | | Timestamp::unix_to_gregorian_ticks(u64::MAX, 0); |
1512 | | Timestamp::unix_to_gregorian_ticks(0, u32::MAX); |
1513 | | Timestamp::unix_to_gregorian_ticks(u64::MAX, u32::MAX); |
1514 | | |
1515 | | Timestamp::gregorian_to_unix(u64::MAX); |
1516 | | } |
1517 | | |
1518 | | #[test] |
1519 | | #[cfg_attr( |
1520 | | all(target_family = "wasm", any(target_os = "unknown", target_os = "none")), |
1521 | | wasm_bindgen_test |
1522 | | )] |
1523 | | fn to_gregorian_truncates_to_usable_bits() { |
1524 | | let ts = Timestamp::from_gregorian_time(123, u16::MAX); |
1525 | | |
1526 | | assert_eq!((123, u16::MAX >> 2), ts.to_gregorian()); |
1527 | | } |
1528 | | |
1529 | | #[test] |
1530 | | #[cfg_attr( |
1531 | | all(target_family = "wasm", any(target_os = "unknown", target_os = "none")), |
1532 | | wasm_bindgen_test |
1533 | | )] |
1534 | | fn clock_sequence_usable_bits() { |
1535 | | struct MyContext; |
1536 | | |
1537 | | impl ClockSequence for MyContext { |
1538 | | type Output = u16; |
1539 | | |
1540 | | fn generate_sequence(&self, _: u64, _: u32) -> Self::Output { |
1541 | | 0 |
1542 | | } |
1543 | | } |
1544 | | |
1545 | | assert_eq!(16, MyContext.usable_bits()); |
1546 | | } |
1547 | | |
1548 | | const MAX_PARTS_NANOS: u128 = (u64::MAX as u128) * 1_000_000_000 + 999_999_999; |
1549 | | |
1550 | | #[test] |
1551 | | fn nanos_to_parts_roundtrip() { |
1552 | | for case in [ |
1553 | | 0, |
1554 | | 1, |
1555 | | 999_999_999, |
1556 | | 1_000_000_000, |
1557 | | u64::MAX as u128, |
1558 | | u64::MAX as u128 + 1, |
1559 | | u64::MAX as u128 + u32::MAX as u128, |
1560 | | u64::MAX as u128 + u32::MAX as u128 + 1, |
1561 | | (u64::MAX as u128) * 1_000_000 + 999_999, |
1562 | | (u64::MAX as u128) * 1_000_000_000, |
1563 | | MAX_PARTS_NANOS - 1, |
1564 | | MAX_PARTS_NANOS, |
1565 | | ] { |
1566 | | let (seconds, subsec_nanos) = nanos_to_parts(case); |
1567 | | |
1568 | | assert!(subsec_nanos < 1_000_000_000, "{case}"); |
1569 | | assert_eq!(case, parts_to_nanos(seconds, subsec_nanos), "{case}"); |
1570 | | } |
1571 | | } |
1572 | | |
1573 | | #[test] |
1574 | | fn nanos_to_parts_unnormalized_roundtrip() { |
1575 | | // Beyond the largest normalized value, seconds can't take the carry, |
1576 | | // so the remainder stays in the subsecond nanos |
1577 | | for (case, expected) in [ |
1578 | | (MAX_PARTS_NANOS + 1, (u64::MAX, 1_000_000_000)), |
1579 | | (MAX_PARTS_NANOS + 2, (u64::MAX, 1_000_000_001)), |
1580 | | ( |
1581 | | parts_to_nanos(u64::MAX, u32::MAX) - 1, |
1582 | | (u64::MAX, u32::MAX - 1), |
1583 | | ), |
1584 | | (parts_to_nanos(u64::MAX, u32::MAX), (u64::MAX, u32::MAX)), |
1585 | | ] { |
1586 | | let (seconds, subsec_nanos) = nanos_to_parts(case); |
1587 | | |
1588 | | assert_eq!(expected, (seconds, subsec_nanos), "{case}"); |
1589 | | assert_eq!(case, parts_to_nanos(seconds, subsec_nanos), "{case}"); |
1590 | | } |
1591 | | } |
1592 | | |
1593 | | #[test] |
1594 | | fn nanos_to_parts_saturating() { |
1595 | | for case in [parts_to_nanos(u64::MAX, u32::MAX) + 1, u128::MAX] { |
1596 | | let (seconds, subsec_nanos) = nanos_to_parts(case); |
1597 | | |
1598 | | assert_eq!(u64::MAX, seconds, "{case}"); |
1599 | | assert_eq!(u32::MAX, subsec_nanos, "{case}"); |
1600 | | } |
1601 | | } |
1602 | | |
1603 | | #[test] |
1604 | | fn parts_to_nanos_unnormalized() { |
1605 | | assert_eq!(2_500_000_000, parts_to_nanos(1, 1_500_000_000)); |
1606 | | assert_eq!( |
1607 | | parts_to_nanos(2, 500_000_000), |
1608 | | parts_to_nanos(1, 1_500_000_000) |
1609 | | ); |
1610 | | |
1611 | | // The largest input doesn't overflow |
1612 | | assert_eq!( |
1613 | | (u64::MAX as u128) * 1_000_000_000 + u32::MAX as u128, |
1614 | | parts_to_nanos(u64::MAX, u32::MAX) |
1615 | | ); |
1616 | | } |
1617 | | |
1618 | | #[test] |
1619 | | fn timestamp_unix_parts_boundaries() { |
1620 | | // Normalized values round-trip |
1621 | | for (seconds, subsec_nanos) in [ |
1622 | | (0, 0), |
1623 | | (1, 999_999_999), |
1624 | | (u64::MAX, 0), |
1625 | | (u64::MAX, 999_999_999), |
1626 | | ] { |
1627 | | let ts = Timestamp::from_unix_time(seconds, subsec_nanos, 0, 0); |
1628 | | |
1629 | | assert_eq!((seconds, subsec_nanos), ts.to_unix()); |
1630 | | } |
1631 | | |
1632 | | // Unnormalized values are normalized |
1633 | | let ts = Timestamp::from_unix_time(1, 1_500_000_000, 0, 0); |
1634 | | assert_eq!((2, 500_000_000), ts.to_unix()); |
1635 | | |
1636 | | // Values beyond the largest normalized are preserved |
1637 | | for (seconds, subsec_nanos) in [(u64::MAX, 1_000_000_000), (u64::MAX, u32::MAX)] { |
1638 | | let ts = Timestamp::from_unix_time(seconds, subsec_nanos, 0, 0); |
1639 | | |
1640 | | assert_eq!((seconds, subsec_nanos), ts.to_unix()); |
1641 | | } |
1642 | | |
1643 | | let ts = Timestamp::from_unix_time(u64::MAX - 1, u32::MAX, 0, 0); |
1644 | | assert_eq!((u64::MAX, u32::MAX - 1_000_000_000), ts.to_unix()); |
1645 | | } |
1646 | | |
1647 | | #[cfg(all(test, feature = "std", not(miri)))] |
1648 | | mod std_support { |
1649 | | use super::*; |
1650 | | |
1651 | | use std::time::{Duration, SystemTime}; |
1652 | | |
1653 | | // Components of an arbitrary timestamp with non-zero nanoseconds. |
1654 | | const KNOWN_SECONDS: u64 = 1_501_520_400; |
1655 | | const KNOWN_NANOS: u32 = 1_000; |
1656 | | |
1657 | | fn known_system_time() -> SystemTime { |
1658 | | SystemTime::UNIX_EPOCH |
1659 | | .checked_add(Duration::new(KNOWN_SECONDS, KNOWN_NANOS)) |
1660 | | .unwrap() |
1661 | | } |
1662 | | |
1663 | | fn known_timestamp() -> Timestamp { |
1664 | | Timestamp::from_unix_time(KNOWN_SECONDS, KNOWN_NANOS, 0, 0) |
1665 | | } |
1666 | | |
1667 | | #[test] |
1668 | | fn to_system_time() { |
1669 | | let st: SystemTime = known_timestamp().into(); |
1670 | | |
1671 | | assert_eq!(known_system_time(), st); |
1672 | | } |
1673 | | |
1674 | | #[test] |
1675 | | fn from_system_time() { |
1676 | | let ts: Timestamp = known_system_time().try_into().unwrap(); |
1677 | | |
1678 | | assert_eq!(known_timestamp(), ts); |
1679 | | } |
1680 | | |
1681 | | #[test] |
1682 | | fn from_system_time_before_epoch() { |
1683 | | let before_epoch = match SystemTime::UNIX_EPOCH.checked_sub(Duration::from_nanos(1_000)) |
1684 | | { |
1685 | | Some(st) => st, |
1686 | | None => return, |
1687 | | }; |
1688 | | |
1689 | | Timestamp::try_from(before_epoch) |
1690 | | .expect_err("Timestamp should not be created from before epoch"); |
1691 | | } |
1692 | | |
1693 | | #[test] |
1694 | | fn from_system_time_max() { |
1695 | | for (seconds, subsec_nanos) in [ |
1696 | | (u64::MAX, 999_999_999), |
1697 | | (u64::MAX, 1_000_000_000), |
1698 | | (u64::MAX, u32::MAX), |
1699 | | ] { |
1700 | | let ts = Timestamp::from_unix_time(seconds, subsec_nanos, 0, 0); |
1701 | | |
1702 | | // Just make sure we don't panic |
1703 | | let _: SystemTime = ts.into(); |
1704 | | } |
1705 | | } |
1706 | | } |
1707 | | } |