Added configurable ByteBuf bounds checking (#7521)
Motivation: The JVM isn't always able to hoist out/reduce bounds checking (due to ref counting operations etc etc) hence making it configurable could improve performances for most CPU intensive use cases. Modifications: Each AbstractByteBuf bounds check has been tested against a new static final configuration property similar to checkAccessible ie io.netty.buffer.bytebuf.checkBounds. Result: Any user could disable ByteBuf bounds checking in order to get extra performances.
This commit is contained in:
parent
3eec66a974
commit
c78be33443
@ -43,13 +43,22 @@ import static io.netty.util.internal.MathUtil.isOutOfBounds;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractByteBuf extends ByteBuf {
|
public abstract class AbstractByteBuf extends ByteBuf {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class);
|
||||||
private static final String PROP_MODE = "io.netty.buffer.bytebuf.checkAccessible";
|
private static final String LEGACY_PROP_CHECK_ACCESSIBLE = "io.netty.buffer.bytebuf.checkAccessible";
|
||||||
|
private static final String PROP_CHECK_ACCESSIBLE = "io.netty.buffer.checkAccessible";
|
||||||
private static final boolean checkAccessible;
|
private static final boolean checkAccessible;
|
||||||
|
private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds";
|
||||||
|
private static final boolean checkBounds;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
checkAccessible = SystemPropertyUtil.getBoolean(PROP_MODE, true);
|
if (SystemPropertyUtil.contains(PROP_CHECK_ACCESSIBLE)) {
|
||||||
|
checkAccessible = SystemPropertyUtil.getBoolean(PROP_CHECK_ACCESSIBLE, true);
|
||||||
|
} else {
|
||||||
|
checkAccessible = SystemPropertyUtil.getBoolean(LEGACY_PROP_CHECK_ACCESSIBLE, true);
|
||||||
|
}
|
||||||
|
checkBounds = SystemPropertyUtil.getBoolean(PROP_CHECK_BOUNDS, true);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("-D{}: {}", PROP_MODE, checkAccessible);
|
logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible);
|
||||||
|
logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,11 +106,18 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
return readerIndex;
|
return readerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkIndexBounds(final int readerIndex, final int writerIndex, final int capacity) {
|
||||||
|
if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity) {
|
||||||
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
|
"readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
|
||||||
|
readerIndex, writerIndex, capacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf readerIndex(int readerIndex) {
|
public ByteBuf readerIndex(int readerIndex) {
|
||||||
if (readerIndex < 0 || readerIndex > writerIndex) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkIndexBounds(readerIndex, writerIndex, capacity());
|
||||||
"readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
|
|
||||||
}
|
}
|
||||||
this.readerIndex = readerIndex;
|
this.readerIndex = readerIndex;
|
||||||
return this;
|
return this;
|
||||||
@ -114,10 +130,8 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf writerIndex(int writerIndex) {
|
public ByteBuf writerIndex(int writerIndex) {
|
||||||
if (writerIndex < readerIndex || writerIndex > capacity()) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkIndexBounds(readerIndex, writerIndex, capacity());
|
||||||
"writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))",
|
|
||||||
writerIndex, readerIndex, capacity()));
|
|
||||||
}
|
}
|
||||||
this.writerIndex = writerIndex;
|
this.writerIndex = writerIndex;
|
||||||
return this;
|
return this;
|
||||||
@ -125,10 +139,8 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf setIndex(int readerIndex, int writerIndex) {
|
public ByteBuf setIndex(int readerIndex, int writerIndex) {
|
||||||
if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkIndexBounds(readerIndex, writerIndex, capacity());
|
||||||
"readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
|
|
||||||
readerIndex, writerIndex, capacity()));
|
|
||||||
}
|
}
|
||||||
setIndex0(readerIndex, writerIndex);
|
setIndex0(readerIndex, writerIndex);
|
||||||
return this;
|
return this;
|
||||||
@ -271,12 +283,13 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
if (minWritableBytes <= writableBytes()) {
|
if (minWritableBytes <= writableBytes()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (checkBounds) {
|
||||||
if (minWritableBytes > maxCapacity - writerIndex) {
|
if (minWritableBytes > maxCapacity - writerIndex) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
|
"writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
|
||||||
writerIndex, minWritableBytes, maxCapacity, this));
|
writerIndex, minWritableBytes, maxCapacity, this));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize the current capacity to the power of 2.
|
// Normalize the current capacity to the power of 2.
|
||||||
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
|
int newCapacity = alloc().calculateNewCapacity(writerIndex + minWritableBytes, maxCapacity);
|
||||||
@ -618,15 +631,21 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkReadableBounds(final ByteBuf src, final int length) {
|
||||||
|
if (length > src.readableBytes()) {
|
||||||
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
|
"length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf setBytes(int index, ByteBuf src, int length) {
|
public ByteBuf setBytes(int index, ByteBuf src, int length) {
|
||||||
checkIndex(index, length);
|
checkIndex(index, length);
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
throw new NullPointerException("src");
|
throw new NullPointerException("src");
|
||||||
}
|
}
|
||||||
if (length > src.readableBytes()) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkReadableBounds(src, length);
|
||||||
"length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setBytes(index, src, src.readerIndex(), length);
|
setBytes(index, src, src.readerIndex(), length);
|
||||||
@ -889,10 +908,12 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf readBytes(ByteBuf dst, int length) {
|
public ByteBuf readBytes(ByteBuf dst, int length) {
|
||||||
|
if (checkBounds) {
|
||||||
if (length > dst.writableBytes()) {
|
if (length > dst.writableBytes()) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
"length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst));
|
"length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
readBytes(dst, dst.writerIndex(), length);
|
readBytes(dst, dst.writerIndex(), length);
|
||||||
dst.writerIndex(dst.writerIndex() + length);
|
dst.writerIndex(dst.writerIndex() + length);
|
||||||
return this;
|
return this;
|
||||||
@ -1065,9 +1086,8 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf writeBytes(ByteBuf src, int length) {
|
public ByteBuf writeBytes(ByteBuf src, int length) {
|
||||||
if (length > src.readableBytes()) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkReadableBounds(src, length);
|
||||||
"length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
|
|
||||||
}
|
}
|
||||||
writeBytes(src, src.readerIndex(), length);
|
writeBytes(src, src.readerIndex(), length);
|
||||||
src.readerIndex(src.readerIndex() + length);
|
src.readerIndex(src.readerIndex() + length);
|
||||||
@ -1357,26 +1377,30 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
checkIndex0(index, fieldLength);
|
checkIndex0(index, fieldLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
final void checkIndex0(int index, int fieldLength) {
|
private static void checkRangeBounds(final int index, final int fieldLength, final int capacity) {
|
||||||
if (isOutOfBounds(index, fieldLength, capacity())) {
|
if (isOutOfBounds(index, fieldLength, capacity)) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity()));
|
"index: %d, length: %d (expected: range(0, %d))", index, fieldLength, capacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final void checkIndex0(int index, int fieldLength) {
|
||||||
|
if (checkBounds) {
|
||||||
|
checkRangeBounds(index, fieldLength, capacity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
|
protected final void checkSrcIndex(int index, int length, int srcIndex, int srcCapacity) {
|
||||||
checkIndex(index, length);
|
checkIndex(index, length);
|
||||||
if (isOutOfBounds(srcIndex, length, srcCapacity)) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkRangeBounds(srcIndex, length, srcCapacity);
|
||||||
"srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
|
protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
|
||||||
checkIndex(index, length);
|
checkIndex(index, length);
|
||||||
if (isOutOfBounds(dstIndex, length, dstCapacity)) {
|
if (checkBounds) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
checkRangeBounds(dstIndex, length, dstCapacity);
|
||||||
"dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1394,19 +1418,24 @@ public abstract class AbstractByteBuf extends ByteBuf {
|
|||||||
|
|
||||||
protected final void checkNewCapacity(int newCapacity) {
|
protected final void checkNewCapacity(int newCapacity) {
|
||||||
ensureAccessible();
|
ensureAccessible();
|
||||||
|
if (checkBounds) {
|
||||||
if (newCapacity < 0 || newCapacity > maxCapacity()) {
|
if (newCapacity < 0 || newCapacity > maxCapacity()) {
|
||||||
throw new IllegalArgumentException("newCapacity: " + newCapacity + " (expected: 0-" + maxCapacity() + ')');
|
throw new IllegalArgumentException("newCapacity: " + newCapacity +
|
||||||
|
" (expected: 0-" + maxCapacity() + ')');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkReadableBytes0(int minimumReadableBytes) {
|
private void checkReadableBytes0(int minimumReadableBytes) {
|
||||||
ensureAccessible();
|
ensureAccessible();
|
||||||
|
if (checkBounds) {
|
||||||
if (readerIndex > writerIndex - minimumReadableBytes) {
|
if (readerIndex > writerIndex - minimumReadableBytes) {
|
||||||
throw new IndexOutOfBoundsException(String.format(
|
throw new IndexOutOfBoundsException(String.format(
|
||||||
"readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
|
"readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
|
||||||
readerIndex, minimumReadableBytes, writerIndex, this));
|
readerIndex, minimumReadableBytes, writerIndex, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be called by every method that tries to access the buffers content to check
|
* Should be called by every method that tries to access the buffers content to check
|
||||||
|
@ -20,6 +20,7 @@ import io.netty.buffer.PooledByteBufAllocator;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.microbench.util.AbstractMicrobenchmark;
|
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
import org.openjdk.jmh.annotations.Setup;
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
import org.openjdk.jmh.annotations.TearDown;
|
import org.openjdk.jmh.annotations.TearDown;
|
||||||
|
|
||||||
@ -27,10 +28,13 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
public class ByteBufBenchmark extends AbstractMicrobenchmark {
|
public class ByteBufBenchmark extends AbstractMicrobenchmark {
|
||||||
static {
|
static {
|
||||||
System.setProperty("io.netty.buffer.bytebuf.checkAccessible", "false");
|
System.setProperty("io.netty.buffer.checkAccessible", "false");
|
||||||
}
|
}
|
||||||
private static final byte BYTE = '0';
|
private static final byte BYTE = '0';
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public String checkBounds;
|
||||||
|
|
||||||
private ByteBuffer byteBuffer;
|
private ByteBuffer byteBuffer;
|
||||||
private ByteBuffer directByteBuffer;
|
private ByteBuffer directByteBuffer;
|
||||||
private ByteBuf buffer;
|
private ByteBuf buffer;
|
||||||
@ -39,6 +43,7 @@ public class ByteBufBenchmark extends AbstractMicrobenchmark {
|
|||||||
|
|
||||||
@Setup
|
@Setup
|
||||||
public void setup() {
|
public void setup() {
|
||||||
|
System.setProperty("io.netty.buffer.checkBounds", checkBounds);
|
||||||
byteBuffer = ByteBuffer.allocate(8);
|
byteBuffer = ByteBuffer.allocate(8);
|
||||||
directByteBuffer = ByteBuffer.allocateDirect(8);
|
directByteBuffer = ByteBuffer.allocateDirect(8);
|
||||||
buffer = Unpooled.buffer(8);
|
buffer = Unpooled.buffer(8);
|
||||||
|
@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.microbench.util.AbstractMicrobenchmark;
|
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
import org.openjdk.jmh.annotations.Setup;
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
import org.openjdk.jmh.annotations.TearDown;
|
import org.openjdk.jmh.annotations.TearDown;
|
||||||
|
|
||||||
@ -26,6 +27,9 @@ import java.lang.reflect.Constructor;
|
|||||||
|
|
||||||
public class HeapByteBufBenchmark extends AbstractMicrobenchmark {
|
public class HeapByteBufBenchmark extends AbstractMicrobenchmark {
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public String checkBounds;
|
||||||
|
|
||||||
private ByteBuf unsafeBuffer;
|
private ByteBuf unsafeBuffer;
|
||||||
private ByteBuf buffer;
|
private ByteBuf buffer;
|
||||||
|
|
||||||
@ -39,6 +43,7 @@ public class HeapByteBufBenchmark extends AbstractMicrobenchmark {
|
|||||||
|
|
||||||
@Setup
|
@Setup
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
|
System.setProperty("io.netty.buffer.bytebuf.checkBounds", checkBounds);
|
||||||
unsafeBuffer = newBuffer("io.netty.buffer.UnpooledUnsafeHeapByteBuf");
|
unsafeBuffer = newBuffer("io.netty.buffer.UnpooledUnsafeHeapByteBuf");
|
||||||
buffer = newBuffer("io.netty.buffer.UnpooledHeapByteBuf");
|
buffer = newBuffer("io.netty.buffer.UnpooledHeapByteBuf");
|
||||||
unsafeBuffer.writeLong(1L);
|
unsafeBuffer.writeLong(1L);
|
||||||
|
Loading…
Reference in New Issue
Block a user