Improve predictability of writeUtf8/writeAscii performance (#10368)

Motivation:

writeUtf8 can suffer from inlining issues and/or megamorphic call-sites on the hot path due to ByteBuf hierarchy

Modifications:

Duplicate and specialize the code paths to reduce the need of polymorphic calls

Result:

Performance are more stable in user code
This commit is contained in:
Francesco Nigro 2020-09-09 16:10:26 +02:00 committed by GitHub
parent 5631f1b2b7
commit 162e59848a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 2044 additions and 132 deletions

View File

@ -705,7 +705,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
} else { } else {
checkIndex(index, length); checkIndex(index, length);
} }
return ByteBufUtil.writeUtf8(this, index, sequence, sequence.length()); return ByteBufUtil.writeUtf8(this, index, length, sequence, sequence.length());
} }
if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) { if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) {
int length = sequence.length(); int length = sequence.length();

View File

@ -570,7 +570,7 @@ public final class ByteBufUtil {
} else if (buf instanceof AbstractByteBuf) { } else if (buf instanceof AbstractByteBuf) {
AbstractByteBuf byteBuf = (AbstractByteBuf) buf; AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
byteBuf.ensureWritable0(reserveBytes); byteBuf.ensureWritable0(reserveBytes);
int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, start, end); int written = writeUtf8(byteBuf, byteBuf.writerIndex, reserveBytes, seq, start, end);
byteBuf.writerIndex += written; byteBuf.writerIndex += written;
return written; return written;
} else if (buf instanceof WrappedByteBuf) { } else if (buf instanceof WrappedByteBuf) {
@ -584,12 +584,111 @@ public final class ByteBufUtil {
} }
} }
static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) { static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes, CharSequence seq, int len) {
return writeUtf8(buffer, writerIndex, seq, 0, len); return writeUtf8(buffer, writerIndex, reservedBytes, seq, 0, len);
} }
// Fast-Path implementation // Fast-Path implementation
static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) { static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes,
CharSequence seq, int start, int end) {
if (seq instanceof AsciiString) {
writeAsciiString(buffer, writerIndex, (AsciiString) seq, start, end);
return end - start;
}
if (PlatformDependent.hasUnsafe()) {
if (buffer.hasArray()) {
return unsafeWriteUtf8(buffer.array(), PlatformDependent.byteArrayBaseOffset(),
buffer.arrayOffset() + writerIndex, seq, start, end);
}
if (buffer.hasMemoryAddress()) {
return unsafeWriteUtf8(null, buffer.memoryAddress(), writerIndex, seq, start, end);
}
} else {
if (buffer.hasArray()) {
return safeArrayWriteUtf8(buffer.array(), buffer.arrayOffset() + writerIndex, seq, start, end);
}
if (buffer.isDirect()) {
assert buffer.nioBufferCount() == 1;
final ByteBuffer internalDirectBuffer = buffer.internalNioBuffer(writerIndex, reservedBytes);
final int bufferPosition = internalDirectBuffer.position();
return safeDirectWriteUtf8(internalDirectBuffer, bufferPosition, seq, start, end);
}
}
return safeWriteUtf8(buffer, writerIndex, seq, start, end);
}
// AsciiString Fast-Path implementation - no explicit bound-checks
static void writeAsciiString(AbstractByteBuf buffer, int writerIndex, AsciiString seq, int start, int end) {
final int begin = seq.arrayOffset() + start;
final int length = end - start;
if (PlatformDependent.hasUnsafe()) {
if (buffer.hasArray()) {
PlatformDependent.copyMemory(seq.array(), begin,
buffer.array(), buffer.arrayOffset() + writerIndex, length);
return;
}
if (buffer.hasMemoryAddress()) {
PlatformDependent.copyMemory(seq.array(), begin, buffer.memoryAddress() + writerIndex, length);
return;
}
}
if (buffer.hasArray()) {
System.arraycopy(seq.array(), begin, buffer.array(), buffer.arrayOffset() + writerIndex, length);
return;
}
buffer.setBytes(writerIndex, seq.array(), begin, length);
}
// Safe off-heap Fast-Path implementation
private static int safeDirectWriteUtf8(ByteBuffer buffer, int writerIndex, CharSequence seq, int start, int end) {
assert !(seq instanceof AsciiString);
int oldWriterIndex = writerIndex;
// We can use the _set methods as these not need to do any index checks and reference checks.
// This is possible as we called ensureWritable(...) before.
for (int i = start; i < end; i++) {
char c = seq.charAt(i);
if (c < 0x80) {
buffer.put(writerIndex++, (byte) c);
} else if (c < 0x800) {
buffer.put(writerIndex++, (byte) (0xc0 | (c >> 6)));
buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
} else if (isSurrogate(c)) {
if (!Character.isHighSurrogate(c)) {
buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
continue;
}
// Surrogate Pair consumes 2 characters.
if (++i == end) {
buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
break;
}
// Extra method is copied here to NOT allow inlining of writeUtf8
// and increase the chance to inline CharSequence::charAt instead
char c2 = seq.charAt(i);
if (!Character.isLowSurrogate(c2)) {
buffer.put(writerIndex++, WRITE_UTF_UNKNOWN);
buffer.put(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : (byte) c2);
} else {
int codePoint = Character.toCodePoint(c, c2);
// See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
buffer.put(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
buffer.put(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
}
} else {
buffer.put(writerIndex++, (byte) (0xe0 | (c >> 12)));
buffer.put(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f)));
}
}
return writerIndex - oldWriterIndex;
}
// Safe off-heap Fast-Path implementation
private static int safeWriteUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) {
assert !(seq instanceof AsciiString);
int oldWriterIndex = writerIndex; int oldWriterIndex = writerIndex;
// We can use the _set methods as these not need to do any index checks and reference checks. // We can use the _set methods as these not need to do any index checks and reference checks.
@ -611,8 +710,20 @@ public final class ByteBufUtil {
buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
break; break;
} }
// Extra method to allow inlining the rest of writeUtf8 which is the most likely code path. // Extra method is copied here to NOT allow inlining of writeUtf8
writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, seq.charAt(i)); // and increase the chance to inline CharSequence::charAt instead
char c2 = seq.charAt(i);
if (!Character.isLowSurrogate(c2)) {
buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
buffer._setByte(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
} else {
int codePoint = Character.toCodePoint(c, c2);
// See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18)));
buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f)));
}
} else { } else {
buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12))); buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
@ -622,19 +733,94 @@ public final class ByteBufUtil {
return writerIndex - oldWriterIndex; return writerIndex - oldWriterIndex;
} }
private static int writeUtf8Surrogate(AbstractByteBuf buffer, int writerIndex, char c, char c2) { // safe byte[] Fast-Path implementation
if (!Character.isLowSurrogate(c2)) { private static int safeArrayWriteUtf8(byte[] buffer, int writerIndex, CharSequence seq, int start, int end) {
buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); int oldWriterIndex = writerIndex;
buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); for (int i = start; i < end; i++) {
return writerIndex; char c = seq.charAt(i);
if (c < 0x80) {
buffer[writerIndex++] = (byte) c;
} else if (c < 0x800) {
buffer[writerIndex++] = (byte) (0xc0 | (c >> 6));
buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
} else if (isSurrogate(c)) {
if (!Character.isHighSurrogate(c)) {
buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
continue;
} }
// Surrogate Pair consumes 2 characters.
if (++i == end) {
buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
break;
}
char c2 = seq.charAt(i);
// Extra method is copied here to NOT allow inlining of writeUtf8
// and increase the chance to inline CharSequence::charAt instead
if (!Character.isLowSurrogate(c2)) {
buffer[writerIndex++] = WRITE_UTF_UNKNOWN;
buffer[writerIndex++] = (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2);
} else {
int codePoint = Character.toCodePoint(c, c2); int codePoint = Character.toCodePoint(c, c2);
// See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); buffer[writerIndex++] = (byte) (0xf0 | (codePoint >> 18));
buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 12) & 0x3f));
buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 6) & 0x3f));
buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); buffer[writerIndex++] = (byte) (0x80 | (codePoint & 0x3f));
return writerIndex; }
} else {
buffer[writerIndex++] = (byte) (0xe0 | (c >> 12));
buffer[writerIndex++] = (byte) (0x80 | ((c >> 6) & 0x3f));
buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f));
}
}
return writerIndex - oldWriterIndex;
}
// unsafe Fast-Path implementation
private static int unsafeWriteUtf8(byte[] buffer, long memoryOffset, int writerIndex,
CharSequence seq, int start, int end) {
assert !(seq instanceof AsciiString);
long writerOffset = memoryOffset + writerIndex;
final long oldWriterOffset = writerOffset;
for (int i = start; i < end; i++) {
char c = seq.charAt(i);
if (c < 0x80) {
PlatformDependent.putByte(buffer, writerOffset++, (byte) c);
} else if (c < 0x800) {
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xc0 | (c >> 6)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
} else if (isSurrogate(c)) {
if (!Character.isHighSurrogate(c)) {
PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
continue;
}
// Surrogate Pair consumes 2 characters.
if (++i == end) {
PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
break;
}
char c2 = seq.charAt(i);
// Extra method is copied here to NOT allow inlining of writeUtf8
// and increase the chance to inline CharSequence::charAt instead
if (!Character.isLowSurrogate(c2)) {
PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN);
PlatformDependent.putByte(buffer, writerOffset++,
(byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2));
} else {
int codePoint = Character.toCodePoint(c, c2);
// See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630.
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xf0 | (codePoint >> 18)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 12) & 0x3f)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 6) & 0x3f)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (codePoint & 0x3f)));
}
} else {
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xe0 | (c >> 12)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((c >> 6) & 0x3f)));
PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f)));
}
}
return (int) (writerOffset - oldWriterOffset);
} }
/** /**
@ -742,21 +928,22 @@ public final class ByteBufUtil {
*/ */
public static int writeAscii(ByteBuf buf, CharSequence seq) { public static int writeAscii(ByteBuf buf, CharSequence seq) {
// ASCII uses 1 byte per char // ASCII uses 1 byte per char
final int len = seq.length();
if (seq instanceof AsciiString) {
AsciiString asciiString = (AsciiString) seq;
buf.writeBytes(asciiString.array(), asciiString.arrayOffset(), len);
} else {
for (;;) { for (;;) {
if (buf instanceof WrappedCompositeByteBuf) { if (buf instanceof WrappedCompositeByteBuf) {
// WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling. // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling.
buf = buf.unwrap(); buf = buf.unwrap();
} else if (buf instanceof AbstractByteBuf) { } else if (buf instanceof AbstractByteBuf) {
final int len = seq.length();
AbstractByteBuf byteBuf = (AbstractByteBuf) buf; AbstractByteBuf byteBuf = (AbstractByteBuf) buf;
byteBuf.ensureWritable0(len); byteBuf.ensureWritable0(len);
int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len); if (seq instanceof AsciiString) {
byteBuf.writerIndex += written; writeAsciiString(byteBuf, byteBuf.writerIndex, (AsciiString) seq, 0, len);
return written; } else {
final int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len);
assert written == len;
}
byteBuf.writerIndex += len;
return len;
} else if (buf instanceof WrappedByteBuf) { } else if (buf instanceof WrappedByteBuf) {
// Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path.
buf = buf.unwrap(); buf = buf.unwrap();
@ -767,8 +954,6 @@ public final class ByteBufUtil {
} }
} }
} }
return len;
}
// Fast-Path implementation // Fast-Path implementation
static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) { static int writeAscii(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) {

View File

@ -18,11 +18,14 @@ package io.netty.buffer;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -31,16 +34,48 @@ import java.util.concurrent.atomic.AtomicReference;
import static io.netty.buffer.Unpooled.unreleasableBuffer; import static io.netty.buffer.Unpooled.unreleasableBuffer;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeThat; import static org.junit.Assume.assumeThat;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public class ByteBufUtilTest { public class ByteBufUtilTest {
private enum BufferType {
DIRECT_UNPOOLED, DIRECT_POOLED, HEAP_POOLED, HEAP_UNPOOLED
}
private final BufferType bufferType;
public ByteBufUtilTest(BufferType bufferType) {
this.bufferType = bufferType;
}
private ByteBuf buffer(int capacity) {
switch (bufferType) {
case DIRECT_UNPOOLED:
return Unpooled.directBuffer(capacity);
case HEAP_UNPOOLED:
return Unpooled.buffer(capacity);
case DIRECT_POOLED:
return PooledByteBufAllocator.DEFAULT.directBuffer(capacity);
case HEAP_POOLED:
return PooledByteBufAllocator.DEFAULT.buffer(capacity);
default:
throw new AssertionError("unexpected buffer type: " + bufferType);
}
}
@Parameterized.Parameters(name = "bufferType = {0}")
public static Collection<Object[]> noUnsafe() {
return Arrays.asList(new Object[][] {
{ BufferType.DIRECT_POOLED },
{ BufferType.DIRECT_UNPOOLED },
{ BufferType.HEAP_POOLED },
{ BufferType.HEAP_UNPOOLED }
});
}
@Test @Test
public void decodeRandomHexBytesWithEvenLength() { public void decodeRandomHexBytesWithEvenLength() {
decodeRandomHexBytes(256); decodeRandomHexBytes(256);
@ -144,14 +179,14 @@ public class ByteBufUtilTest {
public void writeShortBE() { public void writeShortBE() {
int expected = 0x1234; int expected = 0x1234;
ByteBuf buf = Unpooled.buffer(2).order(ByteOrder.BIG_ENDIAN); ByteBuf buf = buffer(2).order(ByteOrder.BIG_ENDIAN);
ByteBufUtil.writeShortBE(buf, expected); ByteBufUtil.writeShortBE(buf, expected);
assertEquals(expected, buf.readShort()); assertEquals(expected, buf.readShort());
buf.resetReaderIndex(); buf.resetReaderIndex();
assertEquals(ByteBufUtil.swapShort((short) expected), buf.readShortLE()); assertEquals(ByteBufUtil.swapShort((short) expected), buf.readShortLE());
buf.release(); buf.release();
buf = Unpooled.buffer(2).order(ByteOrder.LITTLE_ENDIAN); buf = buffer(2).order(ByteOrder.LITTLE_ENDIAN);
ByteBufUtil.writeShortBE(buf, expected); ByteBufUtil.writeShortBE(buf, expected);
assertEquals((short) expected, buf.readShortLE()); assertEquals((short) expected, buf.readShortLE());
buf.resetReaderIndex(); buf.resetReaderIndex();
@ -184,14 +219,14 @@ public class ByteBufUtilTest {
public void writeMediumBE() { public void writeMediumBE() {
int mediumValue = 0x123456; int mediumValue = 0x123456;
ByteBuf buf = Unpooled.buffer(4).order(ByteOrder.BIG_ENDIAN); ByteBuf buf = buffer(4).order(ByteOrder.BIG_ENDIAN);
ByteBufUtil.writeMediumBE(buf, mediumValue); ByteBufUtil.writeMediumBE(buf, mediumValue);
assertEquals(mediumValue, buf.readMedium()); assertEquals(mediumValue, buf.readMedium());
buf.resetReaderIndex(); buf.resetReaderIndex();
assertEquals(ByteBufUtil.swapMedium(mediumValue), buf.readMediumLE()); assertEquals(ByteBufUtil.swapMedium(mediumValue), buf.readMediumLE());
buf.release(); buf.release();
buf = Unpooled.buffer(4).order(ByteOrder.LITTLE_ENDIAN); buf = buffer(4).order(ByteOrder.LITTLE_ENDIAN);
ByteBufUtil.writeMediumBE(buf, mediumValue); ByteBufUtil.writeMediumBE(buf, mediumValue);
assertEquals(mediumValue, buf.readMediumLE()); assertEquals(mediumValue, buf.readMediumLE());
buf.resetReaderIndex(); buf.resetReaderIndex();
@ -202,9 +237,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAscii() { public void testWriteUsAscii() {
String usAscii = "NettyRocks"; String usAscii = "NettyRocks";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -216,9 +251,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAsciiSwapped() { public void testWriteUsAsciiSwapped() {
String usAscii = "NettyRocks"; String usAscii = "NettyRocks";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
SwappedByteBuf buf2 = new SwappedByteBuf(Unpooled.buffer(16)); SwappedByteBuf buf2 = new SwappedByteBuf(buffer(16));
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -230,10 +265,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAsciiWrapped() { public void testWriteUsAsciiWrapped() {
String usAscii = "NettyRocks"; String usAscii = "NettyRocks";
ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16)); ByteBuf buf = unreleasableBuffer(buffer(16));
assertWrapped(buf); assertWrapped(buf);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16)); ByteBuf buf2 = unreleasableBuffer(buffer(16));
assertWrapped(buf2); assertWrapped(buf2);
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
@ -246,10 +281,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAsciiComposite() { public void testWriteUsAsciiComposite() {
String usAscii = "NettyRocks"; String usAscii = "NettyRocks";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( ByteBuf buf2 = Unpooled.compositeBuffer().addComponent(
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); buffer(8)).addComponent(buffer(24));
// write some byte so we start writing with an offset. // write some byte so we start writing with an offset.
buf2.writeByte(1); buf2.writeByte(1);
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
@ -264,10 +299,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAsciiCompositeWrapped() { public void testWriteUsAsciiCompositeWrapped() {
String usAscii = "NettyRocks"; String usAscii = "NettyRocks";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent(
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); buffer(8)).addComponent(buffer(24)));
// write some byte so we start writing with an offset. // write some byte so we start writing with an offset.
buf2.writeByte(1); buf2.writeByte(1);
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
@ -282,9 +317,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8() { public void testWriteUtf8() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, usAscii); ByteBufUtil.writeUtf8(buf2, usAscii);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -296,10 +331,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8Composite() { public void testWriteUtf8Composite() {
String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; String utf8 = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( ByteBuf buf2 = Unpooled.compositeBuffer().addComponent(
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); buffer(8)).addComponent(buffer(24));
// write some byte so we start writing with an offset. // write some byte so we start writing with an offset.
buf2.writeByte(1); buf2.writeByte(1);
ByteBufUtil.writeUtf8(buf2, utf8); ByteBufUtil.writeUtf8(buf2, utf8);
@ -314,10 +349,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8CompositeWrapped() { public void testWriteUtf8CompositeWrapped() {
String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; String utf8 = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent(
Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); buffer(8)).addComponent(buffer(24)));
// write some byte so we start writing with an offset. // write some byte so we start writing with an offset.
buf2.writeByte(1); buf2.writeByte(1);
ByteBufUtil.writeUtf8(buf2, utf8); ByteBufUtil.writeUtf8(buf2, utf8);
@ -338,9 +373,9 @@ public class ByteBufUtilTest {
.append('\uDC00') .append('\uDC00')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -357,9 +392,9 @@ public class ByteBufUtilTest {
.append('\uDC00') .append('\uDC00')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -376,9 +411,9 @@ public class ByteBufUtilTest {
.append('\uD800') .append('\uD800')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -396,9 +431,9 @@ public class ByteBufUtilTest {
.append('\uD800') .append('\uD800')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -416,9 +451,9 @@ public class ByteBufUtilTest {
.append('\uD800') .append('\uD800')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -435,9 +470,9 @@ public class ByteBufUtilTest {
.append('\uDC00') .append('\uDC00')
.append('b') .append('b')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -452,9 +487,9 @@ public class ByteBufUtilTest {
String surrogateString = new StringBuilder(2) String surrogateString = new StringBuilder(2)
.append('\uD800') .append('\uD800')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -469,9 +504,9 @@ public class ByteBufUtilTest {
String surrogateString = new StringBuilder(2) String surrogateString = new StringBuilder(2)
.append('\uDC00') .append('\uDC00')
.toString(); .toString();
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, surrogateString); ByteBufUtil.writeUtf8(buf2, surrogateString);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -484,9 +519,10 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUsAsciiString() { public void testWriteUsAsciiString() {
AsciiString usAscii = new AsciiString("NettyRocks"); AsciiString usAscii = new AsciiString("NettyRocks");
ByteBuf buf = Unpooled.buffer(16); int expectedCapacity = usAscii.length();
ByteBuf buf = buffer(expectedCapacity);
buf.writeBytes(usAscii.toString().getBytes(CharsetUtil.US_ASCII)); buf.writeBytes(usAscii.toString().getBytes(CharsetUtil.US_ASCII));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(expectedCapacity);
ByteBufUtil.writeAscii(buf2, usAscii); ByteBufUtil.writeAscii(buf2, usAscii);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -498,17 +534,17 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8Wrapped() { public void testWriteUtf8Wrapped() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16)); ByteBuf buf = unreleasableBuffer(buffer(16));
assertWrapped(buf); assertWrapped(buf);
buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8)); buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16)); ByteBuf buf2 = unreleasableBuffer(buffer(16));
assertWrapped(buf2); assertWrapped(buf2);
ByteBufUtil.writeUtf8(buf2, usAscii); ByteBufUtil.writeUtf8(buf2, usAscii);
assertEquals(buf, buf2); assertEquals(buf, buf2);
buf.release(); buf.unwrap().release();
buf2.release(); buf2.unwrap().release();
} }
private static void assertWrapped(ByteBuf buf) { private static void assertWrapped(ByteBuf buf) {
@ -518,9 +554,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8Subsequence() { public void testWriteUtf8Subsequence() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8)); buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, usAscii, 5, 18); ByteBufUtil.writeUtf8(buf2, usAscii, 5, 18);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -532,9 +568,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testWriteUtf8SubsequenceSplitSurrogate() { public void testWriteUtf8SubsequenceSplitSurrogate() {
String usAscii = "\uD800\uDC00"; // surrogate pair: one code point, two chars String usAscii = "\uD800\uDC00"; // surrogate pair: one code point, two chars
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.substring(0, 1).getBytes(CharsetUtil.UTF_8)); buf.writeBytes(usAscii.substring(0, 1).getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
ByteBufUtil.writeUtf8(buf2, usAscii, 0, 1); ByteBufUtil.writeUtf8(buf2, usAscii, 0, 1);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -546,9 +582,9 @@ public class ByteBufUtilTest {
@Test @Test
public void testReserveAndWriteUtf8Subsequence() { public void testReserveAndWriteUtf8Subsequence() {
String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; String usAscii = "Some UTF-8 like äÄ∏ŒŒ";
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8)); buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8));
ByteBuf buf2 = Unpooled.buffer(16); ByteBuf buf2 = buffer(16);
int count = ByteBufUtil.reserveAndWriteUtf8(buf2, usAscii, 5, 18, 16); int count = ByteBufUtil.reserveAndWriteUtf8(buf2, usAscii, 5, 18, 16);
assertEquals(buf, buf2); assertEquals(buf, buf2);
@ -575,7 +611,7 @@ public class ByteBufUtilTest {
private void testInvalidSubsequences(TestMethod method) { private void testInvalidSubsequences(TestMethod method) {
for (int [] range : INVALID_RANGES) { for (int [] range : INVALID_RANGES) {
ByteBuf buf = Unpooled.buffer(16); ByteBuf buf = buffer(16);
try { try {
method.invoke(buf, "Some UTF-8 like äÄ∏ŒŒ", range[0], range[1]); method.invoke(buf, "Some UTF-8 like äÄ∏ŒŒ", range[0], range[1]);
fail("Did not throw IndexOutOfBoundsException for range (" + range[0] + ", " + range[1] + ")"); fail("Did not throw IndexOutOfBoundsException for range (" + range[0] + ", " + range[1] + ")");
@ -641,8 +677,8 @@ public class ByteBufUtilTest {
CompositeByteBuf buffer = Unpooled.compositeBuffer(); CompositeByteBuf buffer = Unpooled.compositeBuffer();
try { try {
byte[] bytes = "1234".getBytes(CharsetUtil.UTF_8); byte[] bytes = "1234".getBytes(CharsetUtil.UTF_8);
buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes)); buffer.addComponent(buffer(bytes.length).writeBytes(bytes));
buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes)); buffer.addComponent(buffer(bytes.length).writeBytes(bytes));
assertEquals("1234", buffer.toString(bytes.length, bytes.length, CharsetUtil.UTF_8)); assertEquals("1234", buffer.toString(bytes.length, bytes.length, CharsetUtil.UTF_8));
} finally { } finally {
buffer.release(); buffer.release();
@ -703,7 +739,7 @@ public class ByteBufUtilTest {
@Test @Test
public void testIsTextWithInvalidIndexAndLength() { public void testIsTextWithInvalidIndexAndLength() {
ByteBuf buffer = Unpooled.buffer(); ByteBuf buffer = buffer(4);
try { try {
buffer.writeBytes(new byte[4]); buffer.writeBytes(new byte[4]);
int[][] validIndexLengthPairs = { int[][] validIndexLengthPairs = {
@ -760,8 +796,8 @@ public class ByteBufUtilTest {
checkUtf8Bytes(s); checkUtf8Bytes(s);
} }
private static void checkUtf8Bytes(final CharSequence charSequence) { private void checkUtf8Bytes(final CharSequence charSequence) {
final ByteBuf buf = Unpooled.buffer(ByteBufUtil.utf8MaxBytes(charSequence)); final ByteBuf buf = buffer(ByteBufUtil.utf8MaxBytes(charSequence));
try { try {
final int writtenBytes = ByteBufUtil.writeUtf8(buf, charSequence); final int writtenBytes = ByteBufUtil.writeUtf8(buf, charSequence);
final int utf8Bytes = ByteBufUtil.utf8Bytes(charSequence); final int utf8Bytes = ByteBufUtil.utf8Bytes(charSequence);
@ -771,8 +807,8 @@ public class ByteBufUtilTest {
} }
} }
private static void assertIsText(byte[] bytes, boolean expected, Charset charset) { private void assertIsText(byte[] bytes, boolean expected, Charset charset) {
ByteBuf buffer = Unpooled.buffer(); ByteBuf buffer = buffer(bytes.length);
try { try {
buffer.writeBytes(bytes); buffer.writeBytes(bytes);
assertEquals(expected, ByteBufUtil.isText(buffer, charset)); assertEquals(expected, ByteBufUtil.isText(buffer, charset));
@ -783,6 +819,7 @@ public class ByteBufUtilTest {
@Test @Test
public void testIsTextMultiThreaded() throws Throwable { public void testIsTextMultiThreaded() throws Throwable {
assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED));
final ByteBuf buffer = Unpooled.copiedBuffer("Hello, World!", CharsetUtil.ISO_8859_1); final ByteBuf buffer = Unpooled.copiedBuffer("Hello, World!", CharsetUtil.ISO_8859_1);
try { try {
@ -822,21 +859,9 @@ public class ByteBufUtilTest {
} }
@Test @Test
public void testGetBytesHeap() { public void testGetBytes() {
final ByteBuf buf = Unpooled.buffer(4); final ByteBuf buf = buffer(4);
try { try {
assumeTrue(buf.hasArray());
checkGetBytes(buf);
} finally {
buf.release();
}
}
@Test
public void testGetBytesDirect() {
final ByteBuf buf = Unpooled.directBuffer(4);
try {
assumeFalse(buf.hasArray());
checkGetBytes(buf); checkGetBytes(buf);
} finally { } finally {
buf.release(); buf.release();
@ -845,16 +870,17 @@ public class ByteBufUtilTest {
@Test @Test
public void testGetBytesHeapWithNonZeroArrayOffset() { public void testGetBytesHeapWithNonZeroArrayOffset() {
final ByteBuf buf = Unpooled.buffer(5); assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED));
final ByteBuf buf = buffer(5);
try { try {
buf.setByte(0, 0x05); buf.setByte(0, 0x05);
final ByteBuf slice = buf.slice(1, 4); final ByteBuf slice = buf.slice(1, 4);
slice.writerIndex(0); slice.writerIndex(0);
assumeTrue(slice.hasArray()); assertTrue(slice.hasArray());
assumeThat(slice.arrayOffset(), is(1)); assertThat(slice.arrayOffset(), is(1));
assumeThat(slice.array().length, is(buf.capacity())); assertThat(slice.array().length, is(buf.capacity()));
checkGetBytes(slice); checkGetBytes(slice);
} finally { } finally {
@ -864,16 +890,17 @@ public class ByteBufUtilTest {
@Test @Test
public void testGetBytesHeapWithArrayLengthGreaterThanCapacity() { public void testGetBytesHeapWithArrayLengthGreaterThanCapacity() {
final ByteBuf buf = Unpooled.buffer(5); assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED));
final ByteBuf buf = buffer(5);
try { try {
buf.setByte(4, 0x05); buf.setByte(4, 0x05);
final ByteBuf slice = buf.slice(0, 4); final ByteBuf slice = buf.slice(0, 4);
slice.writerIndex(0); slice.writerIndex(0);
assumeTrue(slice.hasArray()); assertTrue(slice.hasArray());
assumeThat(slice.arrayOffset(), is(0)); assertThat(slice.arrayOffset(), is(0));
assumeThat(slice.array().length, greaterThan(slice.capacity())); assertThat(slice.array().length, greaterThan(slice.capacity()));
checkGetBytes(slice); checkGetBytes(slice);
} finally { } finally {

View File

@ -273,6 +273,10 @@ public final class PlatformDependent {
LINUX_OS_CLASSIFIERS = Collections.unmodifiableSet(availableClassifiers); LINUX_OS_CLASSIFIERS = Collections.unmodifiableSet(availableClassifiers);
} }
public static long byteArrayBaseOffset() {
return BYTE_ARRAY_BASE_OFFSET;
}
public static boolean hasDirectBufferNoCleanerConstructor() { public static boolean hasDirectBufferNoCleanerConstructor() {
return PlatformDependent0.hasDirectBufferNoCleanerConstructor(); return PlatformDependent0.hasDirectBufferNoCleanerConstructor();
} }
@ -659,6 +663,10 @@ public final class PlatformDependent {
PlatformDependent0.putByte(data, index, value); PlatformDependent0.putByte(data, index, value);
} }
public static void putByte(Object data, long offset, byte value) {
PlatformDependent0.putByte(data, offset, value);
}
public static void putShort(byte[] data, int index, short value) { public static void putShort(byte[] data, int index, short value) {
PlatformDependent0.putShort(data, index, value); PlatformDependent0.putShort(data, index, value);
} }
@ -687,6 +695,11 @@ public final class PlatformDependent {
PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length); PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length);
} }
public static void copyMemory(byte[] src, int srcIndex, byte[] dst, int dstIndex, long length) {
PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex,
dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
}
public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) { public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) {
PlatformDependent0.copyMemory(null, srcAddr, dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length); PlatformDependent0.copyMemory(null, srcAddr, dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length);
} }

View File

@ -590,6 +590,10 @@ final class PlatformDependent0 {
UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value); UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value);
} }
static void putByte(Object data, long offset, byte value) {
UNSAFE.putByte(data, offset, value);
}
static void putShort(byte[] data, int index, short value) { static void putShort(byte[] data, int index, short value) {
UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value); UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value);
} }

View File

@ -22,6 +22,7 @@ import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Measurement; import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State; import org.openjdk.jmh.annotations.State;
@ -34,6 +35,11 @@ import org.openjdk.jmh.annotations.Warmup;
@Measurement(iterations = 10) @Measurement(iterations = 10)
public class public class
ByteBufUtilBenchmark extends AbstractMicrobenchmark { ByteBufUtilBenchmark extends AbstractMicrobenchmark {
@Param({ "true", "false" })
private boolean direct;
@Param({ "8", "16", "64", "128" })
private int length;
private ByteBuf buffer; private ByteBuf buffer;
private ByteBuf wrapped; private ByteBuf wrapped;
private ByteBuf asciiBuffer; private ByteBuf asciiBuffer;
@ -48,18 +54,19 @@ public class
@Setup @Setup
public void setup() { public void setup() {
// Use buffer sizes that will also allow to write UTF-8 without grow the buffer // Use buffer sizes that will also allow to write UTF-8 without grow the buffer
buffer = Unpooled.directBuffer(512); final int maxBytes = ByteBufUtil.utf8MaxBytes(length);
wrapped = Unpooled.unreleasableBuffer(Unpooled.directBuffer(512)); buffer = direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes);
asciiSequence = new StringBuilder(128); wrapped = Unpooled.unreleasableBuffer(direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes));
for (int i = 0; i < 128; i++) { asciiSequence = new StringBuilder(length);
for (int i = 0; i < length; i++) {
asciiSequence.append('a'); asciiSequence.append('a');
} }
ascii = asciiSequence.toString(); ascii = asciiSequence.toString();
// Generate some mixed UTF-8 String for benchmark // Generate some mixed UTF-8 String for benchmark
utf8Sequence = new StringBuilder(128); utf8Sequence = new StringBuilder(length);
char[] chars = "Some UTF-8 like äÄ∏ŒŒ".toCharArray(); char[] chars = "Some UTF-8 like äÄ∏ŒŒ".toCharArray();
for (int i = 0; i < 128; i++) { for (int i = 0; i < length; i++) {
utf8Sequence.append(chars[i % chars.length]); utf8Sequence.append(chars[i % chars.length]);
} }
utf8 = utf8Sequence.toString(); utf8 = utf8Sequence.toString();

View File

@ -0,0 +1,277 @@
/*
* Copyright 2020 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.microbench.buffer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.AsciiString;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.CompilerControl;
import org.openjdk.jmh.annotations.CompilerControl.Mode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
@Fork(value = 2, jvmArgsAppend = "-XX:MaxInlineLevel=9")
public class Utf8EncodingBenchmark extends AbstractMicrobenchmark {
private static class AnotherCharSequence implements CharSequence {
private final char[] chars;
AnotherCharSequence(String chars) {
this.chars = new char[chars.length()];
chars.getChars(0, chars.length(), this.chars, 0);
}
@Override
public int length() {
return chars.length;
}
@Override
public char charAt(int i) {
return chars[i];
}
@Override
public CharSequence subSequence(int start, int end) {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
throw new UnsupportedOperationException();
}
}
// experiment test input
private String[] strings;
private StringBuilder[] stringBuilders;
private AnotherCharSequence[] anotherCharSequences;
private AsciiString[] asciiStrings;
@Param({ "false", "true" })
private boolean direct;
private ByteBuf buffer;
@Param({ "false", "true" })
private boolean noUnsafe;
private int dataSetLength;
@Setup
public void init() {
System.setProperty("io.netty.noUnsafe", Boolean.valueOf(noUnsafe).toString());
InputStream testTextStream = null;
InputStreamReader inStreamReader = null;
BufferedReader buffReader = null;
int maxExpectedSize = 0;
List<String> strings = new ArrayList<String>();
List<StringBuilder> stringBuilders = new ArrayList<StringBuilder>();
List<AnotherCharSequence> anotherCharSequenceList = new ArrayList<AnotherCharSequence>();
List<AsciiString> asciiStrings = new ArrayList<AsciiString>();
try {
testTextStream = getClass().getResourceAsStream("/Utf8Samples.txt");
inStreamReader = new InputStreamReader(testTextStream, "UTF-8");
buffReader = new BufferedReader(inStreamReader);
String line;
while ((line = buffReader.readLine()) != null) {
strings.add(line);
stringBuilders.add(new StringBuilder(line));
anotherCharSequenceList.add(new AnotherCharSequence(line));
asciiStrings.add(new AsciiString(line));
maxExpectedSize = Math.max(maxExpectedSize, ByteBufUtil.utf8MaxBytes(line.length()));
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
closeStream(testTextStream);
closeReader(inStreamReader);
closeReader(buffReader);
}
buffer = direct? Unpooled.directBuffer(maxExpectedSize, maxExpectedSize) :
Unpooled.buffer(maxExpectedSize, maxExpectedSize);
buffer.setByte(maxExpectedSize - 1, 0);
this.strings = strings.toArray(new String[strings.size()]);
this.stringBuilders = stringBuilders.toArray(new StringBuilder[stringBuilders.size()]);
this.anotherCharSequences =
anotherCharSequenceList.toArray(new AnotherCharSequence[anotherCharSequenceList.size()]);
this.asciiStrings = asciiStrings.toArray(new AsciiString[asciiStrings.size()]);
this.dataSetLength = this.strings.length;
}
private static void closeStream(InputStream inStream) {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static void closeReader(Reader buffReader) {
if (buffReader != null) {
try {
buffReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int nestedByteBufUtilWriteUtf8String() {
int countBytes = 0;
for (String string : strings) {
countBytes += nestedByteBufUtilWriteUtf8String1(string);
}
return countBytes;
}
private int nestedByteBufUtilWriteUtf8String1(String string) {
return nestedByteBufUtilWriteUtf8String2(string);
}
private int nestedByteBufUtilWriteUtf8String2(String string) {
return nestedByteBufUtilWriteUtf8String3(string);
}
private int nestedByteBufUtilWriteUtf8String3(String string) {
return nestedByteBufUtilWriteUtf8String4(string);
}
private int nestedByteBufUtilWriteUtf8String4(String string) {
return nestedByteBufUtilWriteUtf8String5(string);
}
private int nestedByteBufUtilWriteUtf8String5(String string) {
return nestedByteBufUtilWriteUtf8String6(string);
}
private int nestedByteBufUtilWriteUtf8String6(String string) {
// this calls should be inlined but...what happen to the subsequent calls > MaxInlineLevel?
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, string, 0, string.length());
return buffer.writerIndex();
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int byteBufUtilWriteUtf8String() {
int countBytes = 0;
for (String string : strings) {
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, string, 0, string.length());
countBytes += buffer.writerIndex();
}
return countBytes;
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int byteBufUtilWriteUtf8Bimorphic() {
int countBytes = 0;
for (int i = 0, size = dataSetLength; i < size; i++) {
final StringBuilder stringBuilder = stringBuilders[i];
final String string = strings[i];
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length());
countBytes += buffer.writerIndex();
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, string, 0, string.length());
countBytes += buffer.writerIndex();
}
return countBytes;
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int byteBufUtilWriteUtf8Megamorphic() {
int countBytes = 0;
for (int i = 0, size = dataSetLength; i < size; i++) {
final StringBuilder stringBuilder = stringBuilders[i];
final String string = strings[i];
final AnotherCharSequence anotherCharSequence = anotherCharSequences[i];
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length());
countBytes += buffer.writerIndex();
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, string, 0, string.length());
countBytes += buffer.writerIndex();
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, anotherCharSequence, 0, anotherCharSequence.length());
countBytes += buffer.writerIndex();
}
return countBytes;
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int byteBufUtilWriteUtf8CommonCharSequences() {
int countBytes = 0;
for (int i = 0, size = dataSetLength; i < size; i++) {
final StringBuilder stringBuilder = stringBuilders[i];
final String string = strings[i];
final AsciiString asciiString = asciiStrings[i];
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length());
countBytes += buffer.writerIndex();
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, string, 0, string.length());
countBytes += buffer.writerIndex();
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length());
countBytes += buffer.writerIndex();
}
return countBytes;
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int byteBufUtilWriteUtf8AsciiString() {
int countBytes = 0;
for (int i = 0, size = dataSetLength; i < size; i++) {
final AsciiString asciiString = asciiStrings[i];
buffer.resetWriterIndex();
ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length());
countBytes += buffer.writerIndex();
}
return countBytes;
}
@Benchmark
@CompilerControl(Mode.DONT_INLINE)
public int writeGetBytes() throws UnsupportedEncodingException {
int countBytes = 0;
for (String string : strings) {
buffer.resetWriterIndex();
final byte[] bytes = string.getBytes("UTF-8");
buffer.writeBytes(bytes);
countBytes += buffer.writerIndex();
}
return countBytes;
}
}

File diff suppressed because it is too large Load Diff