Skip to content

Commit ff360c0

Browse files
committed
Use internal NIO ByteBuffer from Netty
1 parent b2cd2b8 commit ff360c0

File tree

1 file changed

+34
-99
lines changed

1 file changed

+34
-99
lines changed

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

+34-99
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import java.io.IOException;
2525
import java.io.OutputStream;
26+
import java.nio.ByteBuffer;
2627
import java.nio.ByteOrder;
2728
import java.util.ArrayList;
2829
import java.util.List;
@@ -284,120 +285,54 @@ protected int writeCharacters(final String str, final boolean checkForNullCharac
284285
if (buf.hasArray()) {
285286
return writeCharactersOnArray(str, checkForNullCharacters, buf);
286287
} else if (buf instanceof NettyByteBuf) {
287-
return writeCharactersOnNettyByteBuf(str, checkForNullCharacters, buf);
288+
io.netty.buffer.ByteBuf nettyBuffer = ((NettyByteBuf) buf).asByteBuf();
289+
if (nettyBuffer.nioBufferCount() == 1) {
290+
return writeCharactersOnInternalNioNettyByteBuf(str, checkForNullCharacters, buf, nettyBuffer);
291+
}
288292
}
289293
}
290294
return super.writeCharacters(str, 0, checkForNullCharacters);
291295
}
292296

