Skip to content

Commit ceeaf4b

Browse files
committed
ensure we don't overflow on counters less than 12
1 parent 63bc8f5 commit ceeaf4b

1 file changed

Lines changed: 39 additions & 24 deletions

File tree

src/v7.rs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! Note that you need to enable the `v7` Cargo feature
44
//! in order to use this module.
55
6-
use crate::{rng, timestamp::Timestamp, Builder, Uuid};
6+
use crate::{rng, std::cmp, timestamp::Timestamp, Builder, Uuid};
77

88
impl Uuid {
99
/// Create a new version 7 UUID using the current time value.
@@ -72,40 +72,49 @@ impl Uuid {
7272
.saturating_mul(1000)
7373
.saturating_add(nanos as u64 / 1_000_000);
7474

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();
8076

8177
// If the counter intersects the variant field then shift around it.
8278
// This ensures that any bits set in the counter that would intersect
8379
// 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);
8782
counter = (counter & !mask) | ((counter & mask) << 2);
88-
8983
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+
};
9195

9296
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)
96105
}
97106
// `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);
104111

105-
counter_and_random
112+
mask_counter_into_random(rng::u128(), counter, counter_bits)
106113
}
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+
109118
counter
110119
}
111120
};
@@ -243,7 +252,13 @@ mod tests {
243252
wasm_bindgen_test
244253
)]
245254
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+
] {
247262
for counter in [0u128, u128::MAX] {
248263
let ts = Timestamp::from_unix_time(1_700_000_000, 0, counter, width);
249264

0 commit comments

Comments
 (0)