1616
1717package org .bson .types ;
1818
19+ import static org .bson .assertions .Assertions .isTrueArgument ;
20+ import static org .bson .assertions .Assertions .notNull ;
21+
1922import java .io .InvalidObjectException ;
2023import java .io .ObjectInputStream ;
2124import java .io .Serializable ;
2225import java .nio .ByteBuffer ;
26+ import java .nio .ByteOrder ;
2327import java .security .SecureRandom ;
2428import java .util .Date ;
2529import java .util .concurrent .atomic .AtomicInteger ;
2630
27- import static org .bson .assertions .Assertions .isTrueArgument ;
28- import static org .bson .assertions .Assertions .notNull ;
29-
3031/**
3132 * <p>A globally unique identifier for objects.</p>
3233 *
@@ -53,9 +54,8 @@ public final class ObjectId implements Comparable<ObjectId>, Serializable {
5354 private static final int OBJECT_ID_LENGTH = 12 ;
5455 private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff ;
5556
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 ;
5959
6060 private static final AtomicInteger NEXT_COUNTER ;
6161
@@ -67,18 +67,12 @@ public final class ObjectId implements Comparable<ObjectId>, Serializable {
6767 * The timestamp
6868 */
6969 private final int timestamp ;
70+
7071 /**
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.
8074 */
81- private final short randomValue2 ;
75+ private final long nonce ;
8276
8377 /**
8478 * Gets a new object id.
@@ -101,7 +95,7 @@ public static ObjectId get() {
10195 * @since 4.1
10296 */
10397 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 );
10599 }
106100
107101 /**
@@ -152,7 +146,7 @@ public ObjectId() {
152146 * @param date the date
153147 */
154148 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 ) );
156150 }
157151
158152 /**
@@ -163,7 +157,7 @@ public ObjectId(final Date date) {
163157 * @throws IllegalArgumentException if the high order byte of counter is not zero
164158 */
165159 public ObjectId (final Date date , final int counter ) {
166- this (dateToTimestampSeconds (date ), counter , true );
160+ this (dateToTimestampSeconds (date ), getNonceFromUntrustedCounter ( counter ) );
167161 }
168162
169163 /**
@@ -174,25 +168,19 @@ public ObjectId(final Date date, final int counter) {
174168 * @throws IllegalArgumentException if the high order byte of counter is not zero
175169 */
176170 public ObjectId (final int timestamp , final int counter ) {
177- this (timestamp , counter , true );
171+ this (timestamp , getNonceFromUntrustedCounter ( counter ) );
178172 }
179173
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 ;
182177 }
183178
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 ) {
190181 throw new IllegalArgumentException ("The counter must be between 0 and 16777215 (it must fit in three bytes)." );
191182 }
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 ;
196184 }
197185
198186 /**
@@ -226,12 +214,14 @@ public ObjectId(final ByteBuffer buffer) {
226214 notNull ("buffer" , buffer );
227215 isTrueArgument ("buffer.remaining() >=12" , buffer .remaining () >= OBJECT_ID_LENGTH );
228216
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+ }
235225 }
236226
237227 /**
@@ -240,9 +230,11 @@ public ObjectId(final ByteBuffer buffer) {
240230 * @return the byte array
241231 */
242232 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 ();
246238 }
247239
248240 /**
@@ -257,18 +249,14 @@ public void putToByteBuffer(final ByteBuffer buffer) {
257249 notNull ("buffer" , buffer );
258250 isTrueArgument ("buffer.remaining() >=12" , buffer .remaining () >= OBJECT_ID_LENGTH );
259251
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+ }
272260 }
273261
274262 /**
@@ -313,49 +301,26 @@ public boolean equals(final Object o) {
313301 return false ;
314302 }
315303
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 ) {
326306 return false ;
327307 }
328-
329- if (randomValue2 != objectId .randomValue2 ) {
330- return false ;
331- }
332-
333- return true ;
308+ return nonce == other .nonce ;
334309 }
335310
336311 @ Override
337312 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 );
343314 }
344315
345316 @ Override
346317 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 ;
349321 }
350322
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 );
359324 }
360325
361326 @ Override
@@ -407,8 +372,7 @@ private Object readResolve() {
407372 static {
408373 try {
409374 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 ;
412376 NEXT_COUNTER = new AtomicInteger (secureRandom .nextInt ());
413377 } catch (Exception e ) {
414378 throw new RuntimeException (e );
@@ -443,46 +407,4 @@ private static int hexCharToInt(final char c) {
443407 private static int dateToTimestampSeconds (final Date time ) {
444408 return (int ) (time .getTime () / 1000 );
445409 }
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-
488410}
0 commit comments