Coverage Report

Created: 2025-11-28 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.18.1/src/v7.rs
Line
Count
Source
1
//! The implementation for Version 7 UUIDs.
2
//!
3
//! Note that you need to enable the `v7` Cargo feature
4
//! in order to use this module.
5
6
use crate::{rng, std::convert::TryInto, timestamp::Timestamp, Builder, Uuid};
7
8
impl Uuid {
9
    /// Create a new version 7 UUID using the current time value.
10
    ///
11
    /// This method is a convenient alternative to [`Uuid::new_v7`] that uses the current system time
12
    /// as the source timestamp. All UUIDs generated through this method by the same process are
13
    /// guaranteed to be ordered by their creation.
14
    #[cfg(feature = "std")]
15
290
    pub fn now_v7() -> Self {
16
290
        Self::new_v7(Timestamp::now(
17
290
            crate::timestamp::context::shared_context_v7(),
18
        ))
19
290
    }
20
21
    /// Create a new version 7 UUID using a time value and random bytes.
22
    ///
23
    /// When the `std` feature is enabled, you can also use [`Uuid::now_v7`].
24
    ///
25
    /// Note that usage of this method requires the `v7` feature of this crate
26
    /// to be enabled.
27
    ///
28
    /// Also see [`Uuid::now_v7`] for a convenient way to generate version 7
29
    /// UUIDs using the current system time.
30
    ///
31
    /// # Examples
32
    ///
33
    /// A v7 UUID can be created from a unix [`Timestamp`] plus a 128 bit
34
    /// random number. When supplied as such, the data will be combined
35
    /// to ensure uniqueness and sortability at millisecond granularity.
36
    ///
37
    /// ```rust
38
    /// # use uuid::{Uuid, Timestamp, NoContext};
39
    /// let ts = Timestamp::from_unix(NoContext, 1497624119, 1234);
40
    ///
41
    /// let uuid = Uuid::new_v7(ts);
42
    ///
43
    /// assert!(
44
    ///     uuid.hyphenated().to_string().starts_with("015cb15a-86d8-7")
45
    /// );
46
    /// ```
47
    ///
48
    /// A v7 UUID can also be created with a counter to ensure batches of
49
    /// UUIDs created together remain sortable:
50
    ///
51
    /// ```rust
52
    /// # use uuid::{Uuid, Timestamp, ContextV7};
53
    /// let context = ContextV7::new();
54
    /// let uuid1 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
55
    /// let uuid2 = Uuid::new_v7(Timestamp::from_unix(&context, 1497624119, 1234));
56
    ///
57
    /// assert!(uuid1 < uuid2);
58
    /// ```
59
    ///
60
    /// # References
61
    ///
62
    /// * [UUID Version 7 in RFC 9562](https://www.ietf.org/rfc/rfc9562.html#section-5.7)
63
290
    pub fn new_v7(ts: Timestamp) -> Self {
64
290
        let (secs, nanos) = ts.to_unix();
65
290
        let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000);
66
67
290
        let mut counter_and_random = rng::u128();
68
69
290
        let (mut counter, counter_bits) = ts.counter();
70
71
290
        debug_assert!(counter_bits <= 128);
72
73
290
        let mut counter_bits = counter_bits as u32;
74
75
        // If the counter intersects the variant field then shift around it.
76
        // This ensures that any bits set in the counter that would intersect
77
        // the variant are still preserved
78
290
        if counter_bits > 12 {
79
290
            let mask = u128::MAX << (counter_bits - 12);
80
290
81
290
            counter = (counter & !mask) | ((counter & mask) << 2);
82
290
83
290
            counter_bits += 2;
84
290
        }
85
86
290
        counter_and_random &= u128::MAX.overflowing_shr(counter_bits).0;
87
290
        counter_and_random |= counter
88
290
            .overflowing_shl(128u32.saturating_sub(counter_bits))
89
290
            .0;
90
91
290
        Builder::from_unix_timestamp_millis(
92
290
            millis,
93
290
            &counter_and_random.to_be_bytes()[..10].try_into().unwrap(),
94
        )
95
290
        .into_uuid()
96
290
    }
