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(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 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 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}