From f24e5ed6158840f08aa95941e53522f44873c062 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Wed, 11 Dec 2024 15:28:51 -0800 Subject: [PATCH 01/14] initial commit --- bson/src/main/org/bson/types/ObjectId.java | 170 +++++------------- .../unit/org/bson/types/ObjectIdTest.java | 103 ++++++++++- 2 files changed, 140 insertions(+), 133 deletions(-) diff --git a/bson/src/main/org/bson/types/ObjectId.java b/bson/src/main/org/bson/types/ObjectId.java index 7c1b1d29540..0296c26e865 100644 --- a/bson/src/main/org/bson/types/ObjectId.java +++ b/bson/src/main/org/bson/types/ObjectId.java @@ -16,17 +16,18 @@ package org.bson.types; +import static org.bson.assertions.Assertions.isTrueArgument; +import static org.bson.assertions.Assertions.notNull; + import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.Serializable; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.security.SecureRandom; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; -import static org.bson.assertions.Assertions.isTrueArgument; -import static org.bson.assertions.Assertions.notNull; - /** *

A globally unique identifier for objects.

* @@ -53,9 +54,8 @@ public final class ObjectId implements Comparable, Serializable { private static final int OBJECT_ID_LENGTH = 12; private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff; - // Use primitives to represent the 5-byte random value. - private static final int RANDOM_VALUE1; - private static final short RANDOM_VALUE2; + // Use upper bytes of a long to represent the 5-byte random value. + private static final long RANDOM_VALUE; private static final AtomicInteger NEXT_COUNTER; @@ -67,18 +67,12 @@ public final class ObjectId implements Comparable, Serializable { * The timestamp */ private final int timestamp; + /** - * The counter. - */ - private final int counter; - /** - * the first four bits of randomness. - */ - private final int randomValue1; - /** - * The last two bits of randomness. + * The final 8 bytes of the ObjectID are 5 bytes probabilistically unique to the machine and + * process, followed by a 3 byte incrementing counter initialized to a random value. */ - private final short randomValue2; + private final long nonce; /** * Gets a new object id. @@ -101,7 +95,7 @@ public static ObjectId get() { * @since 4.1 */ public static ObjectId getSmallestWithDate(final Date date) { - return new ObjectId(dateToTimestampSeconds(date), 0, (short) 0, 0, false); + return new ObjectId(dateToTimestampSeconds(date), 0L); } /** @@ -152,7 +146,7 @@ public ObjectId() { * @param date the date */ public ObjectId(final Date date) { - this(dateToTimestampSeconds(date), NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES, false); + this(dateToTimestampSeconds(date), RANDOM_VALUE | (NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES)); } /** @@ -163,7 +157,7 @@ public ObjectId(final Date date) { * @throws IllegalArgumentException if the high order byte of counter is not zero */ public ObjectId(final Date date, final int counter) { - this(dateToTimestampSeconds(date), counter, true); + this(dateToTimestampSeconds(date), getNonceFromUntrustedCounter(counter)); } /** @@ -174,25 +168,19 @@ public ObjectId(final Date date, final int counter) { * @throws IllegalArgumentException if the high order byte of counter is not zero */ public ObjectId(final int timestamp, final int counter) { - this(timestamp, counter, true); + this(timestamp, getNonceFromUntrustedCounter(counter)); } - private ObjectId(final int timestamp, final int counter, final boolean checkCounter) { - this(timestamp, RANDOM_VALUE1, RANDOM_VALUE2, counter, checkCounter); + private ObjectId(final int timestamp, final long nonce) { + this.timestamp = timestamp; + this.nonce = nonce; } - private ObjectId(final int timestamp, final int randomValue1, final short randomValue2, final int counter, - final boolean checkCounter) { - if ((randomValue1 & 0xff000000) != 0) { - throw new IllegalArgumentException("The random value must be between 0 and 16777215 (it must fit in three bytes)."); - } - if (checkCounter && ((counter & 0xff000000) != 0)) { + private static long getNonceFromUntrustedCounter(final int counter) { + if ((counter & 0xff000000) != 0) { throw new IllegalArgumentException("The counter must be between 0 and 16777215 (it must fit in three bytes)."); } - this.timestamp = timestamp; - this.counter = counter & LOW_ORDER_THREE_BYTES; - this.randomValue1 = randomValue1; - this.randomValue2 = randomValue2; + return RANDOM_VALUE | counter; } /** @@ -226,12 +214,11 @@ public ObjectId(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); - // Note: Cannot use ByteBuffer.getInt because it depends on tbe buffer's byte order - // and ObjectId's are always in big-endian order. - timestamp = makeInt(buffer.get(), buffer.get(), buffer.get(), buffer.get()); - randomValue1 = makeInt((byte) 0, buffer.get(), buffer.get(), buffer.get()); - randomValue2 = makeShort(buffer.get(), buffer.get()); - counter = makeInt((byte) 0, buffer.get(), buffer.get(), buffer.get()); + ByteOrder originalOrder = buffer.order(); + buffer.order(ByteOrder.BIG_ENDIAN); + this.timestamp = buffer.getInt(); + this.nonce = buffer.getLong(); + buffer.order(originalOrder); } /** @@ -240,9 +227,8 @@ public ObjectId(final ByteBuffer buffer) { * @return the byte array */ public byte[] toByteArray() { - ByteBuffer buffer = ByteBuffer.allocate(OBJECT_ID_LENGTH); - putToByteBuffer(buffer); - return buffer.array(); // using .allocate ensures there is a backing array that can be returned + // using .allocate ensures there is a backing array that can be returned + return ByteBuffer.allocate(OBJECT_ID_LENGTH).putInt(this.timestamp).putLong(this.nonce).array(); } /** @@ -257,18 +243,12 @@ public void putToByteBuffer(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); - buffer.put(int3(timestamp)); - buffer.put(int2(timestamp)); - buffer.put(int1(timestamp)); - buffer.put(int0(timestamp)); - buffer.put(int2(randomValue1)); - buffer.put(int1(randomValue1)); - buffer.put(int0(randomValue1)); - buffer.put(short1(randomValue2)); - buffer.put(short0(randomValue2)); - buffer.put(int2(counter)); - buffer.put(int1(counter)); - buffer.put(int0(counter)); + ByteOrder originalOrder = buffer.order(); + buffer + .order(ByteOrder.BIG_ENDIAN) + .putInt(this.timestamp) + .putLong(this.nonce) + .order(originalOrder); } /** @@ -313,49 +293,26 @@ public boolean equals(final Object o) { return false; } - ObjectId objectId = (ObjectId) o; - - if (counter != objectId.counter) { - return false; - } - if (timestamp != objectId.timestamp) { - return false; - } - - if (randomValue1 != objectId.randomValue1) { - return false; - } - - if (randomValue2 != objectId.randomValue2) { + ObjectId other = (ObjectId) o; + if (timestamp != other.timestamp) { return false; } - - return true; + return nonce == other.nonce; } @Override public int hashCode() { - int result = timestamp; - result = 31 * result + counter; - result = 31 * result + randomValue1; - result = 31 * result + randomValue2; - return result; + return 31 * timestamp + Long.hashCode(nonce); } @Override public int compareTo(final ObjectId other) { - if (other == null) { - throw new NullPointerException(); + int cmp = Integer.compareUnsigned(this.timestamp, other.timestamp); + if (cmp != 0) { + return cmp; } - byte[] byteArray = toByteArray(); - byte[] otherByteArray = other.toByteArray(); - for (int i = 0; i < OBJECT_ID_LENGTH; i++) { - if (byteArray[i] != otherByteArray[i]) { - return ((byteArray[i] & 0xff) < (otherByteArray[i] & 0xff)) ? -1 : 1; - } - } - return 0; + return Long.compareUnsigned(nonce, other.nonce); } @Override @@ -407,8 +364,7 @@ private Object readResolve() { static { try { SecureRandom secureRandom = new SecureRandom(); - RANDOM_VALUE1 = secureRandom.nextInt(0x01000000); - RANDOM_VALUE2 = (short) secureRandom.nextInt(0x00008000); + RANDOM_VALUE = secureRandom.nextLong() & ~LOW_ORDER_THREE_BYTES; NEXT_COUNTER = new AtomicInteger(secureRandom.nextInt()); } catch (Exception e) { throw new RuntimeException(e); @@ -443,46 +399,4 @@ private static int hexCharToInt(final char c) { private static int dateToTimestampSeconds(final Date time) { return (int) (time.getTime() / 1000); } - - // Big-Endian helpers, in this class because all other BSON numbers are little-endian - - private static int makeInt(final byte b3, final byte b2, final byte b1, final byte b0) { - // CHECKSTYLE:OFF - return (((b3) << 24) | - ((b2 & 0xff) << 16) | - ((b1 & 0xff) << 8) | - ((b0 & 0xff))); - // CHECKSTYLE:ON - } - - private static short makeShort(final byte b1, final byte b0) { - // CHECKSTYLE:OFF - return (short) (((b1 & 0xff) << 8) | ((b0 & 0xff))); - // CHECKSTYLE:ON - } - - private static byte int3(final int x) { - return (byte) (x >> 24); - } - - private static byte int2(final int x) { - return (byte) (x >> 16); - } - - private static byte int1(final int x) { - return (byte) (x >> 8); - } - - private static byte int0(final int x) { - return (byte) (x); - } - - private static byte short1(final short x) { - return (byte) (x >> 8); - } - - private static byte short0(final short x) { - return (byte) (x); - } - } diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 14c8241f55a..c628d777797 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -17,6 +17,8 @@ package org.bson.types; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -24,29 +26,54 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Random; +import static org.junit.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; public class ObjectIdTest { - @Test - public void testToBytes() { + + /** + * MethodSource for valid ByteBuffers that can hold an ObjectID + */ + public static List outputBuffers() { + List result = new ArrayList<>(); + result.add(ByteBuffer.allocate(12)); + result.add(ByteBuffer.allocate(12).order(ByteOrder.LITTLE_ENDIAN)); + result.add(ByteBuffer.allocate(24).position(12)); + return result; + } + + @MethodSource("outputBuffers") + @ParameterizedTest + public void testToBytes(final ByteBuffer output) { + int originalPosition = output.position(); + ByteOrder originalOrder = output.order(); byte[] expectedBytes = {81, 6, -4, -102, -68, -126, 55, 85, -127, 54, -46, -119}; + byte[] result = new byte[12]; ObjectId objectId = new ObjectId(expectedBytes); assertArrayEquals(expectedBytes, objectId.toByteArray()); - ByteBuffer buffer = ByteBuffer.allocate(12); - objectId.putToByteBuffer(buffer); - assertArrayEquals(expectedBytes, buffer.array()); + objectId.putToByteBuffer(output); + output.position(output.position() - result.length); // read last 12 bytes leaving position intact + output.get(result); + + assertArrayEquals(expectedBytes, result); + assertEquals(originalPosition + 12, output.position()); + assertEquals(originalOrder, output.order()); } @Test @@ -140,6 +167,55 @@ public void testDateCons() { assertEquals(new Date().getTime() / 1000, new ObjectId(new Date()).getDate().getTime() / 1000); } + @Test + public void testRandomConstructors() { + assertNotEquals(new ObjectId(new Date(1_000)), new ObjectId(new Date(1_000))); + assertEquals(new ObjectId(new Date(1_000), 1), new ObjectId(new Date(1_000), 1)); + assertEquals(new ObjectId(1_000, 1), new ObjectId(1_000, 1)); + + assertEquals("00000001", new ObjectId(new Date(1_000)).toHexString().substring(0, 8)); + assertEquals("00000001", new ObjectId(new Date(1_000), 1).toHexString().substring(0, 8)); + assertEquals("7fffffff", new ObjectId(Integer.MAX_VALUE, 1).toHexString().substring(0, 8)); + + assertThrows(NullPointerException.class, () -> new ObjectId(null, Integer.MAX_VALUE)); + assertThrows(IllegalArgumentException.class, () -> new ObjectId(new Date(1_000), Integer.MAX_VALUE)); + assertThrows(IllegalArgumentException.class, () -> new ObjectId(Integer.MAX_VALUE, Integer.MAX_VALUE)); + assertThrows(IllegalArgumentException.class, () -> new ObjectId((ByteBuffer) null)); + assertThrows(IllegalArgumentException.class, () -> new ObjectId(ByteBuffer.allocate(11))); + } + + /** + * MethodSource for valid ByteBuffers containing an ObjectID at the current position. + */ + public static List inputBuffers() { + byte[] data = new byte[12]; + for (byte i = 0; i < data.length; ++i) { + data[i] = i; + } + + List result = new ArrayList<>(); + result.add(ByteBuffer.wrap(data)); + result.add(ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)); + result.add(ByteBuffer.allocateDirect(data.length).put(data).rewind()); + result.add(ByteBuffer.allocateDirect(data.length).put(data).order(ByteOrder.LITTLE_ENDIAN).rewind()); + result.add(ByteBuffer.allocate(2 * data.length).put(data).rewind()); + result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).put(data).position(data.length)); + return result; + } + + @ParameterizedTest + @MethodSource(value = "inputBuffers") + public void testByteBufferCons(final ByteBuffer input) { + ByteOrder order = input.order(); + int position = input.position(); + + byte[] result = new ObjectId(input).toByteArray(); + + assertArrayEquals(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, result); + assertEquals(order, input.order()); + assertEquals(position + 12, input.position()); + } + @Test public void testHexStringConstructor() { ObjectId id = new ObjectId(); @@ -162,6 +238,23 @@ public void testCompareTo() { assertEquals(-1, first.compareTo(third)); assertEquals(1, second.compareTo(first)); assertEquals(1, third.compareTo(first)); + assertThrows(NullPointerException.class, () -> first.compareTo(null)); + } + + @Test + public void testEquals() { + Date dateOne = new Date(); + Date dateTwo = new Date(dateOne.getTime() + 10000); + ObjectId first = new ObjectId(dateOne, 0); + ObjectId second = new ObjectId(dateOne, 1); + ObjectId third = new ObjectId(dateTwo, 0); + ObjectId fourth = new ObjectId(first.toByteArray()); + assertEquals(first, first); + assertEquals(first, fourth); + assertNotEquals(first, second); + assertNotEquals(first, third); + assertNotEquals(second, third); + assertFalse(first.equals(null)); } @Test From 8cc55ed1e9893f688e1aa036836d45c55b5d3090 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Thu, 12 Dec 2024 12:18:58 -0800 Subject: [PATCH 02/14] Remove reference to jdk9 API --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index c628d777797..2d76a8e674e 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.ParseException; @@ -52,7 +53,7 @@ public static List outputBuffers() { List result = new ArrayList<>(); result.add(ByteBuffer.allocate(12)); result.add(ByteBuffer.allocate(12).order(ByteOrder.LITTLE_ENDIAN)); - result.add(ByteBuffer.allocate(24).position(12)); + result.add(ByteBuffer.allocate(24).put(new byte[12])); // Avoid ByteBuffer.position(int) on jdk8 return result; } @@ -68,7 +69,7 @@ public void testToBytes(final ByteBuffer output) { assertArrayEquals(expectedBytes, objectId.toByteArray()); objectId.putToByteBuffer(output); - output.position(output.position() - result.length); // read last 12 bytes leaving position intact + ((Buffer) output).position(output.position() - result.length); // read last 12 bytes leaving position intact output.get(result); assertArrayEquals(expectedBytes, result); @@ -199,7 +200,7 @@ public static List inputBuffers() { result.add(ByteBuffer.allocateDirect(data.length).put(data).rewind()); result.add(ByteBuffer.allocateDirect(data.length).put(data).order(ByteOrder.LITTLE_ENDIAN).rewind()); result.add(ByteBuffer.allocate(2 * data.length).put(data).rewind()); - result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).put(data).position(data.length)); + result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).mark().put(data).reset()); return result; } From 5c20cc3233e5152ff128eb0365fd432d509bdfa4 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Thu, 12 Dec 2024 12:36:15 -0800 Subject: [PATCH 03/14] cleanup test --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 2d76a8e674e..5415bdc9a36 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -68,9 +68,9 @@ public void testToBytes(final ByteBuffer output) { assertArrayEquals(expectedBytes, objectId.toByteArray()); + output.mark(); objectId.putToByteBuffer(output); - ((Buffer) output).position(output.position() - result.length); // read last 12 bytes leaving position intact - output.get(result); + output.reset().get(result); // read last 12 bytes leaving position intact assertArrayEquals(expectedBytes, result); assertEquals(originalPosition + 12, output.position()); From a3668e5f5e85ccd0fc3e8440d37ae91e670ed10c Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Thu, 12 Dec 2024 13:03:00 -0800 Subject: [PATCH 04/14] remove unused import --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 5415bdc9a36..01ec11417bf 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.ParseException; From 29b0ab3b7fb461914f7b1a710e2dea68e1c91ab4 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Thu, 12 Dec 2024 14:06:45 -0800 Subject: [PATCH 05/14] remove usages of buffer.mark() for jdk 8 --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 01ec11417bf..b0a80918bde 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.text.ParseException; @@ -67,9 +68,9 @@ public void testToBytes(final ByteBuffer output) { assertArrayEquals(expectedBytes, objectId.toByteArray()); - output.mark(); objectId.putToByteBuffer(output); - output.reset().get(result); // read last 12 bytes leaving position intact + ((Buffer) output).position(output.position() - 12); + output.get(result); // read last 12 bytes leaving position intact assertArrayEquals(expectedBytes, result); assertEquals(originalPosition + 12, output.position()); @@ -199,7 +200,7 @@ public static List inputBuffers() { result.add(ByteBuffer.allocateDirect(data.length).put(data).rewind()); result.add(ByteBuffer.allocateDirect(data.length).put(data).order(ByteOrder.LITTLE_ENDIAN).rewind()); result.add(ByteBuffer.allocate(2 * data.length).put(data).rewind()); - result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).mark().put(data).reset()); + result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).put(data).rewind().put(new byte[12])); return result; } From a80b605e5396eb61bd2884b949592c42a5b0379d Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Thu, 12 Dec 2024 14:47:44 -0800 Subject: [PATCH 06/14] remove references to rewind for jdk8 tests --- .../test/unit/org/bson/types/ObjectIdTest.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index b0a80918bde..1110ec70a5e 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -46,6 +46,12 @@ public class ObjectIdTest { + /** Calls the base method of ByteBuffer.position(int) since the override is not available in jdk8. */ + private static ByteBuffer setPosition(final ByteBuffer buf, final int pos) { + ((Buffer) buf).position(pos); + return buf; + } + /** * MethodSource for valid ByteBuffers that can hold an ObjectID */ @@ -53,7 +59,9 @@ public static List outputBuffers() { List result = new ArrayList<>(); result.add(ByteBuffer.allocate(12)); result.add(ByteBuffer.allocate(12).order(ByteOrder.LITTLE_ENDIAN)); - result.add(ByteBuffer.allocate(24).put(new byte[12])); // Avoid ByteBuffer.position(int) on jdk8 + result.add(ByteBuffer.allocate(24).put(new byte[12])); + result.add(ByteBuffer.allocateDirect(12)); + result.add(ByteBuffer.allocateDirect(12).order(ByteOrder.LITTLE_ENDIAN)); return result; } @@ -197,10 +205,10 @@ public static List inputBuffers() { List result = new ArrayList<>(); result.add(ByteBuffer.wrap(data)); result.add(ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN)); - result.add(ByteBuffer.allocateDirect(data.length).put(data).rewind()); - result.add(ByteBuffer.allocateDirect(data.length).put(data).order(ByteOrder.LITTLE_ENDIAN).rewind()); - result.add(ByteBuffer.allocate(2 * data.length).put(data).rewind()); - result.add(ByteBuffer.allocate(2 * data.length).put(new byte[12]).put(data).rewind().put(new byte[12])); + result.add(setPosition(ByteBuffer.allocateDirect(data.length).put(data), 0)); + result.add(setPosition(ByteBuffer.allocateDirect(data.length).put(data).order(ByteOrder.LITTLE_ENDIAN), 0)); + result.add(setPosition(ByteBuffer.allocate(2 * data.length).put(data), 0)); + result.add(setPosition(ByteBuffer.allocate(2 * data.length).put(new byte[12]).put(data), 12)); return result; } From 3752712e90a7b72c0b1a96f18078d4c9ec4774d6 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:18:10 -0800 Subject: [PATCH 07/14] Update bson/src/main/org/bson/types/ObjectId.java Co-authored-by: Viacheslav Babanin --- bson/src/main/org/bson/types/ObjectId.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bson/src/main/org/bson/types/ObjectId.java b/bson/src/main/org/bson/types/ObjectId.java index 0296c26e865..5a5e944519a 100644 --- a/bson/src/main/org/bson/types/ObjectId.java +++ b/bson/src/main/org/bson/types/ObjectId.java @@ -214,11 +214,14 @@ public ObjectId(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); - ByteOrder originalOrder = buffer.order(); - buffer.order(ByteOrder.BIG_ENDIAN); - this.timestamp = buffer.getInt(); - this.nonce = buffer.getLong(); - buffer.order(originalOrder); + ByteOrder originalOrder = buffer.order(); + try { + buffer.order(ByteOrder.BIG_ENDIAN); + this.timestamp = buffer.getInt(); + this.nonce = buffer.getLong(); + } finally { + buffer.order(originalOrder); + } } /** From f0b9b1a4ee89e57469319ccb2feed689329ac60f Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:18:19 -0800 Subject: [PATCH 08/14] Update bson/src/main/org/bson/types/ObjectId.java Co-authored-by: Viacheslav Babanin --- bson/src/main/org/bson/types/ObjectId.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bson/src/main/org/bson/types/ObjectId.java b/bson/src/main/org/bson/types/ObjectId.java index 5a5e944519a..8791443e1b5 100644 --- a/bson/src/main/org/bson/types/ObjectId.java +++ b/bson/src/main/org/bson/types/ObjectId.java @@ -246,12 +246,14 @@ public void putToByteBuffer(final ByteBuffer buffer) { notNull("buffer", buffer); isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); - ByteOrder originalOrder = buffer.order(); - buffer - .order(ByteOrder.BIG_ENDIAN) - .putInt(this.timestamp) - .putLong(this.nonce) - .order(originalOrder); + ByteOrder originalOrder = buffer.order(); + try{ + buffer.order(ByteOrder.BIG_ENDIAN); + buffer.putInt(this.timestamp); + buffer.putLong(this.nonce); + } finally { + buffer.order(originalOrder); + } } /** From d99d3f07f7a53259eec057314a55cd3dde9f240f Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:18:35 -0800 Subject: [PATCH 09/14] Update bson/src/main/org/bson/types/ObjectId.java Co-authored-by: Viacheslav Babanin --- bson/src/main/org/bson/types/ObjectId.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bson/src/main/org/bson/types/ObjectId.java b/bson/src/main/org/bson/types/ObjectId.java index 8791443e1b5..d4602b6d95d 100644 --- a/bson/src/main/org/bson/types/ObjectId.java +++ b/bson/src/main/org/bson/types/ObjectId.java @@ -231,7 +231,10 @@ public ObjectId(final ByteBuffer buffer) { */ public byte[] toByteArray() { // using .allocate ensures there is a backing array that can be returned - return ByteBuffer.allocate(OBJECT_ID_LENGTH).putInt(this.timestamp).putLong(this.nonce).array(); + return ByteBuffer.allocate(OBJECT_ID_LENGTH) + .putInt(this.timestamp) + .putLong(this.nonce) + .array(); } /** From 79942778bdabe062e1a17334d102448ddd35afcd Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:18:59 -0800 Subject: [PATCH 10/14] Update bson/src/test/unit/org/bson/types/ObjectIdTest.java Co-authored-by: Viacheslav Babanin --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 1110ec70a5e..6626b6643a4 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -55,7 +55,7 @@ private static ByteBuffer setPosition(final ByteBuffer buf, final int pos) { /** * MethodSource for valid ByteBuffers that can hold an ObjectID */ - public static List outputBuffers() { + public static List validOutputBuffers() { List result = new ArrayList<>(); result.add(ByteBuffer.allocate(12)); result.add(ByteBuffer.allocate(12).order(ByteOrder.LITTLE_ENDIAN)); From 964a52117d5533c45abe3c82fbe144edf2a199c9 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:35:39 -0800 Subject: [PATCH 11/14] Update bson/src/test/unit/org/bson/types/ObjectIdTest.java Co-authored-by: Viacheslav Babanin --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 6626b6643a4..b1ebc8281c1 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -196,7 +196,7 @@ public void testRandomConstructors() { /** * MethodSource for valid ByteBuffers containing an ObjectID at the current position. */ - public static List inputBuffers() { + public static List validInputBuffers() { byte[] data = new byte[12]; for (byte i = 0; i < data.length; ++i) { data[i] = i; From ae364a774e36f9741c6320146444255f86b161c2 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 18:35:50 -0800 Subject: [PATCH 12/14] Update bson/src/test/unit/org/bson/types/ObjectIdTest.java Co-authored-by: Viacheslav Babanin --- bson/src/test/unit/org/bson/types/ObjectIdTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index b1ebc8281c1..2c21847ef80 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -214,7 +214,7 @@ public static List validInputBuffers() { @ParameterizedTest @MethodSource(value = "inputBuffers") - public void testByteBufferCons(final ByteBuffer input) { + public void testByteBufferConstructor(final ByteBuffer input) { ByteOrder order = input.order(); int position = input.position(); From c84e0d950363558653ede6a68ca9070236fc44f3 Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 19:43:28 -0800 Subject: [PATCH 13/14] group ObjectID constructor tests by signature --- .../unit/org/bson/types/ObjectIdTest.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/bson/src/test/unit/org/bson/types/ObjectIdTest.java b/bson/src/test/unit/org/bson/types/ObjectIdTest.java index 2c21847ef80..cfe04623b90 100644 --- a/bson/src/test/unit/org/bson/types/ObjectIdTest.java +++ b/bson/src/test/unit/org/bson/types/ObjectIdTest.java @@ -65,7 +65,7 @@ public static List validOutputBuffers() { return result; } - @MethodSource("outputBuffers") + @MethodSource("validOutputBuffers") @ParameterizedTest public void testToBytes(final ByteBuffer output) { int originalPosition = output.position(); @@ -172,25 +172,26 @@ public void testTime() { } @Test - public void testDateCons() { + public void testDateConstructor() { assertEquals(new Date().getTime() / 1000, new ObjectId(new Date()).getDate().getTime() / 1000); + assertNotEquals(new ObjectId(new Date(1_000)), new ObjectId(new Date(1_000))); + assertEquals("00000001", new ObjectId(new Date(1_000)).toHexString().substring(0, 8)); } @Test - public void testRandomConstructors() { - assertNotEquals(new ObjectId(new Date(1_000)), new ObjectId(new Date(1_000))); + public void testDateConstructorWithCounter() { assertEquals(new ObjectId(new Date(1_000), 1), new ObjectId(new Date(1_000), 1)); - assertEquals(new ObjectId(1_000, 1), new ObjectId(1_000, 1)); - - assertEquals("00000001", new ObjectId(new Date(1_000)).toHexString().substring(0, 8)); assertEquals("00000001", new ObjectId(new Date(1_000), 1).toHexString().substring(0, 8)); - assertEquals("7fffffff", new ObjectId(Integer.MAX_VALUE, 1).toHexString().substring(0, 8)); - assertThrows(NullPointerException.class, () -> new ObjectId(null, Integer.MAX_VALUE)); assertThrows(IllegalArgumentException.class, () -> new ObjectId(new Date(1_000), Integer.MAX_VALUE)); + } + + @Test + public void testTimestampConstructor() { + assertEquals(1_000, new ObjectId(1_000, 1).getTimestamp()); + assertEquals(new ObjectId(1_000, 1), new ObjectId(1_000, 1)); + assertEquals("7fffffff", new ObjectId(Integer.MAX_VALUE, 1).toHexString().substring(0, 8)); assertThrows(IllegalArgumentException.class, () -> new ObjectId(Integer.MAX_VALUE, Integer.MAX_VALUE)); - assertThrows(IllegalArgumentException.class, () -> new ObjectId((ByteBuffer) null)); - assertThrows(IllegalArgumentException.class, () -> new ObjectId(ByteBuffer.allocate(11))); } /** @@ -213,7 +214,7 @@ public static List validInputBuffers() { } @ParameterizedTest - @MethodSource(value = "inputBuffers") + @MethodSource(value = "validInputBuffers") public void testByteBufferConstructor(final ByteBuffer input) { ByteOrder order = input.order(); int position = input.position(); @@ -225,6 +226,12 @@ public void testByteBufferConstructor(final ByteBuffer input) { assertEquals(position + 12, input.position()); } + @Test + public void testInvalidByteBufferConstructor() { + assertThrows(IllegalArgumentException.class, () -> new ObjectId((ByteBuffer) null)); + assertThrows(IllegalArgumentException.class, () -> new ObjectId(ByteBuffer.allocate(11))); + } + @Test public void testHexStringConstructor() { ObjectId id = new ObjectId(); From b3db4551d791b2eb19292216c096614576eb9aba Mon Sep 17 00:00:00 2001 From: Evan Darke Date: Mon, 3 Feb 2025 20:07:44 -0800 Subject: [PATCH 14/14] reformat objectid --- bson/src/main/org/bson/types/ObjectId.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bson/src/main/org/bson/types/ObjectId.java b/bson/src/main/org/bson/types/ObjectId.java index d4602b6d95d..927d3ab0c31 100644 --- a/bson/src/main/org/bson/types/ObjectId.java +++ b/bson/src/main/org/bson/types/ObjectId.java @@ -250,7 +250,7 @@ public void putToByteBuffer(final ByteBuffer buffer) { isTrueArgument("buffer.remaining() >=12", buffer.remaining() >= OBJECT_ID_LENGTH); ByteOrder originalOrder = buffer.order(); - try{ + try { buffer.order(ByteOrder.BIG_ENDIAN); buffer.putInt(this.timestamp); buffer.putLong(this.nonce);