1use crate::{rng, std::convert::TryInto, timestamp::Timestamp, Builder, Uuid};
7
8impl Uuid {
9 #[cfg(feature = "std")]
15 pub fn now_v7() -> Self {
16 Self::new_v7(Timestamp::now(
17 crate::timestamp::context::shared_context_v7(),
18 ))
19 }
20
21 pub fn new_v7(ts: Timestamp) -> Self {
64 let (secs, nanos) = ts.to_unix();
65 let millis = (secs * 1000).saturating_add(nanos as u64 / 1_000_000);
66
67 let mut counter_and_random = rng::u128();
68
69 let (mut counter, counter_bits) = ts.counter();
70
71 debug_assert!(counter_bits <= 128);
72
73 let mut counter_bits = counter_bits as u32;
74
75 if counter_bits > 12 {
79 let mask = u128::MAX << (counter_bits - 12);
80
81 counter = (counter & !mask) | ((counter & mask) << 2);
82
83 counter_bits += 2;
84 }
85
86 counter_and_random &= u128::MAX.overflowing_shr(counter_bits).0;
87 counter_and_random |= counter
88 .overflowing_shl(128u32.saturating_sub(counter_bits))
89 .0;
90
91 Builder::from_unix_timestamp_millis(
92 millis,
93 &counter_and_random.to_be_bytes()[..10].try_into().unwrap(),
94 )
95 .into_uuid()
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 use crate::{std::string::ToString, ClockSequence, NoContext, Variant, Version};
104
105 #[cfg(all(
106 target_arch = "wasm32",
107 target_vendor = "unknown",
108 target_os = "unknown"
109 ))]
110 use wasm_bindgen_test::*;
111
112 #[test]
113 #[cfg_attr(
114 all(
115 target_arch = "wasm32",
116 target_vendor = "unknown",
117 target_os = "unknown"
118 ),
119 wasm_bindgen_test
120 )]
121 fn test_new() {
122 let ts: u64 = 1645557742000;
123
124 let seconds = ts / 1000;
125 let nanos = ((ts % 1000) * 1_000_000) as u32;
126
127 let uuid = Uuid::new_v7(Timestamp::from_unix(NoContext, seconds, nanos));
128 let uustr = uuid.hyphenated().to_string();
129
130 assert_eq!(uuid.get_version(), Some(Version::SortRand));
131 assert_eq!(uuid.get_variant(), Variant::RFC4122);
132 assert!(uuid.hyphenated().to_string().starts_with("017f22e2-79b0-7"));
133
134 let parsed = Uuid::parse_str(uustr.as_str()).unwrap();
136
137 assert_eq!(uuid, parsed);
138 }
139
140 #[test]
141 #[cfg_attr(
142 all(
143 target_arch = "wasm32",
144 target_vendor = "unknown",
145 target_os = "unknown"
146 ),
147 wasm_bindgen_test
148 )]
149 #[cfg(feature = "std")]
150 fn test_now() {
151 let uuid = Uuid::now_v7();
152
153 assert_eq!(uuid.get_version(), Some(Version::SortRand));
154 assert_eq!(uuid.get_variant(), Variant::RFC4122);
155 }
156
157 #[test]
158 #[cfg_attr(
159 all(
160 target_arch = "wasm32",
161 target_vendor = "unknown",
162 target_os = "unknown"
163 ),
164 wasm_bindgen_test
165 )]
166 fn test_sorting() {
167 let time1: u64 = 1_496_854_535;
168 let time_fraction1: u32 = 812_000_000;
169
170 let time2 = time1 + 4000;
171 let time_fraction2 = time_fraction1;
172
173 let uuid1 = Uuid::new_v7(Timestamp::from_unix(NoContext, time1, time_fraction1));
174 let uuid2 = Uuid::new_v7(Timestamp::from_unix(NoContext, time2, time_fraction2));
175
176 assert!(uuid1.as_bytes() < uuid2.as_bytes());
177 assert!(uuid1.to_string() < uuid2.to_string());
178 }
179
180 #[test]
181 #[cfg_attr(
182 all(
183 target_arch = "wasm32",
184 target_vendor = "unknown",
185 target_os = "unknown"
186 ),
187 wasm_bindgen_test
188 )]
189 fn test_new_timestamp_roundtrip() {
190 let time: u64 = 1_496_854_535;
191 let time_fraction: u32 = 812_000_000;
192
193 let ts = Timestamp::from_unix(NoContext, time, time_fraction);
194
195 let uuid = Uuid::new_v7(ts);
196
197 let decoded_ts = uuid.get_timestamp().unwrap();
198
199 assert_eq!(ts.to_unix(), decoded_ts.to_unix());
200 }
201
202 #[test]
203 #[cfg_attr(
204 all(
205 target_arch = "wasm32",
206 target_vendor = "unknown",
207 target_os = "unknown"
208 ),
209 wasm_bindgen_test
210 )]
211 fn test_new_max_context() {
212 struct MaxContext;
213
214 #[cfg(test)]
215 impl ClockSequence for MaxContext {
216 type Output = u128;
217
218 fn generate_sequence(&self, _seconds: u64, _nanos: u32) -> Self::Output {
219 u128::MAX
220 }
221
222 fn usable_bits(&self) -> usize {
223 128
224 }
225 }
226
227 let time: u64 = 1_496_854_535;
228 let time_fraction: u32 = 812_000_000;
229
230 let ts = Timestamp::from_unix(MaxContext, time, time_fraction);
232
233 let uuid = Uuid::new_v7(ts);
234
235 assert_eq!(uuid.get_version(), Some(Version::SortRand));
236 assert_eq!(uuid.get_variant(), Variant::RFC4122);
237
238 let decoded_ts = uuid.get_timestamp().unwrap();
239
240 assert_eq!(ts.to_unix(), decoded_ts.to_unix());
241 }
242}