11package com .fasterxml .uuid .impl ;
22
33import java .security .SecureRandom ;
4+ import java .util .Objects ;
45import java .util .Random ;
56import java .util .UUID ;
6- import java .util .concurrent .locks .Lock ;
7- import java .util .concurrent .locks .ReentrantLock ;
7+ import java .util .function .Consumer ;
88
99import com .fasterxml .uuid .NoArgGenerator ;
1010import com .fasterxml .uuid .UUIDClock ;
@@ -35,9 +35,11 @@ public class TimeBasedEpochGenerator extends NoArgGenerator
3535 */
3636
3737 /**
38- * Random number generator that this generator uses.
38+ * Source for random numbers used to fill a byte array with entropy.
39+ *
40+ * @since 5.3 (replaced earlier {@code java.util.Random _random})
3941 */
40- protected final Random _random ;
42+ protected final Consumer < byte []> _randomNextBytes ;
4143
4244 /**
4345 * Underlying {@link UUIDClock} used for accessing current time, to use for
@@ -49,7 +51,6 @@ public class TimeBasedEpochGenerator extends NoArgGenerator
4951
5052 private long _lastTimestamp = -1 ;
5153 private final byte [] _lastEntropy = new byte [ENTROPY_BYTE_LENGTH ];
52- private final Lock lock = new ReentrantLock ();
5354
5455 /*
5556 /**********************************************************************
@@ -76,10 +77,21 @@ public TimeBasedEpochGenerator(Random rnd) {
7677 */
7778 public TimeBasedEpochGenerator (Random rnd , UUIDClock clock )
7879 {
79- if (rnd == null ) {
80- rnd = LazyRandom .sharedSecureRandom ();
81- }
82- _random = rnd ;
80+ this ((rnd == null ? LazyRandom .sharedSecureRandom () : rnd )::nextBytes , clock );
81+ }
82+
83+ /**
84+ *
85+ * @param randomNextBytes Source for random numbers to use for generating UUIDs.
86+ * Note that it is strongly recommend to use a <b>good</b> (pseudo) random number source;
87+ * for example, JDK's {@code SecureRandom::nextBytes}.
88+ * @param clock clock Object used for accessing current time to use for generation
89+ *
90+ * @since 5.3
91+ */
92+ protected TimeBasedEpochGenerator (Consumer <byte []> randomNextBytes , UUIDClock clock )
93+ {
94+ _randomNextBytes = Objects .requireNonNull (randomNextBytes );
8395 _clock = clock ;
8496 }
8597
@@ -120,28 +132,39 @@ public UUID generate()
120132 */
121133 public UUID construct (long rawTimestamp )
122134 {
123- lock . lock () ;
124- try {
135+ final long mostSigBits , leastSigBits ;
136+ synchronized ( _lastEntropy ) {
125137 if (rawTimestamp == _lastTimestamp ) {
126- boolean c = true ;
127- for (int i = ENTROPY_BYTE_LENGTH - 1 ; i >= 0 ; i --) {
128- if (c ) {
129- byte temp = _lastEntropy [i ];
130- temp = (byte ) (temp + 0x01 );
131- c = _lastEntropy [i ] == (byte ) 0xff ;
132- _lastEntropy [i ] = temp ;
138+ carry :
139+ {
140+ for (int i = ENTROPY_BYTE_LENGTH - 1 ; i > 0 ; i --) {
141+ _lastEntropy [i ] = (byte ) (_lastEntropy [i ] + 1 );
142+ if (_lastEntropy [i ] != 0x00 ) {
143+ break carry ;
144+ }
145+ }
146+ _lastEntropy [0 ] = (byte ) (_lastEntropy [0 ] + 1 );
147+ if (_lastEntropy [0 ] >= 0x04 ) {
148+ throw new IllegalStateException ("overflow on same millisecond" );
133149 }
134- }
135- if (c ) {
136- throw new IllegalStateException ("overflow on same millisecond" );
137150 }
138151 } else {
139152 _lastTimestamp = rawTimestamp ;
140- _random .nextBytes (_lastEntropy );
153+ _randomNextBytes .accept (_lastEntropy );
154+ // In the most significant byte, only 2 bits will fit in the UUID, and one of those should be cleared
155+ // to guard against overflow.
156+ _lastEntropy [0 ] &= 0x01 ;
141157 }
142- return UUIDUtil .constructUUID (UUIDType .TIME_BASED_EPOCH , (rawTimestamp << 16 ) | _toShort (_lastEntropy , 0 ), _toLong (_lastEntropy , 2 ));
143- } finally {
144- lock .unlock ();
158+ mostSigBits = rawTimestamp << 16 |
159+ (long ) UUIDType .TIME_BASED_EPOCH .raw () << 12 |
160+ Byte .toUnsignedLong (_lastEntropy [0 ]) << 10 |
161+ Byte .toUnsignedLong (_lastEntropy [1 ]) << 2 |
162+ Byte .toUnsignedLong (_lastEntropy [2 ]) >>> 6 ;
163+ long right62Mask = (1L << 62 ) - 1 ;
164+ long variant = 0x02 ;
165+ leastSigBits = variant << 62 |
166+ _toLong (_lastEntropy , 2 ) & right62Mask ;
145167 }
168+ return new UUID (mostSigBits , leastSigBits );
146169 }
147170}
0 commit comments