293-
private static void validateNoNullSingleByteChars(String str, long chars, int i) {
294-
long tmp = (chars & 0x7F7F7F7F7F7F7F7FL) + 0x7F7F7F7F7F7F7F7FL;
295-
tmp = ~(tmp | chars | 0x7F7F7F7F7F7F7F7FL);
296-
if (tmp != 0) {
297-
int firstZero = Long.numberOfTrailingZeros(tmp) >>> 3;
298-
throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character "
299-
+ "at index %d", str, i + firstZero));
300-
}
301-
}
302-
303-
private static void validateNoNullAsciiCharacters(String str, long asciiChars, int i) {
304-
// simplified Hacker's delight search for zero with ASCII chars i.e. which doesn't use the MSB
305-
long tmp = asciiChars + 0x7F7F7F7F7F7F7F7FL;
306-
// MSB is 0 iff the byte is 0x00, 1 otherwise
307-
tmp = ~tmp & 0x8080808080808080L;
308-
// MSB is 1 iff the byte is 0x00, 0 otherwise
309-
if (tmp != 0) {
310-
// there's some 0x00 in the word
311-
int firstZero = Long.numberOfTrailingZeros(tmp) >> 3;
312-
throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character "
313-
+ "at index %d", str, i + firstZero));
314-
}
315-
}
316-
317-
private int writeCharactersOnNettyByteBuf(String str, boolean checkForNullCharacters, ByteBuf buf) {
297+
private int writeCharactersOnInternalNioNettyByteBuf(String str, boolean checkForNullCharacters, ByteBuf buf, io.netty.buffer.ByteBuf nettyBuffer) {
298+
int len = str.length();
299+
final ByteBuffer nioBuffer = internalNioBufferOf(nettyBuffer, len + 1);
318300
int i = 0;
319-
io.netty.buffer.ByteBuf nettyBuffer = ((NettyByteBuf) buf).asByteBuf();
320-
// readonly buffers, netty buffers and off-heap NIO ByteBuffer
321-
boolean slowPath = false;
322-
int batches = str.length() / 8;
323-
final int writerIndex = nettyBuffer.writerIndex();
324-
// this would avoid resizing the buffer while appending: ASCII length + delimiter required space
325-
nettyBuffer.ensureWritable(str.length() + 1);
326-
for (int b = 0; b < batches; b++) {
327-
i = b * 8;
328-
// read 4 chars at time to preserve the 0x0100 cases
329-
long evenChars = str.charAt(i) |
330-
str.charAt(i + 2) << 16 |
331-
(long) str.charAt(i + 4) << 32 |
332-
(long) str.charAt(i + 6) << 48;
333-
long oddChars = str.charAt(i + 1) |
334-
str.charAt(i + 3) << 16 |
335-
(long) str.charAt(i + 5) << 32 |
336-
(long) str.charAt(i + 7) << 48;
337-
// check that both the second byte and the MSB of the first byte of each pair is 0
338-
// needed for cases like \u0100 and \u0080
339-
long mergedChars = evenChars | oddChars;
340-
if ((mergedChars & 0xFF80FF80FF80FF80L) != 0) {
341-
if (allSingleByteChars(mergedChars)) {
342-
i = tryWriteAsciiChars(str, checkForNullCharacters, oddChars, evenChars, nettyBuffer, writerIndex, i);
343-
}
344-
slowPath = true;
345-
break;
301+
int pos = nioBuffer.position();
302+
for (; i < len; i++) {
303+
char c = str.charAt(i);
304+
if (checkForNullCharacters && c == 0x0) {
305+
throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character "
306+
+ "at index %d", str, i));
346307
}
347-
// all ASCII - compose them into a single long
348-
long asciiChars = oddChars << 8 | evenChars;
349-
if (checkForNullCharacters) {
350-
validateNoNullAsciiCharacters(str, asciiChars, i);
308+
if (c >= 0x80) {
309+
break;
351310
}
352-
nettyBuffer.setLongLE(writerIndex + i, asciiChars);
311+
nioBuffer.put(pos + i, (byte) c);
353312
}
354-
if (!slowPath) {
355-
i = batches * 8;
356-
// do the rest, if any
357-
for (; i < str.length(); i++) {
358-
char c = str.charAt(i);
359-
if (checkForNullCharacters && c == 0x0) {
360-
throw new BsonSerializationException(format("BSON cstring '%s' is not valid because it contains a null character "
361-
+ "at index %d", str, i));
362-
}
363-
if (c >= 0x80) {
364-
slowPath = true;
365-
break;
366-
}
367-
nettyBuffer.setByte(writerIndex + i, c);
368-
}
313+
if (i == len) {
314+
int total = len + 1;
315+
nioBuffer.put(pos + len, (byte) 0);
316+
position += total;
317+
buf.position(buf.position() + total);
318+
return len + 1;
369319
}
370-
if (slowPath) {
371-
// ith char is not ASCII:
320+
// ith character is not ASCII
321+
if (i > 0) {
372322
position += i;
373-
buf.position(writerIndex + i);
374-
return i + super.writeCharacters(str, i, checkForNullCharacters);
375-
} else {
376-
nettyBuffer.setByte(writerIndex + str.length(), 0);
377-
int totalWritten = str.length() + 1;
378-
position += totalWritten;
379-
buf.position(writerIndex + totalWritten);
380-
return totalWritten;
323+
buf.position(buf.position() + i);
381324
}
325+
return i + super.writeCharacters(str, i, checkForNullCharacters);
382326
}
383327

384-
private static boolean allSingleByteChars(long fourChars) {
385-
return (fourChars & 0xFF00FF00FF00FF00L) == 0;
386-
}
387-
388-
private static int tryWriteAsciiChars(String str, boolean checkForNullCharacters,
389-
long oddChars, long evenChars, io.netty.buffer.ByteBuf nettyByteBuf, int writerIndex, int i) {
390-
// all single byte chars
391-
long latinChars = oddChars << 8 | evenChars;
392-
if (checkForNullCharacters) {
393-
validateNoNullSingleByteChars(str, latinChars, i);
328+
private static ByteBuffer internalNioBufferOf(io.netty.buffer.ByteBuf buf, int minCapacity) {
329+
io.netty.buffer.ByteBuf unwrap;
330+
while ((unwrap = buf.unwrap()) != null) {
331+
buf = unwrap;
394332
}
395-
long msbSetForNonAscii = latinChars & 0x8080808080808080L;
396-
int firstNonAsciiOffset = Long.numberOfTrailingZeros(msbSetForNonAscii) >> 3;
397-
// that's a bit cheating :P but later phases will patch the wrongly encoded ones
398-
nettyByteBuf.setLongLE(writerIndex + i, latinChars);
399-
i += firstNonAsciiOffset;
400-
return i;
333+
assert buf.unwrap() == null;
334+
buf.ensureWritable(minCapacity);
335+
return buf.internalNioBuffer(buf.writerIndex(), buf.writableBytes());
401336
}
402337

403338
private int writeCharactersOnArray(String str, boolean checkForNullCharacters, ByteBuf buf) {

0 commit comments

Comments
 (0)