|
3 | 3 | //! Note that you need to enable the `v7` Cargo feature |
4 | 4 | //! in order to use this module. |
5 | 5 |
|
6 | | -use crate::{rng, timestamp::Timestamp, Builder, Uuid}; |
| 6 | +use crate::{rng, std::cmp, timestamp::Timestamp, Builder, Uuid}; |
7 | 7 |
|
8 | 8 | impl Uuid { |
9 | 9 | /// Create a new version 7 UUID using the current time value. |
@@ -72,40 +72,49 @@ impl Uuid { |
72 | 72 | .saturating_mul(1000) |
73 | 73 | .saturating_add(nanos as u64 / 1_000_000); |
74 | 74 |
|
75 | | - let (mut counter, counter_bits) = ts.counter(); |
76 | | - |
77 | | - debug_assert!(counter_bits <= 128); |
78 | | - |
79 | | - let mut counter_bits = counter_bits as u32; |
| 75 | + let (counter, counter_bits) = ts.counter(); |
80 | 76 |
|
81 | 77 | // If the counter intersects the variant field then shift around it. |
82 | 78 | // This ensures that any bits set in the counter that would intersect |
83 | 79 | // the variant are still preserved |
84 | | - if counter_bits > 12 { |
85 | | - let mask = u128::MAX << (counter_bits - 12); |
86 | | - |
| 80 | + let shift_counter_over_variant = |mut counter: u128, mut counter_bits: u32| { |
| 81 | + let mask = u128::MAX << (cmp::min(128, counter_bits) - 12); |
87 | 82 | counter = (counter & !mask) | ((counter & mask) << 2); |
88 | | - |
89 | 83 | counter_bits += 2; |
90 | | - } |
| 84 | + |
| 85 | + (counter, counter_bits) |
| 86 | + }; |
| 87 | + |
| 88 | + // Mask `counter_bits` of the `counter` into `dst` |
| 89 | + let mask_counter_into_random = |mut dst: u128, counter: u128, counter_bits: u32| { |
| 90 | + dst &= u128::MAX >> counter_bits; |
| 91 | + dst |= counter << (128 - counter_bits); |
| 92 | + |
| 93 | + dst |
| 94 | + }; |
91 | 95 |
|
92 | 96 | let counter_and_random = match counter_bits { |
93 | | - 0 => { |
94 | | - // The counter doesn't contribute any bits |
95 | | - rng::u128() |
| 97 | + // The counter doesn't contribute any bits |
| 98 | + 0 => rng::u128(), |
| 99 | + // The counter doesn't intersect the variant field |
| 100 | + // It needs to be merged with random data |
| 101 | + ..12 => { |
| 102 | + let counter_bits = counter_bits as u32; |
| 103 | + |
| 104 | + mask_counter_into_random(rng::u128(), counter, counter_bits) |
96 | 105 | } |
97 | 106 | // `rand_a` (12 bits) + `rand_b` (62 bits) + `var` (2 bits) |
98 | | - ..76 => { |
99 | | - // The counter assigns some number of bits |
100 | | - let mut counter_and_random = rng::u128(); |
101 | | - |
102 | | - counter_and_random &= u128::MAX >> counter_bits; |
103 | | - counter_and_random |= counter << (128 - counter_bits); |
| 107 | + // The counter needs to be shifted around the variant and merged with random data |
| 108 | + ..74 => { |
| 109 | + let (counter, counter_bits) = |
| 110 | + shift_counter_over_variant(counter, counter_bits as u32); |
104 | 111 |
|
105 | | - counter_and_random |
| 112 | + mask_counter_into_random(rng::u128(), counter, counter_bits) |
106 | 113 | } |
107 | | - 76.. => { |
108 | | - // The counter overrides all bits |
| 114 | + // The counter overrides all bits |
| 115 | + 74.. => { |
| 116 | + let (counter, _) = shift_counter_over_variant(counter, counter_bits as u32); |
| 117 | + |
109 | 118 | counter |
110 | 119 | } |
111 | 120 | }; |
@@ -243,7 +252,13 @@ mod tests { |
243 | 252 | wasm_bindgen_test |
244 | 253 | )] |
245 | 254 | fn test_new_counter_range() { |
246 | | - for (width, eq) in [(0, false), (43, false), (74, true), (128, true)] { |
| 255 | + for (width, eq) in [ |
| 256 | + (0, false), |
| 257 | + (3, false), |
| 258 | + (43, false), |
| 259 | + (74, true), |
| 260 | + (u8::MAX, true), |
| 261 | + ] { |
247 | 262 | for counter in [0u128, u128::MAX] { |
248 | 263 | let ts = Timestamp::from_unix_time(1_700_000_000, 0, counter, width); |
249 | 264 |
|
|
0 commit comments