16
16
17
17
package org .bson .types ;
18
18
19
+ import static org .bson .assertions .Assertions .isTrueArgument ;
20
+ import static org .bson .assertions .Assertions .notNull ;
21
+
19
22
import java .io .InvalidObjectException ;
20
23
import java .io .ObjectInputStream ;
21
24
import java .io .Serializable ;
22
25
import java .nio .ByteBuffer ;
26
+ import java .nio .ByteOrder ;
23
27
import java .security .SecureRandom ;
24
28
import java .util .Date ;
25
29
import java .util .concurrent .atomic .AtomicInteger ;
26
30
27
- import static org .bson .assertions .Assertions .isTrueArgument ;
28
- import static org .bson .assertions .Assertions .notNull ;
29
-
30
31
/**
31
32
* <p>A globally unique identifier for objects.</p>
32
33
*
@@ -53,9 +54,8 @@ public final class ObjectId implements Comparable<ObjectId>, Serializable {
53
54
private static final int OBJECT_ID_LENGTH = 12 ;
54
55
private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff ;
55
56
56
- // Use primitives to represent the 5-byte random value.
57
- private static final int RANDOM_VALUE1 ;
58
- private static final short RANDOM_VALUE2 ;
57
+ // Use upper bytes of a long to represent the 5-byte random value.
58
+ private static final long RANDOM_VALUE ;
59
59
60
60
private static final AtomicInteger NEXT_COUNTER ;
61
61
@@ -67,18 +67,12 @@ public final class ObjectId implements Comparable<ObjectId>, Serializable {
67
67
* The timestamp
68
68
*/
69
69
private final int timestamp ;
70
+
70
71
/**
71
- * The counter.
72
- */
73
- private final int counter ;
74
- /**
75
- * the first four bits of randomness.
76
- */
77
- private final int randomValue1 ;
78
- /**
79
- * The last two bits of randomness.
72
+ * The final 8 bytes of the ObjectID are 5 bytes probabilistically unique to the machine and
73
+ * process, followed by a 3 byte incrementing counter initialized to a random value.
80
74
*/
81
- private final short randomValue2 ;
75
+ private final long nonce ;
82
76
83
77
/**
84
78
* Gets a new object id.
@@ -101,7 +95,7 @@ public static ObjectId get() {
101
95
* @since 4.1
102
96
*/
103
97
public static ObjectId getSmallestWithDate (final Date date ) {
104
- return new ObjectId (dateToTimestampSeconds (date ), 0 , ( short ) 0 , 0 , false );
98
+ return new ObjectId (dateToTimestampSeconds (date ), 0L );
105
99
}
106
100
107
101
/**
@@ -152,7 +146,7 @@ public ObjectId() {
152
146
* @param date the date
153
147
*/
154
148
public ObjectId (final Date date ) {
155
- this (dateToTimestampSeconds (date ), NEXT_COUNTER .getAndIncrement () & LOW_ORDER_THREE_BYTES , false );
149
+ this (dateToTimestampSeconds (date ), RANDOM_VALUE | ( NEXT_COUNTER .getAndIncrement () & LOW_ORDER_THREE_BYTES ) );
156
150
}
157
151
158
152
/**
@@ -163,7 +157,7 @@ public ObjectId(final Date date) {
163
157
* @throws IllegalArgumentException if the high order byte of counter is not zero
164
158
*/
165
159
public ObjectId (final Date date , final int counter ) {
166
- this (dateToTimestampSeconds (date ), counter , true );
160
+ this (dateToTimestampSeconds (date ), getNonceFromUntrustedCounter ( counter ) );
167
161
}
168
162
169
163
/**
@@ -174,25 +168,19 @@ public ObjectId(final Date date, final int counter) {
174
168
* @throws IllegalArgumentException if the high order byte of counter is not zero
175
169
*/
176
170
public ObjectId (final int timestamp , final int counter ) {
177
- this (timestamp , counter , true );
171
+ this (timestamp , getNonceFromUntrustedCounter ( counter ) );
178
172
}
179
173
180
- private ObjectId (final int timestamp , final int counter , final boolean checkCounter ) {
181
- this (timestamp , RANDOM_VALUE1 , RANDOM_VALUE2 , counter , checkCounter );
174
+ private ObjectId (final int timestamp , final long nonce ) {
175
+ this .timestamp = timestamp ;
176
+ this .nonce = nonce ;
182
177
}
183
178
184
- private ObjectId (final int timestamp , final int randomValue1 , final short randomValue2 , final int counter ,
185
- final boolean checkCounter ) {
186
- if ((randomValue1 & 0xff000000 ) != 0 ) {
187
- throw new IllegalArgumentException ("The random value must be between 0 and 16777215 (it must fit in three bytes)." );
188
- }
189
- if (checkCounter && ((counter & 0xff000000 ) != 0 )) {
179
+ private static long getNonceFromUntrustedCounter (final int counter ) {
180
+ if ((counter & 0xff000000 ) != 0 ) {
190
181
throw new IllegalArgumentException ("The counter must be between 0 and 16777215 (it must fit in three bytes)." );
191
182
}
192
- this .timestamp = timestamp ;
193
- this .counter = counter & LOW_ORDER_THREE_BYTES ;
194
- this .randomValue1 = randomValue1 ;
195
- this .randomValue2 = randomValue2 ;
183
+ return RANDOM_VALUE | counter ;
196
184
}
197
185
198
186
/**
@@ -226,12 +214,14 @@ public ObjectId(final ByteBuffer buffer) {
226
214
notNull ("buffer" , buffer );
227
215
isTrueArgument ("buffer.remaining() >=12" , buffer .remaining () >= OBJECT_ID_LENGTH );
228
216
229
- // Note: Cannot use ByteBuffer.getInt because it depends on tbe buffer's byte order
230
- // and ObjectId's are always in big-endian order.
231
- timestamp = makeInt (buffer .get (), buffer .get (), buffer .get (), buffer .get ());
232
- randomValue1 = makeInt ((byte ) 0 , buffer .get (), buffer .get (), buffer .get ());
233
- randomValue2 = makeShort (buffer .get (), buffer .get ());
234
- counter = makeInt ((byte ) 0 , buffer .get (), buffer .get (), buffer .get ());
217
+ ByteOrder originalOrder = buffer .order ();
218
+ try {
219
+ buffer .order (ByteOrder .BIG_ENDIAN );
220
+ this .timestamp = buffer .getInt ();
221
+ this .nonce = buffer .getLong ();
222
+ } finally {
223
+ buffer .order (originalOrder );
224
+ }
235
225
}
236
226
237
227
/**
@@ -240,9 +230,11 @@ public ObjectId(final ByteBuffer buffer) {
240
230
* @return the byte array
241
231
*/
242
232
public byte [] toByteArray () {
243
- ByteBuffer buffer = ByteBuffer .allocate (OBJECT_ID_LENGTH );
244
- putToByteBuffer (buffer );
245
- return buffer .array (); // using .allocate ensures there is a backing array that can be returned
233
+ // using .allocate ensures there is a backing array that can be returned
234
+ return ByteBuffer .allocate (OBJECT_ID_LENGTH )
235
+ .putInt (this .timestamp )
236
+ .putLong (this .nonce )
237
+ .array ();
246
238
}
247
239
248
240
/**
@@ -257,18 +249,14 @@ public void putToByteBuffer(final ByteBuffer buffer) {
257
249
notNull ("buffer" , buffer );
258
250
isTrueArgument ("buffer.remaining() >=12" , buffer .remaining () >= OBJECT_ID_LENGTH );
259
251
260
- buffer .put (int3 (timestamp ));
261
- buffer .put (int2 (timestamp ));
262
- buffer .put (int1 (timestamp ));
263
- buffer .put (int0 (timestamp ));
264
- buffer .put (int2 (randomValue1 ));
265
- buffer .put (int1 (randomValue1 ));
266
- buffer .put (int0 (randomValue1 ));
267
- buffer .put (short1 (randomValue2 ));
268
- buffer .put (short0 (randomValue2 ));
269
- buffer .put (int2 (counter ));
270
- buffer .put (int1 (counter ));
271
- buffer .put (int0 (counter ));
252
+ ByteOrder originalOrder = buffer .order ();
253
+ try {
254
+ buffer .order (ByteOrder .BIG_ENDIAN );
255
+ buffer .putInt (this .timestamp );
256
+ buffer .putLong (this .nonce );
257
+ } finally {
258
+ buffer .order (originalOrder );
259
+ }
272
260
}
273
261
274
262
/**
@@ -313,49 +301,26 @@ public boolean equals(final Object o) {
313
301
return false ;
314
302
}
315
303
316
- ObjectId objectId = (ObjectId ) o ;
317
-
318
- if (counter != objectId .counter ) {
319
- return false ;
320
- }
321
- if (timestamp != objectId .timestamp ) {
322
- return false ;
323
- }
324
-
325
- if (randomValue1 != objectId .randomValue1 ) {
304
+ ObjectId other = (ObjectId ) o ;
305
+ if (timestamp != other .timestamp ) {
326
306
return false ;
327
307
}
328
-
329
- if (randomValue2 != objectId .randomValue2 ) {
330
- return false ;
331
- }
332
-
333
- return true ;
308
+ return nonce == other .nonce ;
334
309
}
335
310
336
311
@ Override
337
312
public int hashCode () {
338
- int result = timestamp ;
339
- result = 31 * result + counter ;
340
- result = 31 * result + randomValue1 ;
341
- result = 31 * result + randomValue2 ;
342
- return result ;
313
+ return 31 * timestamp + Long .hashCode (nonce );
343
314
}
344
315
345
316
@ Override
346
317
public int compareTo (final ObjectId other ) {
347
- if (other == null ) {
348
- throw new NullPointerException ();
318
+ int cmp = Integer .compareUnsigned (this .timestamp , other .timestamp );
319
+ if (cmp != 0 ) {
320
+ return cmp ;
349
321
}
350
322
351
- byte [] byteArray = toByteArray ();
352
- byte [] otherByteArray = other .toByteArray ();
353
- for (int i = 0 ; i < OBJECT_ID_LENGTH ; i ++) {
354
- if (byteArray [i ] != otherByteArray [i ]) {
355
- return ((byteArray [i ] & 0xff ) < (otherByteArray [i ] & 0xff )) ? -1 : 1 ;
356
- }
357
- }
358
- return 0 ;
323
+ return Long .compareUnsigned (nonce , other .nonce );
359
324
}
360
325
361
326
@ Override
@@ -407,8 +372,7 @@ private Object readResolve() {
407
372
static {
408
373
try {
409
374
SecureRandom secureRandom = new SecureRandom ();
410
- RANDOM_VALUE1 = secureRandom .nextInt (0x01000000 );
411
- RANDOM_VALUE2 = (short ) secureRandom .nextInt (0x00008000 );
375
+ RANDOM_VALUE = secureRandom .nextLong () & ~LOW_ORDER_THREE_BYTES ;
412
376
NEXT_COUNTER = new AtomicInteger (secureRandom .nextInt ());
413
377
} catch (Exception e ) {
414
378
throw new RuntimeException (e );
@@ -443,46 +407,4 @@ private static int hexCharToInt(final char c) {
443
407
private static int dateToTimestampSeconds (final Date time ) {
444
408
return (int ) (time .getTime () / 1000 );
445
409
}
446
-
447
- // Big-Endian helpers, in this class because all other BSON numbers are little-endian
448
-
449
- private static int makeInt (final byte b3 , final byte b2 , final byte b1 , final byte b0 ) {
450
- // CHECKSTYLE:OFF
451
- return (((b3 ) << 24 ) |
452
- ((b2 & 0xff ) << 16 ) |
453
- ((b1 & 0xff ) << 8 ) |
454
- ((b0 & 0xff )));
455
- // CHECKSTYLE:ON
456
- }
457
-
458
- private static short makeShort (final byte b1 , final byte b0 ) {
459
- // CHECKSTYLE:OFF
460
- return (short ) (((b1 & 0xff ) << 8 ) | ((b0 & 0xff )));
461
- // CHECKSTYLE:ON
462
- }
463
-
464
- private static byte int3 (final int x ) {
465
- return (byte ) (x >> 24 );
466
- }
467
-
468
- private static byte int2 (final int x ) {
469
- return (byte ) (x >> 16 );
470
- }
471
-
472
- private static byte int1 (final int x ) {
473
- return (byte ) (x >> 8 );
474
- }
475
-
476
- private static byte int0 (final int x ) {
477
- return (byte ) (x );
478
- }
479
-
480
- private static byte short1 (final short x ) {
481
- return (byte ) (x >> 8 );
482
- }
483
-
484
- private static byte short0 (final short x ) {
485
- return (byte ) (x );
486
- }
487
-
488
410
}
0 commit comments