diff --git a/bson/src/main/org/bson/ByteBuf.java b/bson/src/main/org/bson/ByteBuf.java
index e44a97dfc67..089bb67885c 100644
--- a/bson/src/main/org/bson/ByteBuf.java
+++ b/bson/src/main/org/bson/ByteBuf.java
@@ -106,6 +106,54 @@ public interface ByteBuf {
*/
ByteBuf put(byte b);
+ /**
+ * Writes the given int value into this buffer at the current position,
+ * using the current byte order, and increments the position by 4.
+ *
+ * @param b the int value to be written
+ * @return this buffer
+ * @throws java.nio.BufferOverflowException if there are fewer than 4 bytes remaining in this buffer
+ * @throws java.nio.ReadOnlyBufferException if this buffer is read-only
+ * @since 5.4
+ */
+ ByteBuf putInt(int b);
+
+ /**
+ * Writes the given int value into this buffer at the current position,
+ * using the current byte order, and increments the position by 4.
+ *
+ * @param b the int value to be written
+ * @return this buffer
+ * @throws java.nio.BufferOverflowException if there are fewer than 4 bytes remaining in this buffer
+ * @throws java.nio.ReadOnlyBufferException if this buffer is read-only
+ * @since 5.4
+ */
+ ByteBuf putInt(int index, int b);
+
+ /**
+ * Writes the given double value into this buffer at the current position,
+ * using the current byte order, and increments the position by 8.
+ *
+ * @param b the double value to be written
+ * @return this buffer
+ * @throws java.nio.BufferOverflowException if there are fewer than 8 bytes remaining in this buffer
+ * @throws java.nio.ReadOnlyBufferException if this buffer is read-only
+ * @since 5.4
+ */
+ ByteBuf putDouble(double b);
+
+ /**
+ * Writes the given long value into this buffer at the current position,
+ * using the current byte order, and increments the position by 8.
+ *
+ * @param b the long value to be written
+ * @return this buffer
+ * @throws java.nio.BufferOverflowException if there are fewer than 8 bytes remaining in this buffer
+ * @throws java.nio.ReadOnlyBufferException if this buffer is read-only
+ * @since 5.4
+ */
+ ByteBuf putLong(long b);
+
/**
*
Flips this buffer. The limit is set to the current position and then the position is set to zero. If the mark is defined then it
* is discarded.
diff --git a/bson/src/main/org/bson/ByteBufNIO.java b/bson/src/main/org/bson/ByteBufNIO.java
index 83bfa7d893a..ffb6584ac64 100644
--- a/bson/src/main/org/bson/ByteBufNIO.java
+++ b/bson/src/main/org/bson/ByteBufNIO.java
@@ -97,6 +97,30 @@ public ByteBuf put(final byte b) {
return this;
}
+ @Override
+ public ByteBuf putInt(final int b) {
+ buf.putInt(b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putInt(final int index, final int b) {
+ buf.putInt(index, b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putDouble(final double b) {
+ buf.putDouble(b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putLong(final long b) {
+ buf.putLong(b);
+ return this;
+ }
+
@Override
public ByteBuf flip() {
((Buffer) buf).flip();
@@ -160,8 +184,13 @@ public ByteBuf get(final byte[] bytes, final int offset, final int length) {
@Override
public ByteBuf get(final int index, final byte[] bytes, final int offset, final int length) {
- for (int i = 0; i < length; i++) {
- bytes[offset + i] = buf.get(index + i);
+ if (buf.hasArray()) {
+ System.arraycopy(buf.array(), index, bytes, offset, length);
+ } else {
+ // Fallback to per-byte copying if no backing array is available.
+ for (int i = 0; i < length; i++) {
+ bytes[offset + i] = buf.get(index + i);
+ }
}
return this;
}
diff --git a/bson/src/main/org/bson/io/OutputBuffer.java b/bson/src/main/org/bson/io/OutputBuffer.java
index 00f88cea706..7c1a64b2f85 100644
--- a/bson/src/main/org/bson/io/OutputBuffer.java
+++ b/bson/src/main/org/bson/io/OutputBuffer.java
@@ -70,6 +70,7 @@ public void writeInt32(final int value) {
}
@Override
+ @Deprecated
public void writeInt32(final int position, final int value) {
write(position, value >> 0);
write(position + 1, value >> 8);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/ByteBufferBsonOutput.java b/driver-core/src/main/com/mongodb/internal/connection/ByteBufferBsonOutput.java
index 40df1b867fd..d53ffe7c683 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/ByteBufferBsonOutput.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/ByteBufferBsonOutput.java
@@ -82,6 +82,84 @@ public void writeBytes(final byte[] bytes, final int offset, final int length) {
position += length;
}
+ @Override
+ public void writeInt32(final int value) {
+ ensureOpen();
+ ByteBuf buf = getCurrentByteBuffer();
+ if (buf.remaining() >= 4) {
+ buf.putInt(value);
+ position += 4;
+ } else {
+ // fallback for edge cases
+ super.writeInt32(value);
+ }
+ }
+
+
+ @Override
+ public void writeInt32(final int absolutePosition, final int value) {
+ ensureOpen();
+
+ if (absolutePosition < 0) {
+ throw new IllegalArgumentException(String.format("position must be >= 0 but was %d", absolutePosition));
+ }
+
+ if (absolutePosition + 3 > position - 1) {
+ throw new IllegalArgumentException(String.format("Cannot write 4 bytes starting at position %d: current size is %d bytes",
+ position - 1,
+ absolutePosition + 3));
+ }
+
+ BufferPositionPair bufferPositionPair = getBufferPositionPair(absolutePosition);
+ ByteBuf byteBuffer = getByteBufferAtIndex(bufferPositionPair.bufferIndex);
+ int capacity = byteBuffer.position() - bufferPositionPair.position;
+
+ if (capacity >= 4) {
+ byteBuffer.putInt(bufferPositionPair.position, value);
+ } else {
+ // fallback for edge cases
+ int valueToWrite = value;
+ int pos = bufferPositionPair.position;
+ int bufferIndex = bufferPositionPair.bufferIndex;
+
+ for (int i = 0; i < 4; i++) {
+ byteBuffer.put(pos++, (byte) valueToWrite);
+ valueToWrite = valueToWrite >> 8;
+ if (--capacity == 0) {
+ byteBuffer = getByteBufferAtIndex(++bufferIndex);
+ pos = 0;
+ capacity = byteBuffer.position();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void writeDouble(final double value) {
+ ensureOpen();
+ ByteBuf buf = getCurrentByteBuffer();
+ if (buf.remaining() >= 8) {
+ buf.putDouble(value);
+ position += 8;
+ } else {
+ // fallback for edge cases
+ writeInt64(Double.doubleToRawLongBits(value));
+ }
+ }
+
+ @Override
+ public void writeInt64(final long value) {
+ ensureOpen();
+ ByteBuf buf = getCurrentByteBuffer();
+ if (buf.remaining() >= 8) {
+ buf.putLong(value);
+ position += 8;
+ } else {
+ // fallback for edge cases
+ super.writeInt64(value);
+ }
+ }
+
@Override
public void writeByte(final int value) {
ensureOpen();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java b/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
index fa8cde2e517..47545753367 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java
@@ -237,6 +237,26 @@ public ByteBuf put(final byte b) {
throw new UnsupportedOperationException();
}
+ @Override
+ public ByteBuf putInt(final int b) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ByteBuf putInt(final int index, final int b) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ByteBuf putDouble(final double b) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ByteBuf putLong(final long b) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public ByteBuf flip() {
throw new UnsupportedOperationException();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java b/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
index 074e77de04f..cb6ba587419 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java
@@ -89,6 +89,30 @@ public ByteBuf put(final byte b) {
return this;
}
+ @Override
+ public ByteBuf putInt(final int b) {
+ proxied.writeInt(b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putInt(final int index, final int b) {
+ proxied.setInt(index, b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putDouble(final double b) {
+ proxied.writeDouble(b);
+ return this;
+ }
+
+ @Override
+ public ByteBuf putLong(final long b) {
+ proxied.writeLong(b);
+ return this;
+ }
+
@Override
public ByteBuf flip() {
isWriting = !isWriting;
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufTest.java
new file mode 100644
index 00000000000..722d7d62fa4
--- /dev/null
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb.internal.connection;
+
+
+import org.bson.ByteBuf;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+class ByteBufTest {
+
+ static Stream bufferProviders() {
+ return Stream.of(new ByteBufSpecification.NettyBufferProvider(), new SimpleBufferProvider());
+ }
+
+ @ParameterizedTest
+ @MethodSource("bufferProviders")
+ void shouldPutInt(final BufferProvider provider) {
+ ByteBuf buffer = provider.getBuffer(1024);
+ try {
+ buffer.putInt(42);
+ buffer.flip();
+ assertEquals(42, buffer.getInt());
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("bufferProviders")
+ void shouldPutLong(final BufferProvider provider) {
+ ByteBuf buffer = provider.getBuffer(1024);
+ try {
+ buffer.putLong(42L);
+ buffer.flip();
+ assertEquals(42L, buffer.getLong());
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("bufferProviders")
+ void shouldPutDouble(final BufferProvider provider) {
+ ByteBuf buffer = provider.getBuffer(1024);
+ try {
+ buffer.putDouble(42.0D);
+ buffer.flip();
+ assertEquals(42.0D, buffer.getDouble());
+ } finally {
+ buffer.release();
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("bufferProviders")
+ void shouldPutIntAtIndex(final BufferProvider provider) {
+ ByteBuf buffer = provider.getBuffer(1024);
+ try {
+ buffer.putInt(0);
+ buffer.putInt(0);
+ buffer.putInt(0);
+ buffer.putInt(0);
+ buffer.put((byte) 43);
+ buffer.put((byte) 44);
+ buffer.putInt(0, 22);
+ buffer.putInt(4, 23);
+ buffer.putInt(8, 24);
+ buffer.putInt(12, 25);
+ buffer.flip();
+
+ assertEquals(22, buffer.getInt());
+ assertEquals(23, buffer.getInt());
+ assertEquals(24, buffer.getInt());
+ assertEquals(25, buffer.getInt());
+ assertEquals(43, buffer.get());
+ assertEquals(44, buffer.get());
+ } finally {
+ buffer.release();
+ }
+ }
+}
diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufferBsonOutputTest.java b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufferBsonOutputTest.java
index 3a8a2c83acb..560e3177360 100644
--- a/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufferBsonOutputTest.java
+++ b/driver-core/src/test/unit/com/mongodb/internal/connection/ByteBufferBsonOutputTest.java
@@ -16,14 +16,17 @@
package com.mongodb.internal.connection;
-import com.mongodb.assertions.Assertions;
import org.bson.BsonSerializationException;
import org.bson.ByteBuf;
+import org.bson.ByteBufNIO;
import org.bson.types.ObjectId;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.io.ByteArrayOutputStream;
@@ -31,10 +34,12 @@
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
+import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import static com.mongodb.assertions.Assertions.fail;
import static com.mongodb.internal.connection.ByteBufferBsonOutput.INITIAL_BUFFER_SIZE;
import static com.mongodb.internal.connection.ByteBufferBsonOutput.MAX_BUFFER_SIZE;
import static java.util.Arrays.asList;
@@ -82,7 +87,7 @@ void positionAndSizeShouldBe0AfterConstructor(final String branchState) {
break;
}
default: {
- throw Assertions.fail(branchState);
+ throw fail(branchState);
}
}
assertEquals(0, out.getPosition());
@@ -109,6 +114,51 @@ void shouldWriteByte(final boolean useBranch) {
}
}
+ @DisplayName("should write byte at position")
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ void shouldWriteByteAtPosition(final boolean useBranch) {
+ for (int offset = 0; offset < 5; offset++) {
+ try (ByteBufferBsonOutput out = new ByteBufferBsonOutput(new SimpleBufferProvider())) {
+ byte v = 11;
+ byte[] byteToWrite = {1, 2, 3, 4, 5};
+ if (useBranch) {
+ try (ByteBufferBsonOutput.Branch branch = out.branch()) {
+ branch.writeBytes(byteToWrite);
+ branch.write(offset, v);
+ }
+ } else {
+ out.writeBytes(byteToWrite);
+ out.write(offset, v);
+ }
+ byteToWrite[offset] = v;
+ assertArrayEquals(byteToWrite, out.toByteArray());
+ assertEquals(5, out.getPosition());
+ assertEquals(5, out.size());
+
+ }
+ }
+ }
+
+ @DisplayName("should throw exception when writing byte at invalid position")
+ @ParameterizedTest
+ @ValueSource(booleans = {false, true})
+ void shouldThrowExceptionWhenWriteByteAtInvalidPosition(final boolean useBranch) {
+ try (ByteBufferBsonOutput out = new ByteBufferBsonOutput(new SimpleBufferProvider())) {
+ byte v = 11;
+ byte[] byteToWrite = {1, 2, 3, 4, 5};
+ if (useBranch) {
+ try (ByteBufferBsonOutput.Branch branch = out.branch()) {
+ out.writeBytes(byteToWrite);
+ assertThrows(IllegalArgumentException.class, () -> branch.write(-1, v));
+ }
+ } else {
+ out.writeBytes(byteToWrite);
+ assertThrows(IllegalArgumentException.class, () -> out.write(-1, v));
+ }
+ }
+ }
+
@DisplayName("should write a bytes")
@ParameterizedTest
@ValueSource(booleans = {false, true})
@@ -622,4 +672,270 @@ void shouldHandleMixedBranchingAndTruncating(final int reps) throws CharacterCod
assertEquals(expected.toString(), StandardCharsets.UTF_8.newDecoder().decode(ByteBuffer.wrap(out.toByteArray())).toString());
}
}
+
+ @Test
+ @DisplayName("should throw exception when calling writeInt32 at absolute position where integer would not fit")
+ void shouldThrowExceptionWhenIntegerDoesNotFitWriteInt32() {
+ try (ByteBufferBsonOutput output = new ByteBufferBsonOutput(new SimpleBufferProvider())) {
+ // Write 10 bytes (position becomes 10)
+ for (int i = 0; i < 10; i++) {
+ output.writeByte(0);
+ }
+
+ // absolutePosition = 7 would require bytes at positions 7,8,9,10, but the last written element was at 9.
+ assertThrows(IllegalArgumentException.class, () ->
+ output.writeInt32(7, 5678)
+ );
+ }
+ }
+
+ @Test
+ @DisplayName("should throw exception when calling writeInt32 with negative absolute position")
+ void shouldThrowExceptionWhenAbsolutePositionIsNegative() {
+ try (ByteBufferBsonOutput output = new ByteBufferBsonOutput(new SimpleBufferProvider())) {
+ Assertions.assertThrows(IllegalArgumentException.class, () ->
+ output.writeInt32(-1, 5678)
+ );
+ }
+ }
+
+ static java.util.stream.Stream shouldWriteInt32AbsoluteValueWithinSpanningBuffers() {
+ return java.util.stream.Stream.of(
+ Arguments.of(
+ 0, // absolute position
+ 0x09080706, // int value
+ asList(
+ // initial data
+ new byte[]{0, 1, 2, 3},
+ new byte[]{4, 5, 6, 7}),
+ asList(
+ // expected BsonByteBufferOutput data
+ new byte[]{0x06, 0x07, 0x08, 0x09},
+ new byte[]{4, 5, 6, 7})),
+ Arguments.of(1, 0x09080706,
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{4, 5, 6, 7}),
+ asList(new byte[]{0, 0x06, 0x07, 0x08}, new byte[]{0x09, 5, 6, 7})),
+ Arguments.of(2, 0x09080706,
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{4, 5, 6, 7}),
+ asList(new byte[]{0, 1, 0x06, 0x07}, new byte[]{0x08, 0x09, 6, 7})
+ ),
+ Arguments.of(3, 0x09080706,
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{4, 5, 6, 7}),
+ asList(new byte[]{0, 1, 2, 0x06}, new byte[]{0x07, 0x08, 0x09, 7})
+ ),
+ Arguments.of(4, 0x09080706,
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{4, 5, 6, 7}),
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{0x06, 0x07, 0x08, 0x09})
+ ));
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void shouldWriteInt32AbsoluteValueWithinSpanningBuffers(
+ final int absolutePosition,
+ final int intValue,
+ final List initialData,
+ final List expectedBuffers) {
+
+ try (ByteBufferBsonOutput output =
+ new ByteBufferBsonOutput(size -> new ByteBufNIO(ByteBuffer.allocate(4)))) {
+
+ //given
+ initialData.forEach(output::writeBytes);
+
+ //when
+ output.writeInt32(absolutePosition, intValue);
+
+ //then
+ List buffers = output.getByteBuffers();
+ assertEquals(expectedBuffers.size(), buffers.size(), "Number of buffers mismatch");
+ for (int i = 0; i < expectedBuffers.size(); i++) {
+ assertArrayEquals(expectedBuffers.get(i), buffers.get(i).array(),
+ "Buffer " + i + " contents mismatch");
+ }
+ }
+ }
+
+ static java.util.stream.Stream int32SpanningBuffersData() {
+ return java.util.stream.Stream.of(
+ // Test case 1: No initial data; entire int written into one buffer.
+ Arguments.of(0x09080706,
+ asList(
+ // No initial data
+ ),
+ asList(
+ // expected BsonByteBufferOutput data
+ new byte[]{0x06, 0x07, 0x08, 0x09}),
+ 4, // expected overall position after write (0 + 4)
+ 4 // expected last buffer position (buffer fully written)
+ ),
+ Arguments.of(0x09080706,
+ asList(new byte[]{0}),
+ asList(new byte[]{0, 0x06, 0x07, 0x08}, new byte[]{0x09, 0, 0, 0}), 5, 1
+ ),
+ Arguments.of(0x09080706,
+ asList(new byte[]{0, 1}),
+ asList(new byte[]{0, 1, 0x06, 0x07}, new byte[]{0x08, 0x09, 0, 0}), 6, 2
+ ),
+ Arguments.of(0x09080706,
+ asList(new byte[]{0, 1, 2}),
+ asList(new byte[]{0, 1, 2, 0x06}, new byte[]{0x07, 0x08, 0x09, 0}), 7, 3
+ ),
+ Arguments.of(0x09080706,
+ asList(new byte[]{0, 1, 2, 3}),
+ asList(new byte[]{0, 1, 2, 3}, new byte[]{0x06, 0x07, 0x08, 0x09}), 8, 4
+ ));
+ }
+
+ static java.util.stream.Stream int64SpanningBuffersData() {
+ return java.util.stream.Stream.of(
+ // Test case 1: No initial data; entire long written into one buffer.
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(
+ // No initial data
+ ),
+ asList(
+ // expected BsonByteBufferOutput data
+ new byte[]{0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A}
+ ),
+ 8, // expected overall position after write (0 + 8)
+ 8 // expected last buffer position (buffer fully written)
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0}),
+ asList(new byte[]{0, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B}, new byte[]{0x0A, 0, 0, 0, 0, 0, 0, 0}),
+ 9, 1
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1}),
+ asList(new byte[]{0, 1, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C}, new byte[]{0x0B, 0x0A, 0, 0, 0, 0, 0, 0}),
+ 10, 2
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2}),
+ asList(new byte[]{0, 1, 2, 0x11, 0x10, 0x0F, 0x0E, 0x0D}, new byte[]{0x0C, 0x0B, 0x0A, 0, 0, 0, 0, 0}),
+ 11, 3
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2, 3}),
+ asList(new byte[]{0, 1, 2, 3, 0x11, 0x10, 0x0F, 0x0E}, new byte[]{0x0D, 0x0C, 0x0B, 0x0A, 0, 0, 0, 0}),
+ 12, 4
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2, 3, 4}),
+ asList(new byte[]{0, 1, 2, 3, 4, 0x11, 0x10, 0x0F}, new byte[]{0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0, 0, 0}),
+ 13, 5
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2, 3, 4, 5}),
+ asList(new byte[]{0, 1, 2, 3, 4, 5, 0x11, 0x10}, new byte[]{0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0, 0}),
+ 14, 6
+ ), Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2, 3, 4, 5, 6}),
+ asList(new byte[]{0, 1, 2, 3, 4, 5, 6, 0x11}, new byte[]{0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0}),
+ 15, 7
+ ),
+ Arguments.of(0x0A0B0C0D0E0F1011L,
+ asList(new byte[]{0, 1, 2, 3, 4, 5, 6, 7}),
+ asList(new byte[]{0, 1, 2, 3, 4, 5, 6, 7}, new byte[]{0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A}),
+ 16, 8
+ )
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("int32SpanningBuffersData")
+ void shouldWriteInt32WithinSpanningBuffers(
+ final int intValue,
+ final List initialData,
+ final List expectedBuffers,
+ final int expectedOutputPosition,
+ final int expectedLastBufferPosition) {
+
+ try (ByteBufferBsonOutput output =
+ new ByteBufferBsonOutput(size -> new ByteBufNIO(ByteBuffer.allocate(4)))) {
+
+ //given
+ initialData.forEach(output::writeBytes);
+
+ //when
+ output.writeInt32(intValue);
+
+ //then
+ //getByteBuffers returns ByteBuffers with limit() set to position, position set to 0.
+ List buffers = output.getByteBuffers();
+ assertEquals(expectedBuffers.size(), buffers.size(), "Number of buffers mismatch");
+ for (int i = 0; i < expectedBuffers.size(); i++) {
+ assertArrayEquals(expectedBuffers.get(i), buffers.get(i).array(),
+ "Buffer " + i + " contents mismatch");
+ }
+
+ assertEquals(expectedLastBufferPosition, buffers.get(buffers.size() - 1).limit());
+ assertEquals(expectedOutputPosition, output.getPosition());
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("int64SpanningBuffersData")
+ void shouldWriteInt64WithinSpanningBuffers(
+ final long intValue,
+ final List initialData,
+ final List expectedBuffers,
+ final int expectedOutputPosition,
+ final int expectedLastBufferPosition) {
+
+ try (ByteBufferBsonOutput output =
+ new ByteBufferBsonOutput(size -> new ByteBufNIO(ByteBuffer.allocate(8)))) {
+
+ //given
+ initialData.forEach(output::writeBytes);
+
+ //when
+ output.writeInt64(intValue);
+
+ //then
+ //getByteBuffers returns ByteBuffers with limit() set to position, position set to 0.
+ List buffers = output.getByteBuffers();
+ assertEquals(expectedBuffers.size(), buffers.size(), "Number of buffers mismatch");
+ for (int i = 0; i < expectedBuffers.size(); i++) {
+ assertArrayEquals(expectedBuffers.get(i), buffers.get(i).array(),
+ "Buffer " + i + " contents mismatch");
+ }
+
+ assertEquals(expectedLastBufferPosition, buffers.get(buffers.size() - 1).limit());
+ assertEquals(expectedOutputPosition, output.getPosition());
+ }
+ }
+
+ @ParameterizedTest
+ @MethodSource("int64SpanningBuffersData")
+ void shouldWriteDoubleWithinSpanningBuffers(
+ final long intValue,
+ final List initialData,
+ final List expectedBuffers,
+ final int expectedOutputPosition,
+ final int expectedLastBufferPosition) {
+
+ try (ByteBufferBsonOutput output =
+ new ByteBufferBsonOutput(size -> new ByteBufNIO(ByteBuffer.allocate(8)))) {
+
+ //given
+ initialData.forEach(output::writeBytes);
+
+ //when
+ output.writeDouble(Double.longBitsToDouble(intValue));
+
+ //then
+ //getByteBuffers returns ByteBuffers with limit() set to position, position set to 0.
+ List buffers = output.getByteBuffers();
+ assertEquals(expectedBuffers.size(), buffers.size(), "Number of buffers mismatch");
+ for (int i = 0; i < expectedBuffers.size(); i++) {
+ assertArrayEquals(expectedBuffers.get(i), buffers.get(i).array(),
+ "Buffer " + i + " contents mismatch");
+ }
+
+ assertEquals(expectedLastBufferPosition, buffers.get(buffers.size() - 1).limit());
+ assertEquals(expectedOutputPosition, output.getPosition());
+ }
+ }
}