Skip to content

Commit 1c3fdc8

Browse files
authored
Optimize writing numeric values. (#1635)
- Optimize writing numeric values with putInt, putLong, and putDouble. - Optimize writing values at absolute buffer position. JAVA-5800 --------- Co-authored-by: Ross Lawley <[email protected]>
1 parent 6fa95c2 commit 1c3fdc8

File tree

8 files changed

+621
-4
lines changed

8 files changed

+621
-4
lines changed

Diff for: bson/src/main/org/bson/ByteBuf.java

+48
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,54 @@ public interface ByteBuf {
106106
*/
107107
ByteBuf put(byte b);
108108

109+
/**
110+
* Writes the given int value into this buffer at the current position,
111+
* using the current byte order, and increments the position by 4.
112+
*
113+
* @param b the int value to be written
114+
* @return this buffer
115+
* @throws java.nio.BufferOverflowException if there are fewer than 4 bytes remaining in this buffer
116+
* @throws java.nio.ReadOnlyBufferException if this buffer is read-only
117+
* @since 5.4
118+
*/
119+
ByteBuf putInt(int b);
120+
121+
/**
122+
* Writes the given int value into this buffer at the current position,
123+
* using the current byte order, and increments the position by 4.
124+
*
125+
* @param b the int value to be written
126+
* @return this buffer
127+
* @throws java.nio.BufferOverflowException if there are fewer than 4 bytes remaining in this buffer
128+
* @throws java.nio.ReadOnlyBufferException if this buffer is read-only
129+
* @since 5.4
130+
*/
131+
ByteBuf putInt(int index, int b);
132+
133+
/**
134+
* Writes the given double value into this buffer at the current position,
135+
* using the current byte order, and increments the position by 8.
136+
*
137+
* @param b the double value to be written
138+
* @return this buffer
139+
* @throws java.nio.BufferOverflowException if there are fewer than 8 bytes remaining in this buffer
140+
* @throws java.nio.ReadOnlyBufferException if this buffer is read-only
141+
* @since 5.4
142+
*/
143+
ByteBuf putDouble(double b);
144+
145+
/**
146+
* Writes the given long value into this buffer at the current position,
147+
* using the current byte order, and increments the position by 8.
148+
*
149+
* @param b the long value to be written
150+
* @return this buffer
151+
* @throws java.nio.BufferOverflowException if there are fewer than 8 bytes remaining in this buffer
152+
* @throws java.nio.ReadOnlyBufferException if this buffer is read-only
153+
* @since 5.4
154+
*/
155+
ByteBuf putLong(long b);
156+
109157
/**
110158
* <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
111159
* is discarded.</p>

Diff for: bson/src/main/org/bson/ByteBufNIO.java

+31-2
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,30 @@ public ByteBuf put(final byte b) {
9797
return this;
9898
}
9999

100+
@Override
101+
public ByteBuf putInt(final int b) {
102+
buf.putInt(b);
103+
return this;
104+
}
105+
106+
@Override
107+
public ByteBuf putInt(final int index, final int b) {
108+
buf.putInt(index, b);
109+
return this;
110+
}
111+
112+
@Override
113+
public ByteBuf putDouble(final double b) {
114+
buf.putDouble(b);
115+
return this;
116+
}
117+
118+
@Override
119+
public ByteBuf putLong(final long b) {
120+
buf.putLong(b);
121+
return this;
122+
}
123+
100124
@Override
101125
public ByteBuf flip() {
102126
((Buffer) buf).flip();
@@ -160,8 +184,13 @@ public ByteBuf get(final byte[] bytes, final int offset, final int length) {
160184

161185
@Override
162186
public ByteBuf get(final int index, final byte[] bytes, final int offset, final int length) {
163-
for (int i = 0; i < length; i++) {
164-
bytes[offset + i] = buf.get(index + i);
187+
if (buf.hasArray()) {
188+
System.arraycopy(buf.array(), index, bytes, offset, length);
189+
} else {
190+
// Fallback to per-byte copying if no backing array is available.
191+
for (int i = 0; i < length; i++) {
192+
bytes[offset + i] = buf.get(index + i);
193+
}
165194
}
166195
return this;
167196
}

Diff for: bson/src/main/org/bson/io/OutputBuffer.java

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public void writeInt32(final int value) {
7070
}
7171

7272
@Override
73+
@Deprecated
7374
public void writeInt32(final int position, final int value) {
7475
write(position, value >> 0);
7576
write(position + 1, value >> 8);

Diff for: driver-core/src/main/com/mongodb/internal/connection/ByteBufferBsonOutput.java

+78
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,84 @@ public void writeBytes(final byte[] bytes, final int offset, final int length) {
8282
position += length;
8383
}
8484

85+
@Override
86+
public void writeInt32(final int value) {
87+
ensureOpen();
88+
ByteBuf buf = getCurrentByteBuffer();
89+
if (buf.remaining() >= 4) {
90+
buf.putInt(value);
91+
position += 4;
92+
} else {
93+
// fallback for edge cases
94+
super.writeInt32(value);
95+
}
96+
}
97+
98+
99+
@Override
100+
public void writeInt32(final int absolutePosition, final int value) {
101+
ensureOpen();
102+
103+
if (absolutePosition < 0) {
104+
throw new IllegalArgumentException(String.format("position must be >= 0 but was %d", absolutePosition));
105+
}
106+
107+
if (absolutePosition + 3 > position - 1) {
108+
throw new IllegalArgumentException(String.format("Cannot write 4 bytes starting at position %d: current size is %d bytes",
109+
position - 1,
110+
absolutePosition + 3));
111+
}
112+
113+
BufferPositionPair bufferPositionPair = getBufferPositionPair(absolutePosition);
114+
ByteBuf byteBuffer = getByteBufferAtIndex(bufferPositionPair.bufferIndex);
115+
int capacity = byteBuffer.position() - bufferPositionPair.position;
116+
117+
if (capacity >= 4) {
118+
byteBuffer.putInt(bufferPositionPair.position, value);
119+
} else {
120+
// fallback for edge cases
121+
int valueToWrite = value;
122+
int pos = bufferPositionPair.position;
123+
int bufferIndex = bufferPositionPair.bufferIndex;
124+
125+
for (int i = 0; i < 4; i++) {
126+
byteBuffer.put(pos++, (byte) valueToWrite);
127+
valueToWrite = valueToWrite >> 8;
128+
if (--capacity == 0) {
129+
byteBuffer = getByteBufferAtIndex(++bufferIndex);
130+
pos = 0;
131+
capacity = byteBuffer.position();
132+
}
133+
}
134+
}
135+
}
136+
137+
@Override
138+
public void writeDouble(final double value) {
139+
ensureOpen();
140+
ByteBuf buf = getCurrentByteBuffer();
141+
if (buf.remaining() >= 8) {
142+
buf.putDouble(value);
143+
position += 8;
144+
} else {
145+
// fallback for edge cases
146+
writeInt64(Double.doubleToRawLongBits(value));
147+
}
148+
}
149+
150+
@Override
151+
public void writeInt64(final long value) {
152+
ensureOpen();
153+
ByteBuf buf = getCurrentByteBuffer();
154+
if (buf.remaining() >= 8) {
155+
buf.putLong(value);
156+
position += 8;
157+
} else {
158+
// fallback for edge cases
159+
super.writeInt64(value);
160+
}
161+
}
162+
85163
@Override
86164
public void writeByte(final int value) {
87165
ensureOpen();

Diff for: driver-core/src/main/com/mongodb/internal/connection/CompositeByteBuf.java

+20
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,26 @@ public ByteBuf put(final byte b) {
237237
throw new UnsupportedOperationException();
238238
}
239239

240+
@Override
241+
public ByteBuf putInt(final int b) {
242+
throw new UnsupportedOperationException();
243+
}
244+
245+
@Override
246+
public ByteBuf putInt(final int index, final int b) {
247+
throw new UnsupportedOperationException();
248+
}
249+
250+
@Override
251+
public ByteBuf putDouble(final double b) {
252+
throw new UnsupportedOperationException();
253+
}
254+
255+
@Override
256+
public ByteBuf putLong(final long b) {
257+
throw new UnsupportedOperationException();
258+
}
259+
240260
@Override
241261
public ByteBuf flip() {
242262
throw new UnsupportedOperationException();

Diff for: driver-core/src/main/com/mongodb/internal/connection/netty/NettyByteBuf.java

+24
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,30 @@ public ByteBuf put(final byte b) {
8989
return this;
9090
}
9191

92+
@Override
93+
public ByteBuf putInt(final int b) {
94+
proxied.writeInt(b);
95+
return this;
96+
}
97+
98+
@Override
99+
public ByteBuf putInt(final int index, final int b) {
100+
proxied.setInt(index, b);
101+
return this;
102+
}
103+
104+
@Override
105+
public ByteBuf putDouble(final double b) {
106+
proxied.writeDouble(b);
107+
return this;
108+
}
109+
110+
@Override
111+
public ByteBuf putLong(final long b) {
112+
proxied.writeLong(b);
113+
return this;
114+
}
115+
92116
@Override
93117
public ByteBuf flip() {
94118
isWriting = !isWriting;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.internal.connection;
18+
19+
20+
import org.bson.ByteBuf;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.MethodSource;
23+
24+
import java.util.stream.Stream;
25+
26+
import static org.junit.jupiter.api.Assertions.assertEquals;
27+
28+
29+
class ByteBufTest {
30+
31+
static Stream<BufferProvider> bufferProviders() {
32+
return Stream.of(new ByteBufSpecification.NettyBufferProvider(), new SimpleBufferProvider());
33+
}
34+
35+
@ParameterizedTest
36+
@MethodSource("bufferProviders")
37+
void shouldPutInt(final BufferProvider provider) {
38+
ByteBuf buffer = provider.getBuffer(1024);
39+
try {
40+
buffer.putInt(42);
41+
buffer.flip();
42+
assertEquals(42, buffer.getInt());
43+
} finally {
44+
buffer.release();
45+
}
46+
}
47+
48+
@ParameterizedTest
49+
@MethodSource("bufferProviders")
50+
void shouldPutLong(final BufferProvider provider) {
51+
ByteBuf buffer = provider.getBuffer(1024);
52+
try {
53+
buffer.putLong(42L);
54+
buffer.flip();
55+
assertEquals(42L, buffer.getLong());
56+
} finally {
57+
buffer.release();
58+
}
59+
}
60+
61+
@ParameterizedTest
62+
@MethodSource("bufferProviders")
63+
void shouldPutDouble(final BufferProvider provider) {
64+
ByteBuf buffer = provider.getBuffer(1024);
65+
try {
66+
buffer.putDouble(42.0D);
67+
buffer.flip();
68+
assertEquals(42.0D, buffer.getDouble());
69+
} finally {
70+
buffer.release();
71+
}
72+
}
73+
74+
@ParameterizedTest
75+
@MethodSource("bufferProviders")
76+
void shouldPutIntAtIndex(final BufferProvider provider) {
77+
ByteBuf buffer = provider.getBuffer(1024);
78+
try {
79+
buffer.putInt(0);
80+
buffer.putInt(0);
81+
buffer.putInt(0);
82+
buffer.putInt(0);
83+
buffer.put((byte) 43);
84+
buffer.put((byte) 44);
85+
buffer.putInt(0, 22);
86+
buffer.putInt(4, 23);
87+
buffer.putInt(8, 24);
88+
buffer.putInt(12, 25);
89+
buffer.flip();
90+
91+
assertEquals(22, buffer.getInt());
92+
assertEquals(23, buffer.getInt());
93+
assertEquals(24, buffer.getInt());
94+
assertEquals(25, buffer.getInt());
95+
assertEquals(43, buffer.get());
96+
assertEquals(44, buffer.get());
97+
} finally {
98+
buffer.release();
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)