Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize writing numeric values. #1635

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions bson/src/main/org/bson/ByteBuf.java
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Note] As @stIncMale suggested, lets put Evolving annotation here.

Original file line number Diff line number Diff line change
@@ -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);
Copy link
Member Author

@vbabanin vbabanin Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the interface includes the warning:

This interface is not frozen yet, and methods may be added in a minor release, so beware implementing this yourself we are justified in adding new methods to this class.

it is permissible to add new methods to this class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add @since to the javadoc for all the new methods.


/**
* 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);

/**
* <p>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.</p>
33 changes: 31 additions & 2 deletions bson/src/main/org/bson/ByteBufNIO.java
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -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();
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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<BufferProvider> bufferProviders() {
return Stream.of(new ByteBufSpecification.NettyBufferProvider(), new SimpleBufferProvider());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

@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();
}
}
}
Loading
Oops, something went wrong.