97
}
98
99
#[cfg(test)]
100
mod tests {
101
    use super::*;
102
103
    use crate::{std::string::ToString, ClockSequence, NoContext, Variant, Version};
104
105
    #[cfg(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")))]
106
    use wasm_bindgen_test::*;
107
108
    #[test]
109
    #[cfg_attr(
110
        all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
111
        wasm_bindgen_test
112
    )]
113
    fn test_new() {
114
        let ts: u64 = 1645557742000;
115
116
        let seconds = ts / 1000;
117
        let nanos = ((ts % 1000) * 1_000_000) as u32;
118
119
        let uuid = Uuid::new_v7(Timestamp::from_unix(NoContext, seconds, nanos));
120
        let uustr = uuid.hyphenated().to_string();
121
122
        assert_eq!(uuid.get_version(), Some(Version::SortRand));
123
        assert_eq!(uuid.get_variant(), Variant::RFC4122);
124
        assert!(uuid.hyphenated().to_string().starts_with("017f22e2-79b0-7"));
125
126
        // Ensure parsing the same UUID produces the same timestamp
127
        let parsed = Uuid::parse_str(uustr.as_str()).unwrap();
128
129
        assert_eq!(uuid, parsed);
130
    }
131
132
    #[test]
133
    #[cfg_attr(
134
        all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
135
        wasm_bindgen_test
136
    )]
137
    #[cfg(feature = "std")]
138
    fn test_now() {
139
        let uuid = Uuid::now_v7();
140
141
        assert_eq!(uuid.get_version(), Some(Version::SortRand));
142
        assert_eq!(uuid.get_variant(), Variant::RFC4122);
143
    }
144
145
    #[test]
146
    #[cfg_attr(
147
        all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
148
        wasm_bindgen_test
149
    )]
150
    fn test_sorting() {
151
        let time1: u64 = 1_496_854_535;
152
        let time_fraction1: u32 = 812_000_000;
153
154
        let time2 = time1 + 4000;
155
        let time_fraction2 = time_fraction1;
156
157
        let uuid1 = Uuid::new_v7(Timestamp::from_unix(NoContext, time1, time_fraction1));
158
        let uuid2 = Uuid::new_v7(Timestamp::from_unix(NoContext, time2, time_fraction2));
159
160
        assert!(uuid1.as_bytes() < uuid2.as_bytes());
161
        assert!(uuid1.to_string() < uuid2.to_string());
162
    }
163
164
    #[test]
165
    #[cfg_attr(
166
        all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
167
        wasm_bindgen_test
168
    )]
169
    fn test_new_timestamp_roundtrip() {
170
        let time: u64 = 1_496_854_535;
171
        let time_fraction: u32 = 812_000_000;
172
173
        let ts = Timestamp::from_unix(NoContext, time, time_fraction);
174
175
        let uuid = Uuid::new_v7(ts);
176
177
        let decoded_ts = uuid.get_timestamp().unwrap();
178
179
        assert_eq!(ts.to_unix(), decoded_ts.to_unix());
180
    }
181
182
    #[test]
183
    #[cfg_attr(
184
        all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none")),
185
        wasm_bindgen_test
186
    )]
187
    fn test_new_max_context() {
188
        struct MaxContext;
189
190
        #[cfg(test)]
191
        impl ClockSequence for MaxContext {
192
            type Output = u128;
193
194
            fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
195
                u128::MAX
196
            }
197
198
            fn usable_bits(&self) -> usize {
199
                128
200
            }
201
        }
202
203
        let time: u64 = 1_496_854_535;
204
        let time_fraction: u32 = 812_000_000;
205
206
        // Ensure we don't overflow here
207
        let ts = Timestamp::from_unix(MaxContext, time, time_fraction);
208
209
        let uuid = Uuid::new_v7(ts);
210
211
        assert_eq!(uuid.get_version(), Some(Version::SortRand));
212
        assert_eq!(uuid.get_variant(), Variant::RFC4122);
213
214
        let decoded_ts = uuid.get_timestamp().unwrap();
215
216
        assert_eq!(ts.to_unix(), decoded_ts.to_unix());
217
    }
218
}