diff --git a/.gitignore b/.gitignore
index a3c75313a21c..ed43883f5ba7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,9 @@
hs_err_pid*.log
dependency-reduced-pom.xml
+
+*/.unison.*
+
+# exclude mainframer files
+mainframer
+.mainframer
diff --git a/.mvn/settings.xml b/.mvn/settings.xml
new file mode 100644
index 000000000000..1f7f6fafec76
--- /dev/null
+++ b/.mvn/settings.xml
@@ -0,0 +1,9 @@
+
+
+
+ sonatype-nexus-snapshots
+ ${env.SANOTYPE_USER}
+ ${env.SANOTYPE_PASSWORD}
+
+
+
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b35e822464d3..000000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-language: java
-jdk:
- - oraclejdk7
- - openjdk7
-branches:
- only:
- - master
- - 3
- - 3.5
-before_install: 'mvn -version'
-install: 'mvn clean install -Pfull -DskipTests'
-
diff --git a/all/pom.xml b/all/pom.xml
index b0b519419597..e0ca51fc0bcb 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -17,11 +17,11 @@
4.0.0
-
+
io.nettynetty-parent
- 4.1.25.5.dse
+ 4.1.34.3.dsenetty-all
@@ -32,6 +32,7 @@
${project.build.directory}/src${project.build.directory}/versions
+ true
diff --git a/bom/pom.xml b/bom/pom.xml
index b8542d9fca65..b78da2b70dc1 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -16,16 +16,16 @@
-->
4.0.0
-
+
io.nettynetty-bom
- 4.1.25.dse
+ 4.1.34.3.dsepomNetty/BOM
@@ -49,7 +49,6 @@
https://github.com/netty/nettyscm:git:git://github.com/netty/netty.gitscm:git:ssh://git@github.com/netty/netty.git
- netty-4.1.25.dse
@@ -69,165 +68,165 @@
io.nettynetty-buffer
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-dns
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-haproxy
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-http
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-http2
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-memcache
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-mqtt
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-redis
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-smtp
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-socks
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-stomp
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-codec-xml
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-common
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-dev-tools
- 4.1.25.dse
+ 4.1.34.3.dseio.nettynetty-handler
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-handler-proxy
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-resolver
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-resolver-dns
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-rxtx
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-sctp
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-udt
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-example
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-all
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-native-unix-common
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-native-unix-common
- 4.1.25.5.dse
+ 4.1.34.3.dselinux-x86_64io.nettynetty-transport-native-unix-common
- 4.1.25.5.dse
+ 4.1.34.3.dseosx-x86_64io.nettynetty-transport-native-epoll
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-native-epoll
- 4.1.25.5.dse
+ 4.1.34.3.dselinux-x86_64io.nettynetty-transport-native-kqueue
- 4.1.25.5.dse
+ 4.1.34.3.dseio.nettynetty-transport-native-kqueue
- 4.1.25.5.dse
+ 4.1.34.3.dseosx-x86_64
diff --git a/buffer/pom.xml b/buffer/pom.xml
index 6ee9d013b0eb..4e5586497c95 100644
--- a/buffer/pom.xml
+++ b/buffer/pom.xml
@@ -20,7 +20,7 @@
io.nettynetty-parent
- 4.1.25.5.dse
+ 4.1.34.3.dsenetty-buffer
diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
index 4a13d821b933..ad2f793b1abf 100644
--- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java
@@ -15,6 +15,7 @@
*/
package io.netty.buffer;
+import io.netty.util.AsciiString;
import io.netty.util.ByteProcessor;
import io.netty.util.CharsetUtil;
import io.netty.util.IllegalReferenceCountException;
@@ -37,19 +38,29 @@
import java.nio.charset.Charset;
import static io.netty.util.internal.MathUtil.isOutOfBounds;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
/**
* A skeletal implementation of a buffer.
*/
public abstract class AbstractByteBuf extends ByteBuf {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractByteBuf.class);
- private static final String PROP_MODE = "io.netty.buffer.bytebuf.checkAccessible";
- private static final boolean 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";
+ static final boolean checkAccessible; // accessed from CompositeByteBuf
+ private static final String PROP_CHECK_BOUNDS = "io.netty.buffer.checkBounds";
+ private static final boolean checkBounds;
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()) {
- logger.debug("-D{}: {}", PROP_MODE, checkAccessible);
+ logger.debug("-D{}: {}", PROP_CHECK_ACCESSIBLE, checkAccessible);
+ logger.debug("-D{}: {}", PROP_CHECK_BOUNDS, checkBounds);
}
}
@@ -63,9 +74,7 @@ public abstract class AbstractByteBuf extends ByteBuf {
private int maxCapacity;
protected AbstractByteBuf(int maxCapacity) {
- if (maxCapacity < 0) {
- throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)");
- }
+ checkPositiveOrZero(maxCapacity, "maxCapacity");
this.maxCapacity = maxCapacity;
}
@@ -97,11 +106,18 @@ public int 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
public ByteBuf readerIndex(int readerIndex) {
- if (readerIndex < 0 || readerIndex > writerIndex) {
- throw new IndexOutOfBoundsException(String.format(
- "readerIndex: %d (expected: 0 <= readerIndex <= writerIndex(%d))", readerIndex, writerIndex));
+ if (checkBounds) {
+ checkIndexBounds(readerIndex, writerIndex, capacity());
}
this.readerIndex = readerIndex;
return this;
@@ -114,10 +130,8 @@ public int writerIndex() {
@Override
public ByteBuf writerIndex(int writerIndex) {
- if (writerIndex < readerIndex || writerIndex > capacity()) {
- throw new IndexOutOfBoundsException(String.format(
- "writerIndex: %d (expected: readerIndex(%d) <= writerIndex <= capacity(%d))",
- writerIndex, readerIndex, capacity()));
+ if (checkBounds) {
+ checkIndexBounds(readerIndex, writerIndex, capacity());
}
this.writerIndex = writerIndex;
return this;
@@ -125,10 +139,8 @@ public ByteBuf writerIndex(int writerIndex) {
@Override
public ByteBuf setIndex(int readerIndex, int writerIndex) {
- 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()));
+ if (checkBounds) {
+ checkIndexBounds(readerIndex, writerIndex, capacity());
}
setIndex0(readerIndex, writerIndex);
return this;
@@ -258,10 +270,7 @@ protected final void adjustMarkers(int decrement) {
@Override
public ByteBuf ensureWritable(int minWritableBytes) {
- if (minWritableBytes < 0) {
- throw new IllegalArgumentException(String.format(
- "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
- }
+ checkPositiveOrZero(minWritableBytes, "minWritableBytes");
ensureWritable0(minWritableBytes);
return this;
}
@@ -271,11 +280,12 @@ final void ensureWritable0(int minWritableBytes) {
if (minWritableBytes <= writableBytes()) {
return;
}
-
- if (minWritableBytes > maxCapacity - writerIndex) {
- throw new IndexOutOfBoundsException(String.format(
- "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
- writerIndex, minWritableBytes, maxCapacity, this));
+ if (checkBounds) {
+ if (minWritableBytes > maxCapacity - writerIndex) {
+ throw new IndexOutOfBoundsException(String.format(
+ "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",
+ writerIndex, minWritableBytes, maxCapacity, this));
+ }
}
// Normalize the current capacity to the power of 2.
@@ -288,10 +298,7 @@ final void ensureWritable0(int minWritableBytes) {
@Override
public int ensureWritable(int minWritableBytes, boolean force) {
ensureAccessible();
- if (minWritableBytes < 0) {
- throw new IllegalArgumentException(String.format(
- "minWritableBytes: %d (expected: >= 0)", minWritableBytes));
- }
+ checkPositiveOrZero(minWritableBytes, "minWritableBytes");
if (minWritableBytes <= writableBytes()) {
return 0;
@@ -318,12 +325,12 @@ public int ensureWritable(int minWritableBytes, boolean force) {
@Override
public ByteBuf order(ByteOrder endianness) {
- if (endianness == null) {
- throw new NullPointerException("endianness");
- }
if (endianness == order()) {
return this;
}
+ if (endianness == null) {
+ throw new NullPointerException("endianness");
+ }
return newSwappedByteBuf();
}
@@ -490,7 +497,10 @@ public ByteBuf getBytes(int index, ByteBuf dst, int length) {
@Override
public CharSequence getCharSequence(int index, int length, Charset charset) {
- // TODO: We could optimize this for UTF8 and US_ASCII
+ if (CharsetUtil.US_ASCII.equals(charset) || CharsetUtil.ISO_8859_1.equals(charset)) {
+ // ByteBufUtil.getBytes(...) will return a new copy which the AsciiString uses directly
+ return new AsciiString(ByteBufUtil.getBytes(this, index, length, true), false);
+ }
return toString(index, length, charset);
}
@@ -618,15 +628,21 @@ public ByteBuf setBytes(int index, ByteBuf src) {
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
public ByteBuf setBytes(int index, ByteBuf src, int length) {
checkIndex(index, length);
if (src == null) {
throw new NullPointerException("src");
}
- if (length > src.readableBytes()) {
- throw new IndexOutOfBoundsException(String.format(
- "length(%d) exceeds src.readableBytes(%d) where src is: %s", length, src.readableBytes(), src));
+ if (checkBounds) {
+ checkReadableBounds(src, length);
}
setBytes(index, src, src.readerIndex(), length);
@@ -889,9 +905,11 @@ public ByteBuf readBytes(ByteBuf dst) {
@Override
public ByteBuf readBytes(ByteBuf dst, int length) {
- if (length > dst.writableBytes()) {
- throw new IndexOutOfBoundsException(String.format(
- "length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst));
+ if (checkBounds) {
+ if (length > dst.writableBytes()) {
+ throw new IndexOutOfBoundsException(String.format(
+ "length(%d) exceeds dst.writableBytes(%d) where dst is: %s", length, dst.writableBytes(), dst));
+ }
}
readBytes(dst, dst.writerIndex(), length);
dst.writerIndex(dst.writerIndex() + length);
@@ -1065,9 +1083,8 @@ public ByteBuf writeBytes(ByteBuf src) {
@Override
public ByteBuf writeBytes(ByteBuf src, 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));
+ if (checkBounds) {
+ checkReadableBounds(src, length);
}
writeBytes(src, src.readerIndex(), length);
src.readerIndex(src.readerIndex() + length);
@@ -1269,7 +1286,7 @@ public int forEachByte(int index, int length, ByteProcessor processor) {
}
}
- private int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
+ int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
for (; start < end; ++start) {
if (!processor.process(_getByte(start))) {
return start;
@@ -1301,7 +1318,7 @@ public int forEachByteDesc(int index, int length, ByteProcessor processor) {
}
}
- private int forEachByteDesc0(int rStart, final int rEnd, ByteProcessor processor) throws Exception {
+ int forEachByteDesc0(int rStart, final int rEnd, ByteProcessor processor) throws Exception {
for (; rStart >= rEnd; --rStart) {
if (!processor.process(_getByte(rStart))) {
return rStart;
@@ -1357,26 +1374,30 @@ protected final void checkIndex(int index, int fieldLength) {
checkIndex0(index, fieldLength);
}
- final void checkIndex0(int index, int fieldLength) {
- if (isOutOfBounds(index, fieldLength, capacity())) {
+ private static void checkRangeBounds(final int index, final int fieldLength, final int capacity) {
+ if (isOutOfBounds(index, fieldLength, capacity)) {
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) {
checkIndex(index, length);
- if (isOutOfBounds(srcIndex, length, srcCapacity)) {
- throw new IndexOutOfBoundsException(String.format(
- "srcIndex: %d, length: %d (expected: range(0, %d))", srcIndex, length, srcCapacity));
+ if (checkBounds) {
+ checkRangeBounds(srcIndex, length, srcCapacity);
}
}
protected final void checkDstIndex(int index, int length, int dstIndex, int dstCapacity) {
checkIndex(index, length);
- if (isOutOfBounds(dstIndex, length, dstCapacity)) {
- throw new IndexOutOfBoundsException(String.format(
- "dstIndex: %d, length: %d (expected: range(0, %d))", dstIndex, length, dstCapacity));
+ if (checkBounds) {
+ checkRangeBounds(dstIndex, length, dstCapacity);
}
}
@@ -1386,25 +1407,28 @@ protected final void checkDstIndex(int index, int length, int dstIndex, int dstC
* than the specified value.
*/
protected final void checkReadableBytes(int minimumReadableBytes) {
- if (minimumReadableBytes < 0) {
- throw new IllegalArgumentException("minimumReadableBytes: " + minimumReadableBytes + " (expected: >= 0)");
- }
+ checkPositiveOrZero(minimumReadableBytes, "minimumReadableBytes");
checkReadableBytes0(minimumReadableBytes);
}
protected final void checkNewCapacity(int newCapacity) {
ensureAccessible();
- if (newCapacity < 0 || newCapacity > maxCapacity()) {
- throw new IllegalArgumentException("newCapacity: " + newCapacity + " (expected: 0-" + maxCapacity() + ')');
+ if (checkBounds) {
+ if (newCapacity < 0 || newCapacity > maxCapacity()) {
+ throw new IllegalArgumentException("newCapacity: " + newCapacity +
+ " (expected: 0-" + maxCapacity() + ')');
+ }
}
}
private void checkReadableBytes0(int minimumReadableBytes) {
ensureAccessible();
- if (readerIndex > writerIndex - minimumReadableBytes) {
- throw new IndexOutOfBoundsException(String.format(
- "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
- readerIndex, minimumReadableBytes, writerIndex, this));
+ if (checkBounds) {
+ if (readerIndex > writerIndex - minimumReadableBytes) {
+ throw new IndexOutOfBoundsException(String.format(
+ "readerIndex(%d) + length(%d) exceeds writerIndex(%d): %s",
+ readerIndex, minimumReadableBytes, writerIndex, this));
+ }
}
}
@@ -1413,7 +1437,7 @@ private void checkReadableBytes0(int minimumReadableBytes) {
* if the buffer was released before.
*/
protected final void ensureAccessible() {
- if (checkAccessible && refCnt() == 0) {
+ if (checkAccessible && !isAccessible()) {
throw new IllegalReferenceCountException(0);
}
}
diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java
index 40525144e3f3..920b3fb46d04 100644
--- a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java
+++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java
@@ -16,6 +16,8 @@
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakTracker;
import io.netty.util.internal.PlatformDependent;
@@ -222,9 +224,7 @@ public CompositeByteBuf compositeDirectBuffer(int maxNumComponents) {
}
private static void validate(int initialCapacity, int maxCapacity) {
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("initialCapacity: " + initialCapacity + " (expected: 0+)");
- }
+ checkPositiveOrZero(initialCapacity, "initialCapacity");
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity: %d (expected: not greater than maxCapacity(%d)",
@@ -249,9 +249,7 @@ public String toString() {
@Override
public int calculateNewCapacity(int minNewCapacity, int maxCapacity) {
- if (minNewCapacity < 0) {
- throw new IllegalArgumentException("minNewCapacity: " + minNewCapacity + " (expected: 0+)");
- }
+ checkPositiveOrZero(minNewCapacity, "minNewCapacity");
if (minNewCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"minNewCapacity: %d (expected: not greater than maxCapacity(%d)",
diff --git a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java
index 58f1d907a156..40844020dd21 100644
--- a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java
@@ -31,6 +31,11 @@ protected AbstractDerivedByteBuf(int maxCapacity) {
super(maxCapacity);
}
+ @Override
+ final boolean isAccessible() {
+ return unwrap().isAccessible();
+ }
+
@Override
public final int refCnt() {
return refCnt0();
diff --git a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java
index d624d855f4da..548bdba5e493 100644
--- a/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/AbstractReferenceCountedByteBuf.java
@@ -17,6 +17,7 @@
package io.netty.buffer;
import io.netty.util.IllegalReferenceCountException;
+import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@@ -26,27 +27,68 @@
* Abstract base class for {@link ByteBuf} implementations that count references.
*/
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
-
+ private static final long REFCNT_FIELD_OFFSET;
private static final AtomicIntegerFieldUpdater refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
- private volatile int refCnt;
+ // even => "real" refcount is (refCnt >>> 1); odd => "real" refcount is 0
+ @SuppressWarnings("unused")
+ private volatile int refCnt = 2;
+
+ static {
+ long refCntFieldOffset = -1;
+ try {
+ if (PlatformDependent.hasUnsafe()) {
+ refCntFieldOffset = PlatformDependent.objectFieldOffset(
+ AbstractReferenceCountedByteBuf.class.getDeclaredField("refCnt"));
+ }
+ } catch (Throwable ignore) {
+ refCntFieldOffset = -1;
+ }
+
+ REFCNT_FIELD_OFFSET = refCntFieldOffset;
+ }
+
+ private static int realRefCnt(int rawCnt) {
+ return (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
+ }
protected AbstractReferenceCountedByteBuf(int maxCapacity) {
super(maxCapacity);
- refCntUpdater.set(this, 1);
+ }
+
+ private int nonVolatileRawCnt() {
+ // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles.
+ return REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET)
+ : refCntUpdater.get(this);
+ }
+
+ @Override
+ boolean isAccessible() {
+ // Try to do non-volatile read for performance as the ensureAccessible() is racy anyway and only provide
+ // a best-effort guard.
+
+ // This is copied explicitly from the nonVolatileRawCnt() method above to reduce call stack depth,
+ // to avoid hitting the default limit for inlining (9)
+ final int rawCnt = REFCNT_FIELD_OFFSET != -1 ? PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET)
+ : refCntUpdater.get(this);
+
+ // The "real" ref count is > 0 if the rawCnt is even.
+ // (x & y) appears to be surprisingly expensive relative to (x == y). Thus the expression below provides
+ // a fast path for most common cases where the ref count is 1, 2, 3 or 4.
+ return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0;
}
@Override
public int refCnt() {
- return refCnt;
+ return realRefCnt(refCntUpdater.get(this));
}
/**
* An unsafe operation intended for use by a subclass that sets the reference count of the buffer directly
*/
- protected final void setRefCnt(int refCnt) {
- refCntUpdater.set(this, refCnt);
+ protected final void setRefCnt(int newRefCnt) {
+ refCntUpdater.set(this, newRefCnt << 1); // overflow OK here
}
@Override
@@ -60,11 +102,18 @@ public ByteBuf retain(int increment) {
}
private ByteBuf retain0(final int increment) {
- int oldRef = refCntUpdater.getAndAdd(this, increment);
- if (oldRef <= 0 || oldRef + increment < oldRef) {
- // Ensure we don't resurrect (which means the refCnt was 0) and also that we encountered an overflow.
- refCntUpdater.getAndAdd(this, -increment);
- throw new IllegalReferenceCountException(oldRef, increment);
+ // all changes to the raw count are 2x the "real" change
+ int adjustedIncrement = increment << 1; // overflow OK here
+ int oldRef = refCntUpdater.getAndAdd(this, adjustedIncrement);
+ if ((oldRef & 1) != 0) {
+ throw new IllegalReferenceCountException(0, increment);
+ }
+ // don't pass 0!
+ if ((oldRef <= 0 && oldRef + adjustedIncrement >= 0)
+ || (oldRef >= 0 && oldRef + adjustedIncrement < oldRef)) {
+ // overflow case
+ refCntUpdater.getAndAdd(this, -adjustedIncrement);
+ throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
}
return this;
}
@@ -90,17 +139,57 @@ public boolean release(int decrement) {
}
private boolean release0(int decrement) {
- int oldRef = refCntUpdater.getAndAdd(this, -decrement);
- if (oldRef == decrement) {
- deallocate();
- return true;
- } else if (oldRef < decrement || oldRef - decrement > oldRef) {
- // Ensure we don't over-release, and avoid underflow.
- refCntUpdater.getAndAdd(this, decrement);
- throw new IllegalReferenceCountException(oldRef, -decrement);
+ int rawCnt = nonVolatileRawCnt(), realCnt = toLiveRealCnt(rawCnt, decrement);
+ if (decrement == realCnt) {
+ if (refCntUpdater.compareAndSet(this, rawCnt, 1)) {
+ deallocate();
+ return true;
+ }
+ return retryRelease0(decrement);
}
- return false;
+ return releaseNonFinal0(decrement, rawCnt, realCnt);
}
+
+ private boolean releaseNonFinal0(int decrement, int rawCnt, int realCnt) {
+ if (decrement < realCnt
+ // all changes to the raw count are 2x the "real" change
+ && refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) {
+ return false;
+ }
+ return retryRelease0(decrement);
+ }
+
+ private boolean retryRelease0(int decrement) {
+ for (;;) {
+ int rawCnt = refCntUpdater.get(this), realCnt = toLiveRealCnt(rawCnt, decrement);
+ if (decrement == realCnt) {
+ if (refCntUpdater.compareAndSet(this, rawCnt, 1)) {
+ deallocate();
+ return true;
+ }
+ } else if (decrement < realCnt) {
+ // all changes to the raw count are 2x the "real" change
+ if (refCntUpdater.compareAndSet(this, rawCnt, rawCnt - (decrement << 1))) {
+ return false;
+ }
+ } else {
+ throw new IllegalReferenceCountException(realCnt, -decrement);
+ }
+ Thread.yield(); // this benefits throughput under high contention
+ }
+ }
+
+ /**
+ * Like {@link #realRefCnt(int)} but throws if refCnt == 0
+ */
+ private static int toLiveRealCnt(int rawCnt, int decrement) {
+ if ((rawCnt & 1) == 0) {
+ return rawCnt >>> 1;
+ }
+ // odd rawCnt => already deallocated
+ throw new IllegalReferenceCountException(0, -decrement);
+ }
+
/**
* Called once {@link #refCnt()} is equals 0.
*/
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java
index c04340a0d15e..83ecc6685a5b 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java
@@ -258,14 +258,14 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable {
* capacity, the content of this buffer is truncated. If the {@code newCapacity} is greater
* than the current capacity, the buffer is appended with unspecified data whose length is
* {@code (newCapacity - currentCapacity)}.
+ *
+ * @throws IllegalArgumentException if the {@code newCapacity} is greater than {@link #maxCapacity()}
*/
public abstract ByteBuf capacity(int newCapacity);
/**
- * Returns the maximum allowed capacity of this buffer. If a user attempts to increase the
- * capacity of this buffer beyond the maximum capacity using {@link #capacity(int)} or
- * {@link #ensureWritable(int)}, those methods will raise an
- * {@link IllegalArgumentException}.
+ * Returns the maximum allowed capacity of this buffer. This value provides an upper
+ * bound on {@link #capacity()}.
*/
public abstract int maxCapacity();
@@ -513,22 +513,23 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable {
public abstract ByteBuf discardSomeReadBytes();
/**
- * Makes sure the number of {@linkplain #writableBytes() the writable bytes}
- * is equal to or greater than the specified value. If there is enough
- * writable bytes in this buffer, this method returns with no side effect.
- * Otherwise, it raises an {@link IllegalArgumentException}.
+ * Expands the buffer {@link #capacity()} to make sure the number of
+ * {@linkplain #writableBytes() writable bytes} is equal to or greater than the
+ * specified value. If there are enough writable bytes in this buffer, this method
+ * returns with no side effect.
*
* @param minWritableBytes
* the expected minimum number of writable bytes
* @throws IndexOutOfBoundsException
- * if {@link #writerIndex()} + {@code minWritableBytes} > {@link #maxCapacity()}
+ * if {@link #writerIndex()} + {@code minWritableBytes} > {@link #maxCapacity()}.
+ * @see #capacity(int)
*/
public abstract ByteBuf ensureWritable(int minWritableBytes);
/**
- * Tries to make sure the number of {@linkplain #writableBytes() the writable bytes}
- * is equal to or greater than the specified value. Unlike {@link #ensureWritable(int)},
- * this method does not raise an exception but returns a code.
+ * Expands the buffer {@link #capacity()} to make sure the number of
+ * {@linkplain #writableBytes() writable bytes} is equal to or greater than the
+ * specified value. Unlike {@link #ensureWritable(int)}, this method returns a status code.
*
* @param minWritableBytes
* the expected minimum number of writable bytes
@@ -1756,9 +1757,8 @@ public double readDoubleLE() {
/**
* Sets the specified boolean at the current {@code writerIndex}
* and increases the {@code writerIndex} by {@code 1} in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 1}
+ * If {@code this.writableBytes} is less than {@code 1}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeBoolean(boolean value);
@@ -1766,9 +1766,8 @@ public double readDoubleLE() {
* Sets the specified byte at the current {@code writerIndex}
* and increases the {@code writerIndex} by {@code 1} in this buffer.
* The 24 high-order bits of the specified value are ignored.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 1}
+ * If {@code this.writableBytes} is less than {@code 1}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeByte(int value);
@@ -1776,9 +1775,8 @@ public double readDoubleLE() {
* Sets the specified 16-bit short integer at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 2}
* in this buffer. The 16 high-order bits of the specified value are ignored.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 2}
+ * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeShort(int value);
@@ -1787,9 +1785,8 @@ public double readDoubleLE() {
* Order at the current {@code writerIndex} and increases the
* {@code writerIndex} by {@code 2} in this buffer.
* The 16 high-order bits of the specified value are ignored.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 2}
+ * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeShortLE(int value);
@@ -1797,9 +1794,8 @@ public double readDoubleLE() {
* Sets the specified 24-bit medium integer at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 3}
* in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 3}
+ * If {@code this.writableBytes} is less than {@code 3}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeMedium(int value);
@@ -1808,18 +1804,16 @@ public double readDoubleLE() {
* {@code writerIndex} in the Little Endian Byte Order and
* increases the {@code writerIndex} by {@code 3} in this
* buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 3}
+ * If {@code this.writableBytes} is less than {@code 3}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeMediumLE(int value);
/**
* Sets the specified 32-bit integer at the current {@code writerIndex}
* and increases the {@code writerIndex} by {@code 4} in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 4}
+ * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeInt(int value);
@@ -1827,9 +1821,8 @@ public double readDoubleLE() {
* Sets the specified 32-bit integer at the current {@code writerIndex}
* in the Little Endian Byte Order and increases the {@code writerIndex}
* by {@code 4} in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 4}
+ * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeIntLE(int value);
@@ -1837,9 +1830,8 @@ public double readDoubleLE() {
* Sets the specified 64-bit long integer at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 8}
* in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 8}
+ * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeLong(long value);
@@ -1848,9 +1840,8 @@ public double readDoubleLE() {
* {@code writerIndex} in the Little Endian Byte Order and
* increases the {@code writerIndex} by {@code 8}
* in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 8}
+ * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeLongLE(long value);
@@ -1858,9 +1849,8 @@ public double readDoubleLE() {
* Sets the specified 2-byte UTF-16 character at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 2}
* in this buffer. The 16 high-order bits of the specified value are ignored.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 2}
+ * If {@code this.writableBytes} is less than {@code 2}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeChar(int value);
@@ -1868,9 +1858,8 @@ public double readDoubleLE() {
* Sets the specified 32-bit floating point number at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 4}
* in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 4}
+ * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeFloat(float value);
@@ -1878,9 +1867,8 @@ public double readDoubleLE() {
* Sets the specified 32-bit floating point number at the current
* {@code writerIndex} in Little Endian Byte Order and increases
* the {@code writerIndex} by {@code 4} in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 4}
+ * If {@code this.writableBytes} is less than {@code 4}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public ByteBuf writeFloatLE(float value) {
return writeIntLE(Float.floatToRawIntBits(value));
@@ -1890,9 +1878,8 @@ public ByteBuf writeFloatLE(float value) {
* Sets the specified 64-bit floating point number at the current
* {@code writerIndex} and increases the {@code writerIndex} by {@code 8}
* in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 8}
+ * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeDouble(double value);
@@ -1900,9 +1887,8 @@ public ByteBuf writeFloatLE(float value) {
* Sets the specified 64-bit floating point number at the current
* {@code writerIndex} in Little Endian Byte Order and increases
* the {@code writerIndex} by {@code 8} in this buffer.
- *
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is less than {@code 8}
+ * If {@code this.writableBytes} is less than {@code 8}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public ByteBuf writeDoubleLE(double value) {
return writeLongLE(Double.doubleToRawLongBits(value));
@@ -1917,10 +1903,9 @@ public ByteBuf writeDoubleLE(double value) {
* increases the {@code readerIndex} of the source buffer by the number of
* the transferred bytes while {@link #writeBytes(ByteBuf, int, int)}
* does not.
- *
- * @throws IndexOutOfBoundsException
- * if {@code src.readableBytes} is greater than
- * {@code this.writableBytes}
+ * If {@code this.writableBytes} is less than {@code src.readableBytes},
+ * {@link #ensureWritable(int)} will be called in an attempt to expand
+ * capacity to accommodate.
*/
public abstract ByteBuf writeBytes(ByteBuf src);
@@ -1932,12 +1917,11 @@ public ByteBuf writeDoubleLE(double value) {
* except that this method increases the {@code readerIndex} of the source
* buffer by the number of the transferred bytes (= {@code length}) while
* {@link #writeBytes(ByteBuf, int, int)} does not.
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param length the number of bytes to transfer
- *
- * @throws IndexOutOfBoundsException
- * if {@code length} is greater than {@code this.writableBytes} or
- * if {@code length} is greater then {@code src.readableBytes}
+ * @throws IndexOutOfBoundsException if {@code length} is greater then {@code src.readableBytes}
*/
public abstract ByteBuf writeBytes(ByteBuf src, int length);
@@ -1945,15 +1929,15 @@ public ByteBuf writeDoubleLE(double value) {
* Transfers the specified source buffer's data to this buffer starting at
* the current {@code writerIndex} and increases the {@code writerIndex}
* by the number of the transferred bytes (= {@code length}).
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param srcIndex the first index of the source
* @param length the number of bytes to transfer
*
* @throws IndexOutOfBoundsException
- * if the specified {@code srcIndex} is less than {@code 0},
- * if {@code srcIndex + length} is greater than
- * {@code src.capacity}, or
- * if {@code length} is greater than {@code this.writableBytes}
+ * if the specified {@code srcIndex} is less than {@code 0}, or
+ * if {@code srcIndex + length} is greater than {@code src.capacity}
*/
public abstract ByteBuf writeBytes(ByteBuf src, int srcIndex, int length);
@@ -1961,9 +1945,8 @@ public ByteBuf writeDoubleLE(double value) {
* Transfers the specified source array's data to this buffer starting at
* the current {@code writerIndex} and increases the {@code writerIndex}
* by the number of the transferred bytes (= {@code src.length}).
- *
- * @throws IndexOutOfBoundsException
- * if {@code src.length} is greater than {@code this.writableBytes}
+ * If {@code this.writableBytes} is less than {@code src.length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*/
public abstract ByteBuf writeBytes(byte[] src);
@@ -1971,15 +1954,15 @@ public ByteBuf writeDoubleLE(double value) {
* Transfers the specified source array's data to this buffer starting at
* the current {@code writerIndex} and increases the {@code writerIndex}
* by the number of the transferred bytes (= {@code length}).
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param srcIndex the first index of the source
* @param length the number of bytes to transfer
*
* @throws IndexOutOfBoundsException
- * if the specified {@code srcIndex} is less than {@code 0},
- * if {@code srcIndex + length} is greater than
- * {@code src.length}, or
- * if {@code length} is greater than {@code this.writableBytes}
+ * if the specified {@code srcIndex} is less than {@code 0}, or
+ * if {@code srcIndex + length} is greater than {@code src.length}
*/
public abstract ByteBuf writeBytes(byte[] src, int srcIndex, int length);
@@ -1988,10 +1971,9 @@ public ByteBuf writeDoubleLE(double value) {
* the current {@code writerIndex} until the source buffer's position
* reaches its limit, and increases the {@code writerIndex} by the
* number of the transferred bytes.
- *
- * @throws IndexOutOfBoundsException
- * if {@code src.remaining()} is greater than
- * {@code this.writableBytes}
+ * If {@code this.writableBytes} is less than {@code src.remaining()},
+ * {@link #ensureWritable(int)} will be called in an attempt to expand
+ * capacity to accommodate.
*/
public abstract ByteBuf writeBytes(ByteBuffer src);
@@ -1999,29 +1981,28 @@ public ByteBuf writeDoubleLE(double value) {
* Transfers the content of the specified stream to this buffer
* starting at the current {@code writerIndex} and increases the
* {@code writerIndex} by the number of the transferred bytes.
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param length the number of bytes to transfer
*
* @return the actual number of bytes read in from the specified stream
*
- * @throws IndexOutOfBoundsException
- * if {@code length} is greater than {@code this.writableBytes}
- * @throws IOException
- * if the specified stream threw an exception during I/O
+ * @throws IOException if the specified stream threw an exception during I/O
*/
- public abstract int writeBytes(InputStream in, int length) throws IOException;
+ public abstract int writeBytes(InputStream in, int length) throws IOException;
/**
* Transfers the content of the specified channel to this buffer
* starting at the current {@code writerIndex} and increases the
* {@code writerIndex} by the number of the transferred bytes.
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param length the maximum number of bytes to transfer
*
* @return the actual number of bytes read in from the specified channel
*
- * @throws IndexOutOfBoundsException
- * if {@code length} is greater than {@code this.writableBytes}
* @throws IOException
* if the specified channel threw an exception during I/O
*/
@@ -2032,14 +2013,14 @@ public ByteBuf writeDoubleLE(double value) {
* to this buffer starting at the current {@code writerIndex} and increases the
* {@code writerIndex} by the number of the transferred bytes.
* This method does not modify the channel's position.
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param position the file position at which the transfer is to begin
* @param length the maximum number of bytes to transfer
*
* @return the actual number of bytes read in from the specified channel
*
- * @throws IndexOutOfBoundsException
- * if {@code length} is greater than {@code this.writableBytes}
* @throws IOException
* if the specified channel threw an exception during I/O
*/
@@ -2049,11 +2030,10 @@ public ByteBuf writeDoubleLE(double value) {
* Fills this buffer with NUL (0x00) starting at the current
* {@code writerIndex} and increases the {@code writerIndex} by the
* specified {@code length}.
+ * If {@code this.writableBytes} is less than {@code length}, {@link #ensureWritable(int)}
+ * will be called in an attempt to expand capacity to accommodate.
*
* @param length the number of NULs to write to the buffer
- *
- * @throws IndexOutOfBoundsException
- * if {@code length} is greater than {@code this.writableBytes}
*/
public abstract ByteBuf writeZero(int length);
@@ -2061,12 +2041,12 @@ public ByteBuf writeDoubleLE(double value) {
* Writes the specified {@link CharSequence} at the current {@code writerIndex} and increases
* the {@code writerIndex} by the written bytes.
* in this buffer.
+ * If {@code this.writableBytes} is not large enough to write the whole sequence,
+ * {@link #ensureWritable(int)} will be called in an attempt to expand capacity to accommodate.
*
* @param sequence to write
* @param charset that should be used
* @return the written number of bytes
- * @throws IndexOutOfBoundsException
- * if {@code this.writableBytes} is not large enough to write the whole sequence
*/
public abstract int writeCharSequence(CharSequence sequence, Charset charset);
@@ -2465,4 +2445,12 @@ public ByteBuf writeDoubleLE(double value) {
@Override
public abstract ByteBuf touch(Object hint);
+
+ /**
+ * Used internally by {@link AbstractByteBuf#ensureAccessible()} to try to guard
+ * against using the buffer after it was released (best-effort).
+ */
+ boolean isAccessible() {
+ return refCnt() != 0;
+ }
}
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java
index 2d8d34d32317..038cd8db459b 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBufInputStream.java
@@ -16,6 +16,7 @@
package io.netty.buffer;
import io.netty.util.ReferenceCounted;
+import io.netty.util.internal.StringUtil;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -240,17 +241,18 @@ public int readInt() throws IOException {
return buffer.readInt();
}
- private final StringBuilder lineBuf = new StringBuilder();
+ private StringBuilder lineBuf;
@Override
public String readLine() throws IOException {
- lineBuf.setLength(0);
-
- loop: while (true) {
- if (!buffer.isReadable()) {
- return lineBuf.length() > 0 ? lineBuf.toString() : null;
- }
+ if (!buffer.isReadable()) {
+ return null;
+ }
+ if (lineBuf != null) {
+ lineBuf.setLength(0);
+ }
+ loop: do {
int c = buffer.readUnsignedByte();
switch (c) {
case '\n':
@@ -263,11 +265,14 @@ public String readLine() throws IOException {
break loop;
default:
+ if (lineBuf == null) {
+ lineBuf = new StringBuilder();
+ }
lineBuf.append((char) c);
}
- }
+ } while (buffer.isReadable());
- return lineBuf.toString();
+ return lineBuf != null && lineBuf.length() > 0 ? lineBuf.toString() : StringUtil.EMPTY_STRING;
}
@Override
diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
index e7afab36728f..54a9f0c9fa02 100644
--- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
+++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java
@@ -43,6 +43,7 @@
import static io.netty.util.internal.MathUtil.isOutOfBounds;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
import static io.netty.util.internal.StringUtil.NEWLINE;
import static io.netty.util.internal.StringUtil.isSurrogate;
@@ -53,10 +54,10 @@
public final class ByteBufUtil {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ByteBufUtil.class);
- private static final FastThreadLocal CHAR_BUFFERS = new FastThreadLocal() {
+ private static final FastThreadLocal BYTE_ARRAYS = new FastThreadLocal() {
@Override
- protected CharBuffer initialValue() throws Exception {
- return CharBuffer.allocate(1024);
+ protected byte[] initialValue() throws Exception {
+ return PlatformDependent.allocateUninitializedArray(MAX_TL_ARRAY_LEN);
}
};
@@ -95,6 +96,16 @@ protected CharBuffer initialValue() throws Exception {
logger.debug("-Dio.netty.maxThreadLocalCharBufferSize: {}", MAX_CHAR_BUFFER_SIZE);
}
+ static final int MAX_TL_ARRAY_LEN = 1024;
+
+ /**
+ * Allocates a new array if minLength > {@link ByteBufUtil#MAX_TL_ARRAY_LEN}
+ */
+ static byte[] threadLocalTempArray(int minLength) {
+ return minLength <= MAX_TL_ARRAY_LEN ? BYTE_ARRAYS.get()
+ : PlatformDependent.allocateUninitializedArray(minLength);
+ }
+
/**
* Returns a hex dump
* of the specified buffer's readable bytes.
@@ -452,8 +463,9 @@ private static int firstIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte
}
private static int lastIndexOf(ByteBuf buffer, int fromIndex, int toIndex, byte value) {
- fromIndex = Math.min(fromIndex, buffer.capacity());
- if (fromIndex < 0 || buffer.capacity() == 0) {
+ int capacity = buffer.capacity();
+ fromIndex = Math.min(fromIndex, capacity);
+ if (fromIndex < 0 || capacity == 0) {
return -1;
}
@@ -546,17 +558,8 @@ static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq,
buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
break;
}
- if (!Character.isLowSurrogate(c2)) {
- buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
- buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2);
- continue;
- }
- 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)));
+ // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path.
+ writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, c2);
} else {
buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12)));
buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f)));
@@ -566,6 +569,21 @@ static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq,
return writerIndex - oldWriterIndex;
}
+ private static int writeUtf8Surrogate(AbstractByteBuf buffer, int writerIndex, char c, char c2) {
+ if (!Character.isLowSurrogate(c2)) {
+ buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN);
+ buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2);
+ return writerIndex;
+ }
+ 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)));
+ return writerIndex;
+ }
+
/**
* Returns max bytes length of UTF8 character sequence of the given length.
*/
@@ -756,52 +774,27 @@ static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBu
}
}
+ @SuppressWarnings("deprecation")
static String decodeString(ByteBuf src, int readerIndex, int len, Charset charset) {
if (len == 0) {
return StringUtil.EMPTY_STRING;
}
- final CharsetDecoder decoder = CharsetUtil.decoder(charset);
- final int maxLength = (int) ((double) len * decoder.maxCharsPerByte());
- CharBuffer dst = CHAR_BUFFERS.get();
- if (dst.length() < maxLength) {
- dst = CharBuffer.allocate(maxLength);
- if (maxLength <= MAX_CHAR_BUFFER_SIZE) {
- CHAR_BUFFERS.set(dst);
- }
+ final byte[] array;
+ final int offset;
+
+ if (src.hasArray()) {
+ array = src.array();
+ offset = src.arrayOffset() + readerIndex;
} else {
- dst.clear();
+ array = threadLocalTempArray(len);
+ offset = 0;
+ src.getBytes(readerIndex, array, 0, len);
}
- if (src.nioBufferCount() == 1) {
- decodeString(decoder, src.nioBuffer(readerIndex, len), dst);
- } else {
- // We use a heap buffer as CharsetDecoder is most likely able to use a fast-path if src and dst buffers
- // are both backed by a byte array.
- ByteBuf buffer = src.alloc().heapBuffer(len);
- try {
- buffer.writeBytes(src, readerIndex, len);
- // Use internalNioBuffer(...) to reduce object creation.
- decodeString(decoder, buffer.internalNioBuffer(buffer.readerIndex(), len), dst);
- } finally {
- // Release the temporary buffer again.
- buffer.release();
- }
- }
- return dst.flip().toString();
- }
-
- private static void decodeString(CharsetDecoder decoder, ByteBuffer src, CharBuffer dst) {
- try {
- CoderResult cr = decoder.decode(src, dst, true);
- if (!cr.isUnderflow()) {
- cr.throwException();
- }
- cr = decoder.flush(dst);
- if (!cr.isUnderflow()) {
- cr.throwException();
- }
- } catch (CharacterCodingException x) {
- throw new IllegalStateException(x);
+ if (CharsetUtil.US_ASCII.equals(charset)) {
+ // Fast-path for US-ASCII which is used frequently.
+ return new String(array, 0, offset, len);
}
+ return new String(array, offset, len, charset);
}
/**
@@ -844,13 +837,14 @@ public static byte[] getBytes(ByteBuf buf, int start, int length) {
* If {@code copy} is false the underlying storage will be shared, if possible.
*/
public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy) {
- if (isOutOfBounds(start, length, buf.capacity())) {
+ int capacity = buf.capacity();
+ if (isOutOfBounds(start, length, capacity)) {
throw new IndexOutOfBoundsException("expected: " + "0 <= start(" + start + ") <= start + length(" + length
- + ") <= " + "buf.capacity(" + buf.capacity() + ')');
+ + ") <= " + "buf.capacity(" + capacity + ')');
}
if (buf.hasArray()) {
- if (copy || start != 0 || length != buf.capacity()) {
+ if (copy || start != 0 || length != capacity) {
int baseOffset = buf.arrayOffset() + start;
return Arrays.copyOfRange(buf.array(), baseOffset, baseOffset + length);
} else {
@@ -858,7 +852,7 @@ public static byte[] getBytes(ByteBuf buf, int start, int length, boolean copy)
}
}
- byte[] v = new byte[length];
+ byte[] v = PlatformDependent.allocateUninitializedArray(length);
buf.getBytes(start, v);
return v;
}
@@ -1007,9 +1001,7 @@ private static final class HexUtil {
}
private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
- if (length < 0) {
- throw new IllegalArgumentException("length: " + length);
- }
+ checkPositiveOrZero(length, "length");
if (length == 0) {
return "";
}
@@ -1029,9 +1021,7 @@ private static String hexDump(ByteBuf buffer, int fromIndex, int length) {
}
private static String hexDump(byte[] array, int fromIndex, int length) {
- if (length < 0) {
- throw new IllegalArgumentException("length: " + length);
- }
+ checkPositiveOrZero(length, "length");
if (length == 0) {
return "";
}
@@ -1413,7 +1403,9 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio
int chunkLen = Math.min(length, WRITE_CHUNK_SIZE);
buffer.clear().position(position);
- if (allocator.isDirectBufferPooled()) {
+ if (length <= MAX_TL_ARRAY_LEN || !allocator.isDirectBufferPooled()) {
+ getBytes(buffer, threadLocalTempArray(chunkLen), 0, chunkLen, out, length);
+ } else {
// if direct buffers are pooled chances are good that heap buffers are pooled as well.
ByteBuf tmpBuf = allocator.heapBuffer(chunkLen);
try {
@@ -1423,8 +1415,6 @@ static void readBytes(ByteBufAllocator allocator, ByteBuffer buffer, int positio
} finally {
tmpBuf.release();
}
- } else {
- getBytes(buffer, new byte[chunkLen], 0, chunkLen, out, length);
}
}
}
diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java
index 7afff7e7f13e..99127cbd2e06 100644
--- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java
@@ -15,7 +15,11 @@
*/
package io.netty.buffer;
+import io.netty.util.ByteProcessor;
+import io.netty.util.IllegalReferenceCountException;
+import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.EmptyArrays;
+import io.netty.util.internal.RecyclableArrayList;
import java.io.IOException;
import java.io.InputStream;
@@ -26,12 +30,12 @@
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
-import java.util.ListIterator;
import java.util.NoSuchElementException;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
@@ -48,70 +52,94 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf implements
private final ByteBufAllocator alloc;
private final boolean direct;
- private final ComponentList components;
private final int maxNumComponents;
+ private int componentCount;
+ private Component[] components; // resized when needed
+
private boolean freed;
- public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
+ private CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, int initSize) {
super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
if (alloc == null) {
throw new NullPointerException("alloc");
}
+ if (maxNumComponents < 1) {
+ throw new IllegalArgumentException(
+ "maxNumComponents: " + maxNumComponents + " (expected: >= 1)");
+ }
this.alloc = alloc;
this.direct = direct;
this.maxNumComponents = maxNumComponents;
- components = newList(maxNumComponents);
+ components = newCompArray(initSize, maxNumComponents);
}
- public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) {
- this(alloc, direct, maxNumComponents, buffers, 0, buffers.length);
+ public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
+ this(alloc, direct, maxNumComponents, 0);
}
- CompositeByteBuf(
- ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf[] buffers, int offset, int len) {
- super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
- if (alloc == null) {
- throw new NullPointerException("alloc");
- }
- if (maxNumComponents < 2) {
- throw new IllegalArgumentException(
- "maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
- }
+ public CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents, ByteBuf... buffers) {
+ this(alloc, direct, maxNumComponents, buffers, 0);
+ }
- this.alloc = alloc;
- this.direct = direct;
- this.maxNumComponents = maxNumComponents;
- components = newList(maxNumComponents);
+ CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents,
+ ByteBuf[] buffers, int offset) {
+ this(alloc, direct, maxNumComponents, buffers.length - offset);
- addComponents0(false, 0, buffers, offset, len);
+ addComponents0(false, 0, buffers, offset);
consolidateIfNeeded();
- setIndex(0, capacity());
+ setIndex0(0, capacity());
}
public CompositeByteBuf(
ByteBufAllocator alloc, boolean direct, int maxNumComponents, Iterable buffers) {
- super(AbstractByteBufAllocator.DEFAULT_MAX_CAPACITY);
- if (alloc == null) {
- throw new NullPointerException("alloc");
+ this(alloc, direct, maxNumComponents,
+ buffers instanceof Collection ? ((Collection) buffers).size() : 0);
+
+ addComponents(false, 0, buffers);
+ setIndex(0, capacity());
+ }
+
+ // support passing arrays of other types instead of having to copy to a ByteBuf[] first
+ interface ByteWrapper {
+ ByteBuf wrap(T bytes);
+ boolean isEmpty(T bytes);
+ }
+
+ static final ByteWrapper BYTE_ARRAY_WRAPPER = new ByteWrapper() {
+ @Override
+ public ByteBuf wrap(byte[] bytes) {
+ return Unpooled.wrappedBuffer(bytes);
}
- if (maxNumComponents < 2) {
- throw new IllegalArgumentException(
- "maxNumComponents: " + maxNumComponents + " (expected: >= 2)");
+ @Override
+ public boolean isEmpty(byte[] bytes) {
+ return bytes.length == 0;
}
+ };
- this.alloc = alloc;
- this.direct = direct;
- this.maxNumComponents = maxNumComponents;
- components = newList(maxNumComponents);
+ static final ByteWrapper BYTE_BUFFER_WRAPPER = new ByteWrapper() {
+ @Override
+ public ByteBuf wrap(ByteBuffer bytes) {
+ return Unpooled.wrappedBuffer(bytes);
+ }
+ @Override
+ public boolean isEmpty(ByteBuffer bytes) {
+ return !bytes.hasRemaining();
+ }
+ };
- addComponents0(false, 0, buffers);
+ CompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents,
+ ByteWrapper wrapper, T[] buffers, int offset) {
+ this(alloc, direct, maxNumComponents, buffers.length - offset);
+
+ addComponents0(false, 0, wrapper, buffers, offset);
consolidateIfNeeded();
setIndex(0, capacity());
}
- private static ComponentList newList(int maxNumComponents) {
- return new ComponentList(Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents));
+ private static Component[] newCompArray(int initComponents, int maxNumComponents) {
+ int capacityGuess = Math.min(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, maxNumComponents);
+ return new Component[Math.max(initComponents, capacityGuess)];
}
// Special constructor used by WrappedCompositeByteBuf
@@ -129,8 +157,8 @@ private static ComponentList newList(int maxNumComponents) {
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponent(boolean, ByteBuf)}.
*
- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
- * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
+ * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}.
+ * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(ByteBuf buffer) {
@@ -143,10 +171,10 @@ public CompositeByteBuf addComponent(ByteBuf buffer) {
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponents(boolean, ByteBuf[])}.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
- * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
+ * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(ByteBuf... buffers) {
return addComponents(false, buffers);
@@ -158,10 +186,10 @@ public CompositeByteBuf addComponents(ByteBuf... buffers) {
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponents(boolean, Iterable)}.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
- * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
+ * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(Iterable buffers) {
return addComponents(false, buffers);
@@ -173,9 +201,9 @@ public CompositeByteBuf addComponents(Iterable buffers) {
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased use {@link #addComponent(boolean, int, ByteBuf)}.
*
- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
+ * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
- * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
+ * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) {
@@ -186,28 +214,26 @@ public CompositeByteBuf addComponent(int cIndex, ByteBuf buffer) {
* Add the given {@link ByteBuf} and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
- * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
+ * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}.
+ * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(boolean increaseWriterIndex, ByteBuf buffer) {
- checkNotNull(buffer, "buffer");
- addComponent0(increaseWriterIndex, components.size(), buffer);
- consolidateIfNeeded();
- return this;
+ return addComponent(increaseWriterIndex, componentCount, buffer);
}
/**
* Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
- * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
+ * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... buffers) {
- addComponents0(increaseWriterIndex, components.size(), buffers, 0, buffers.length);
+ checkNotNull(buffers, "buffers");
+ addComponents0(increaseWriterIndex, componentCount, buffers, 0);
consolidateIfNeeded();
return this;
}
@@ -216,24 +242,22 @@ public CompositeByteBuf addComponents(boolean increaseWriterIndex, ByteBuf... bu
* Add the given {@link ByteBuf}s and increase the {@code writerIndex} if {@code increaseWriterIndex} is
* {@code true}.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
- * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
+ * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(boolean increaseWriterIndex, Iterable buffers) {
- addComponents0(increaseWriterIndex, components.size(), buffers);
- consolidateIfNeeded();
- return this;
+ return addComponents(increaseWriterIndex, componentCount, buffers);
}
/**
* Add the given {@link ByteBuf} on the specific index and increase the {@code writerIndex}
* if {@code increaseWriterIndex} is {@code true}.
*
- * {@link ByteBuf#release()} ownership of {@code buffer} is transfered to this {@link CompositeByteBuf}.
+ * {@link ByteBuf#release()} ownership of {@code buffer} is transferred to this {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
- * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transfered to this
+ * @param buffer the {@link ByteBuf} to add. {@link ByteBuf#release()} ownership is transferred to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponent(boolean increaseWriterIndex, int cIndex, ByteBuf buffer) {
@@ -252,29 +276,19 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe
try {
checkComponentIndex(cIndex);
- int readableBytes = buffer.readableBytes();
-
// No need to consolidate - just add a component to the list.
- @SuppressWarnings("deprecation")
- Component c = new Component(buffer.order(ByteOrder.BIG_ENDIAN).slice());
- if (cIndex == components.size()) {
- wasAdded = components.add(c);
- if (cIndex == 0) {
- c.endOffset = readableBytes;
- } else {
- Component prev = components.get(cIndex - 1);
- c.offset = prev.endOffset;
- c.endOffset = c.offset + readableBytes;
- }
- } else {
- components.add(cIndex, c);
- wasAdded = true;
- if (readableBytes != 0) {
- updateComponentOffsets(cIndex);
- }
+ Component c = newComponent(buffer, 0);
+ int readableBytes = c.length();
+
+ addComp(cIndex, c);
+ wasAdded = true;
+ if (readableBytes > 0 && cIndex < componentCount - 1) {
+ updateComponentOffsets(cIndex);
+ } else if (cIndex > 0) {
+ c.reposition(components[cIndex - 1].endOffset);
}
if (increaseWriterIndex) {
- writerIndex(writerIndex() + buffer.readableBytes());
+ writerIndex(writerIndex() + readableBytes);
}
return cIndex;
} finally {
@@ -284,59 +298,103 @@ private int addComponent0(boolean increaseWriterIndex, int cIndex, ByteBuf buffe
}
}
+ @SuppressWarnings("deprecation")
+ private Component newComponent(ByteBuf buf, int offset) {
+ if (checkAccessible && !buf.isAccessible()) {
+ throw new IllegalReferenceCountException(0);
+ }
+ int srcIndex = buf.readerIndex(), len = buf.readableBytes();
+ ByteBuf slice = null;
+ // unwrap if already sliced
+ if (buf instanceof AbstractUnpooledSlicedByteBuf) {
+ srcIndex += ((AbstractUnpooledSlicedByteBuf) buf).idx(0);
+ slice = buf;
+ buf = buf.unwrap();
+ } else if (buf instanceof PooledSlicedByteBuf) {
+ srcIndex += ((PooledSlicedByteBuf) buf).adjustment;
+ slice = buf;
+ buf = buf.unwrap();
+ }
+ return new Component(buf.order(ByteOrder.BIG_ENDIAN), srcIndex, offset, len, slice);
+ }
+
/**
* Add the given {@link ByteBuf}s on the specific index
*
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased you need to handle it by your own.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added. {@link ByteBuf#release()} ownership of all
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transferred to this
* {@link CompositeByteBuf}.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all {@link ByteBuf#release()}
- * ownership of all {@link ByteBuf} objects is transfered to this {@link CompositeByteBuf}.
+ * ownership of all {@link ByteBuf} objects is transferred to this {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(int cIndex, ByteBuf... buffers) {
- addComponents0(false, cIndex, buffers, 0, buffers.length);
+ checkNotNull(buffers, "buffers");
+ addComponents0(false, cIndex, buffers, 0);
consolidateIfNeeded();
return this;
}
- private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] buffers, int offset, int len) {
- checkNotNull(buffers, "buffers");
- int i = offset;
+ private CompositeByteBuf addComponents0(boolean increaseWriterIndex,
+ final int cIndex, ByteBuf[] buffers, int arrOffset) {
+ final int len = buffers.length, count = len - arrOffset;
+ // only set ci after we've shifted so that finally block logic is always correct
+ int ci = Integer.MAX_VALUE;
try {
checkComponentIndex(cIndex);
-
- // No need for consolidation
- while (i < len) {
- // Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
- // will release if an exception occurs, and we also release in the finally block here).
- ByteBuf b = buffers[i++];
+ shiftComps(cIndex, count); // will increase componentCount
+ int nextOffset = cIndex > 0 ? components[cIndex - 1].endOffset : 0;
+ for (ci = cIndex; arrOffset < len; arrOffset++, ci++) {
+ ByteBuf b = buffers[arrOffset];
if (b == null) {
break;
}
- cIndex = addComponent0(increaseWriterIndex, cIndex, b) + 1;
- int size = components.size();
- if (cIndex > size) {
- cIndex = size;
- }
+ Component c = newComponent(b, nextOffset);
+ components[ci] = c;
+ nextOffset = c.endOffset;
}
- return cIndex;
+ return this;
} finally {
- for (; i < len; ++i) {
- ByteBuf b = buffers[i];
- if (b != null) {
- try {
- b.release();
- } catch (Throwable ignored) {
- // ignore
+ // ci is now the index following the last successfully added component
+ if (ci < componentCount) {
+ if (ci < cIndex + count) {
+ // we bailed early
+ removeCompRange(ci, cIndex + count);
+ for (; arrOffset < len; ++arrOffset) {
+ ReferenceCountUtil.safeRelease(buffers[arrOffset]);
}
}
+ updateComponentOffsets(ci); // only need to do this here for components after the added ones
+ }
+ if (increaseWriterIndex && ci > cIndex && ci <= componentCount) {
+ writerIndex(writerIndex() + components[ci - 1].endOffset - components[cIndex].offset);
+ }
+ }
+ }
+
+ private int addComponents0(boolean increaseWriterIndex, int cIndex,
+ ByteWrapper wrapper, T[] buffers, int offset) {
+ checkComponentIndex(cIndex);
+
+ // No need for consolidation
+ for (int i = offset, len = buffers.length; i < len; i++) {
+ T b = buffers[i];
+ if (b == null) {
+ break;
+ }
+ if (!wrapper.isEmpty(b)) {
+ cIndex = addComponent0(increaseWriterIndex, cIndex, wrapper.wrap(b)) + 1;
+ int size = componentCount;
+ if (cIndex > size) {
+ cIndex = size;
+ }
}
}
+ return cIndex;
}
/**
@@ -345,50 +403,46 @@ private int addComponents0(boolean increaseWriterIndex, int cIndex, ByteBuf[] bu
* Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuf}.
* If you need to have it increased you need to handle it by your own.
*
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects in {@code buffers} is transferred to this
* {@link CompositeByteBuf}.
* @param cIndex the index on which the {@link ByteBuf} will be added.
* @param buffers the {@link ByteBuf}s to add. {@link ByteBuf#release()} ownership of all
- * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transfered to this
+ * {@link ByteBuf#release()} ownership of all {@link ByteBuf} objects is transferred to this
* {@link CompositeByteBuf}.
*/
public CompositeByteBuf addComponents(int cIndex, Iterable buffers) {
- addComponents0(false, cIndex, buffers);
- consolidateIfNeeded();
- return this;
+ return addComponents(false, cIndex, buffers);
}
- private int addComponents0(boolean increaseIndex, int cIndex, Iterable buffers) {
+ // TODO optimize further, similar to ByteBuf[] version
+ // (difference here is that we don't know *always* know precise size increase in advance,
+ // but we do in the most common case that the Iterable is a Collection)
+ private CompositeByteBuf addComponents(boolean increaseIndex, int cIndex, Iterable buffers) {
if (buffers instanceof ByteBuf) {
// If buffers also implements ByteBuf (e.g. CompositeByteBuf), it has to go to addComponent(ByteBuf).
- return addComponent0(increaseIndex, cIndex, (ByteBuf) buffers);
+ return addComponent(increaseIndex, cIndex, (ByteBuf) buffers);
}
checkNotNull(buffers, "buffers");
+ Iterator it = buffers.iterator();
+ try {
+ checkComponentIndex(cIndex);
- if (!(buffers instanceof Collection)) {
- List list = new ArrayList();
- try {
- for (ByteBuf b: buffers) {
- list.add(b);
- }
- buffers = list;
- } finally {
- if (buffers != list) {
- for (ByteBuf b: buffers) {
- if (b != null) {
- try {
- b.release();
- } catch (Throwable ignored) {
- // ignore
- }
- }
- }
+ // No need for consolidation
+ while (it.hasNext()) {
+ ByteBuf b = it.next();
+ if (b == null) {
+ break;
}
+ cIndex = addComponent0(increaseIndex, cIndex, b) + 1;
+ cIndex = Math.min(cIndex, componentCount);
+ }
+ } finally {
+ while (it.hasNext()) {
+ ReferenceCountUtil.safeRelease(it.next());
}
}
-
- Collection col = (Collection) buffers;
- return addComponents0(increaseIndex, cIndex, col.toArray(new ByteBuf[col.size()]), 0 , col.size());
+ consolidateIfNeeded();
+ return this;
}
/**
@@ -398,63 +452,53 @@ private int addComponents0(boolean increaseIndex, int cIndex, Iterable
private void consolidateIfNeeded() {
// Consolidate if the number of components will exceed the allowed maximum by the current
// operation.
- final int numComponents = components.size();
- if (numComponents > maxNumComponents) {
- final int capacity = components.get(numComponents - 1).endOffset;
+ int size = componentCount;
+ if (size > maxNumComponents) {
+ final int capacity = components[size - 1].endOffset;
ByteBuf consolidated = allocBuffer(capacity);
+ lastAccessed = null;
// We're not using foreach to avoid creating an iterator.
- for (int i = 0; i < numComponents; i ++) {
- Component c = components.get(i);
- ByteBuf b = c.buf;
- consolidated.writeBytes(b);
- c.freeIfNecessary();
+ for (int i = 0; i < size; i ++) {
+ components[i].transferTo(consolidated);
}
- Component c = new Component(consolidated);
- c.endOffset = c.length;
- components.clear();
- components.add(c);
+
+ components[0] = new Component(consolidated, 0, 0, capacity, consolidated);
+ removeCompRange(1, size);
}
}
private void checkComponentIndex(int cIndex) {
ensureAccessible();
- if (cIndex < 0 || cIndex > components.size()) {
+ if (cIndex < 0 || cIndex > componentCount) {
throw new IndexOutOfBoundsException(String.format(
"cIndex: %d (expected: >= 0 && <= numComponents(%d))",
- cIndex, components.size()));
+ cIndex, componentCount));
}
}
private void checkComponentIndex(int cIndex, int numComponents) {
ensureAccessible();
- if (cIndex < 0 || cIndex + numComponents > components.size()) {
+ if (cIndex < 0 || cIndex + numComponents > componentCount) {
throw new IndexOutOfBoundsException(String.format(
"cIndex: %d, numComponents: %d " +
"(expected: cIndex >= 0 && cIndex + numComponents <= totalNumComponents(%d))",
- cIndex, numComponents, components.size()));
+ cIndex, numComponents, componentCount));
}
}
private void updateComponentOffsets(int cIndex) {
- int size = components.size();
+ int size = componentCount;
if (size <= cIndex) {
return;
}
- Component c = components.get(cIndex);
- if (cIndex == 0) {
- c.offset = 0;
- c.endOffset = c.length;
- cIndex ++;
- }
-
- for (int i = cIndex; i < size; i ++) {
- Component prev = components.get(i - 1);
- Component cur = components.get(i);
- cur.offset = prev.endOffset;
- cur.endOffset = cur.offset + cur.length;
+ int nextIndex = cIndex > 0 ? components[cIndex - 1].endOffset : 0;
+ for (; cIndex < size; cIndex++) {
+ Component c = components[cIndex];
+ c.reposition(nextIndex);
+ nextIndex = c.endOffset;
}
}
@@ -465,9 +509,13 @@ private void updateComponentOffsets(int cIndex) {
*/
public CompositeByteBuf removeComponent(int cIndex) {
checkComponentIndex(cIndex);
- Component comp = components.remove(cIndex);
- comp.freeIfNecessary();
- if (comp.length > 0) {
+ Component comp = components[cIndex];
+ if (lastAccessed == comp) {
+ lastAccessed = null;
+ }
+ comp.free();
+ removeComp(cIndex);
+ if (comp.length() > 0) {
// Only need to call updateComponentOffsets if the length was > 0
updateComponentOffsets(cIndex);
}
@@ -489,13 +537,16 @@ public CompositeByteBuf removeComponents(int cIndex, int numComponents) {
int endIndex = cIndex + numComponents;
boolean needsUpdate = false;
for (int i = cIndex; i < endIndex; ++i) {
- Component c = components.get(i);
- if (c.length > 0) {
+ Component c = components[i];
+ if (c.length() > 0) {
needsUpdate = true;
}
- c.freeIfNecessary();
+ if (lastAccessed == c) {
+ lastAccessed = null;
+ }
+ c.free();
}
- components.removeRange(cIndex, endIndex);
+ removeCompRange(cIndex, endIndex);
if (needsUpdate) {
// Only need to call updateComponentOffsets if the length was > 0
@@ -507,10 +558,59 @@ public CompositeByteBuf removeComponents(int cIndex, int numComponents) {
@Override
public Iterator iterator() {
ensureAccessible();
- if (components.isEmpty()) {
- return EMPTY_ITERATOR;
+ return componentCount == 0 ? EMPTY_ITERATOR : new CompositeByteBufIterator();
+ }
+
+ @Override
+ protected int forEachByteAsc0(int start, int end, ByteProcessor processor) throws Exception {
+ if (end <= start) {
+ return -1;
+ }
+ for (int i = toComponentIndex0(start), length = end - start; length > 0; i++) {
+ Component c = components[i];
+ if (c.offset == c.endOffset) {
+ continue; // empty
+ }
+ ByteBuf s = c.buf;
+ int localStart = c.idx(start);
+ int localLength = Math.min(length, c.endOffset - start);
+ // avoid additional checks in AbstractByteBuf case
+ int result = s instanceof AbstractByteBuf
+ ? ((AbstractByteBuf) s).forEachByteAsc0(localStart, localStart + localLength, processor)
+ : s.forEachByte(localStart, localLength, processor);
+ if (result != -1) {
+ return result - c.adjustment;
+ }
+ start += localLength;
+ length -= localLength;
}
- return new CompositeByteBufIterator();
+ return -1;
+ }
+
+ @Override
+ protected int forEachByteDesc0(int rStart, int rEnd, ByteProcessor processor) throws Exception {
+ if (rEnd > rStart) { // rStart *and* rEnd are inclusive
+ return -1;
+ }
+ for (int i = toComponentIndex0(rStart), length = 1 + rStart - rEnd; length > 0; i--) {
+ Component c = components[i];
+ if (c.offset == c.endOffset) {
+ continue; // empty
+ }
+ ByteBuf s = c.buf;
+ int localRStart = c.idx(length + rEnd);
+ int localLength = Math.min(length, localRStart), localIndex = localRStart - localLength;
+ // avoid additional checks in AbstractByteBuf case
+ int result = s instanceof AbstractByteBuf
+ ? ((AbstractByteBuf) s).forEachByteDesc0(localRStart - 1, localIndex, processor)
+ : s.forEachByteDesc(localIndex, localLength, processor);
+
+ if (result != -1) {
+ return result - c.adjustment;
+ }
+ length -= localLength;
+ }
+ return -1;
}
/**
@@ -522,50 +622,40 @@ public List decompose(int offset, int length) {
return Collections.emptyList();
}
- int componentId = toComponentIndex(offset);
- List slice = new ArrayList(components.size());
-
- // The first component
- Component firstC = components.get(componentId);
- ByteBuf first = firstC.buf.duplicate();
- first.readerIndex(offset - firstC.offset);
-
- ByteBuf buf = first;
+ int componentId = toComponentIndex0(offset);
int bytesToSlice = length;
- do {
- int readableBytes = buf.readableBytes();
- if (bytesToSlice <= readableBytes) {
- // Last component
- buf.writerIndex(buf.readerIndex() + bytesToSlice);
- slice.add(buf);
- break;
- } else {
- // Not the last component
- slice.add(buf);
- bytesToSlice -= readableBytes;
- componentId ++;
+ // The first component
+ Component firstC = components[componentId];
- // Fetch the next component.
- buf = components.get(componentId).buf.duplicate();
- }
- } while (bytesToSlice > 0);
+ ByteBuf slice = firstC.buf.slice(firstC.idx(offset), Math.min(firstC.endOffset - offset, bytesToSlice));
+ bytesToSlice -= slice.readableBytes();
- // Slice all components because only readable bytes are interesting.
- for (int i = 0; i < slice.size(); i ++) {
- slice.set(i, slice.get(i).slice());
+ if (bytesToSlice == 0) {
+ return Collections.singletonList(slice);
}
- return slice;
+ List sliceList = new ArrayList(componentCount - componentId);
+ sliceList.add(slice);
+
+ // Add all the slices until there is nothing more left and then return the List.
+ do {
+ Component component = components[++componentId];
+ slice = component.buf.slice(component.idx(component.offset), Math.min(component.length(), bytesToSlice));
+ bytesToSlice -= slice.readableBytes();
+ sliceList.add(slice);
+ } while (bytesToSlice > 0);
+
+ return sliceList;
}
@Override
public boolean isDirect() {
- int size = components.size();
+ int size = componentCount;
if (size == 0) {
return false;
}
for (int i = 0; i < size; i++) {
- if (!components.get(i).buf.isDirect()) {
+ if (!components[i].buf.isDirect()) {
return false;
}
}
@@ -574,11 +664,11 @@ public boolean isDirect() {
@Override
public boolean hasArray() {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return true;
case 1:
- return components.get(0).buf.hasArray();
+ return components[0].buf.hasArray();
default:
return false;
}
@@ -586,11 +676,11 @@ public boolean hasArray() {
@Override
public byte[] array() {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return EmptyArrays.EMPTY_BYTES;
case 1:
- return components.get(0).buf.array();
+ return components[0].buf.array();
default:
throw new UnsupportedOperationException();
}
@@ -598,11 +688,12 @@ public byte[] array() {
@Override
public int arrayOffset() {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return 0;
case 1:
- return components.get(0).buf.arrayOffset();
+ Component c = components[0];
+ return c.idx(c.buf.arrayOffset());
default:
throw new UnsupportedOperationException();
}
@@ -610,11 +701,11 @@ public int arrayOffset() {
@Override
public boolean hasMemoryAddress() {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return Unpooled.EMPTY_BUFFER.hasMemoryAddress();
case 1:
- return components.get(0).buf.hasMemoryAddress();
+ return components[0].buf.hasMemoryAddress();
default:
return false;
}
@@ -622,11 +713,12 @@ public boolean hasMemoryAddress() {
@Override
public long memoryAddress() {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return Unpooled.EMPTY_BUFFER.memoryAddress();
case 1:
- return components.get(0).buf.memoryAddress();
+ Component c = components[0];
+ return c.buf.memoryAddress() + c.adjustment;
default:
throw new UnsupportedOperationException();
}
@@ -634,51 +726,45 @@ public long memoryAddress() {
@Override
public int capacity() {
- final int numComponents = components.size();
- if (numComponents == 0) {
- return 0;
- }
- return components.get(numComponents - 1).endOffset;
+ int size = componentCount;
+ return size > 0 ? components[size - 1].endOffset : 0;
}
@Override
public CompositeByteBuf capacity(int newCapacity) {
checkNewCapacity(newCapacity);
- int oldCapacity = capacity();
+ final int size = componentCount, oldCapacity = capacity();
if (newCapacity > oldCapacity) {
final int paddingLength = newCapacity - oldCapacity;
- ByteBuf padding;
- int nComponents = components.size();
- if (nComponents < maxNumComponents) {
- padding = allocBuffer(paddingLength);
- padding.setIndex(0, paddingLength);
- addComponent0(false, components.size(), padding);
- } else {
- padding = allocBuffer(paddingLength);
- padding.setIndex(0, paddingLength);
+ ByteBuf padding = allocBuffer(paddingLength).setIndex(0, paddingLength);
+ addComponent0(false, size, padding);
+ if (componentCount >= maxNumComponents) {
// FIXME: No need to create a padding buffer and consolidate.
// Just create a big single buffer and put the current content there.
- addComponent0(false, components.size(), padding);
consolidateIfNeeded();
}
} else if (newCapacity < oldCapacity) {
- int bytesToTrim = oldCapacity - newCapacity;
- for (ListIterator i = components.listIterator(components.size()); i.hasPrevious();) {
- Component c = i.previous();
- if (bytesToTrim >= c.length) {
- bytesToTrim -= c.length;
- i.remove();
- continue;
+ lastAccessed = null;
+ int i = size - 1;
+ for (int bytesToTrim = oldCapacity - newCapacity; i >= 0; i--) {
+ Component c = components[i];
+ final int cLength = c.length();
+ if (bytesToTrim < cLength) {
+ // Trim the last component
+ c.endOffset -= bytesToTrim;
+ ByteBuf slice = c.slice;
+ if (slice != null) {
+ // We must replace the cached slice with a derived one to ensure that
+ // it can later be released properly in the case of PooledSlicedByteBuf.
+ c.slice = slice.slice(0, c.length());
+ }
+ break;
}
-
- // Replace the last component with the trimmed slice.
- Component newC = new Component(c.buf.slice(0, c.length - bytesToTrim));
- newC.offset = c.offset;
- newC.endOffset = newC.offset + newC.length;
- i.set(newC);
- break;
+ c.free();
+ bytesToTrim -= cLength;
}
+ removeCompRange(i + 1, size);
if (readerIndex() > newCapacity) {
setIndex(newCapacity, newCapacity);
@@ -703,7 +789,7 @@ public ByteOrder order() {
* Return the current number of {@link ByteBuf}'s that are composed in this instance
*/
public int numComponents() {
- return components.size();
+ return componentCount;
}
/**
@@ -718,10 +804,21 @@ public int maxNumComponents() {
*/
public int toComponentIndex(int offset) {
checkIndex(offset);
+ return toComponentIndex0(offset);
+ }
- for (int low = 0, high = components.size(); low <= high;) {
+ private int toComponentIndex0(int offset) {
+ int size = componentCount;
+ if (offset == 0) { // fast-path zero offset
+ for (int i = 0; i < size; i++) {
+ if (components[i].endOffset > 0) {
+ return i;
+ }
+ }
+ }
+ for (int low = 0, high = size; low <= high;) {
int mid = low + high >>> 1;
- Component c = components.get(mid);
+ Component c = components[mid];
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
@@ -736,25 +833,26 @@ public int toComponentIndex(int offset) {
public int toByteIndex(int cIndex) {
checkComponentIndex(cIndex);
- return components.get(cIndex).offset;
+ return components[cIndex].offset;
}
@Override
public byte getByte(int index) {
- return _getByte(index);
+ Component c = findComponent(index);
+ return c.buf.getByte(c.idx(index));
}
@Override
protected byte _getByte(int index) {
- Component c = findComponent(index);
- return c.buf.getByte(index - c.offset);
+ Component c = findComponent0(index);
+ return c.buf.getByte(c.idx(index));
}
@Override
protected short _getShort(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 2 <= c.endOffset) {
- return c.buf.getShort(index - c.offset);
+ return c.buf.getShort(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (short) ((_getByte(index) & 0xff) << 8 | _getByte(index + 1) & 0xff);
} else {
@@ -764,9 +862,9 @@ protected short _getShort(int index) {
@Override
protected short _getShortLE(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 2 <= c.endOffset) {
- return c.buf.getShortLE(index - c.offset);
+ return c.buf.getShortLE(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (short) (_getByte(index) & 0xff | (_getByte(index + 1) & 0xff) << 8);
} else {
@@ -776,9 +874,9 @@ protected short _getShortLE(int index) {
@Override
protected int _getUnsignedMedium(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 3 <= c.endOffset) {
- return c.buf.getUnsignedMedium(index - c.offset);
+ return c.buf.getUnsignedMedium(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getShort(index) & 0xffff) << 8 | _getByte(index + 2) & 0xff;
} else {
@@ -788,9 +886,9 @@ protected int _getUnsignedMedium(int index) {
@Override
protected int _getUnsignedMediumLE(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 3 <= c.endOffset) {
- return c.buf.getUnsignedMediumLE(index - c.offset);
+ return c.buf.getUnsignedMediumLE(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return _getShortLE(index) & 0xffff | (_getByte(index + 2) & 0xff) << 16;
} else {
@@ -800,9 +898,9 @@ protected int _getUnsignedMediumLE(int index) {
@Override
protected int _getInt(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 4 <= c.endOffset) {
- return c.buf.getInt(index - c.offset);
+ return c.buf.getInt(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getShort(index) & 0xffff) << 16 | _getShort(index + 2) & 0xffff;
} else {
@@ -812,9 +910,9 @@ protected int _getInt(int index) {
@Override
protected int _getIntLE(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 4 <= c.endOffset) {
- return c.buf.getIntLE(index - c.offset);
+ return c.buf.getIntLE(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return _getShortLE(index) & 0xffff | (_getShortLE(index + 2) & 0xffff) << 16;
} else {
@@ -824,9 +922,9 @@ protected int _getIntLE(int index) {
@Override
protected long _getLong(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 8 <= c.endOffset) {
- return c.buf.getLong(index - c.offset);
+ return c.buf.getLong(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return (_getInt(index) & 0xffffffffL) << 32 | _getInt(index + 4) & 0xffffffffL;
} else {
@@ -836,9 +934,9 @@ protected long _getLong(int index) {
@Override
protected long _getLongLE(int index) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 8 <= c.endOffset) {
- return c.buf.getLongLE(index - c.offset);
+ return c.buf.getLongLE(c.idx(index));
} else if (order() == ByteOrder.BIG_ENDIAN) {
return _getIntLE(index) & 0xffffffffL | (_getIntLE(index + 4) & 0xffffffffL) << 32;
} else {
@@ -853,13 +951,11 @@ public CompositeByteBuf getBytes(int index, byte[] dst, int dstIndex, int length
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.getBytes(index - adjustment, dst, dstIndex, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.getBytes(c.idx(index), dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
@@ -878,15 +974,13 @@ public CompositeByteBuf getBytes(int index, ByteBuffer dst) {
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
try {
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
dst.limit(dst.position() + localLength);
- s.getBytes(index - adjustment, dst);
+ c.buf.getBytes(c.idx(index), dst);
index += localLength;
length -= localLength;
i ++;
@@ -904,13 +998,11 @@ public CompositeByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int lengt
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.getBytes(index - adjustment, dst, dstIndex, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.getBytes(c.idx(index), dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
@@ -960,13 +1052,11 @@ public CompositeByteBuf getBytes(int index, OutputStream out, int length) throws
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.getBytes(index - adjustment, out, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.getBytes(c.idx(index), out, localLength);
index += localLength;
length -= localLength;
i ++;
@@ -977,25 +1067,28 @@ public CompositeByteBuf getBytes(int index, OutputStream out, int length) throws
@Override
public CompositeByteBuf setByte(int index, int value) {
Component c = findComponent(index);
- c.buf.setByte(index - c.offset, value);
+ c.buf.setByte(c.idx(index), value);
return this;
}
@Override
protected void _setByte(int index, int value) {
- setByte(index, value);
+ Component c = findComponent0(index);
+ c.buf.setByte(c.idx(index), value);
}
@Override
public CompositeByteBuf setShort(int index, int value) {
- return (CompositeByteBuf) super.setShort(index, value);
+ checkIndex(index, 2);
+ _setShort(index, value);
+ return this;
}
@Override
protected void _setShort(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 2 <= c.endOffset) {
- c.buf.setShort(index - c.offset, value);
+ c.buf.setShort(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setByte(index, (byte) (value >>> 8));
_setByte(index + 1, (byte) value);
@@ -1007,9 +1100,9 @@ protected void _setShort(int index, int value) {
@Override
protected void _setShortLE(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 2 <= c.endOffset) {
- c.buf.setShortLE(index - c.offset, value);
+ c.buf.setShortLE(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setByte(index, (byte) value);
_setByte(index + 1, (byte) (value >>> 8));
@@ -1021,14 +1114,16 @@ protected void _setShortLE(int index, int value) {
@Override
public CompositeByteBuf setMedium(int index, int value) {
- return (CompositeByteBuf) super.setMedium(index, value);
+ checkIndex(index, 3);
+ _setMedium(index, value);
+ return this;
}
@Override
protected void _setMedium(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 3 <= c.endOffset) {
- c.buf.setMedium(index - c.offset, value);
+ c.buf.setMedium(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShort(index, (short) (value >> 8));
_setByte(index + 2, (byte) value);
@@ -1040,9 +1135,9 @@ protected void _setMedium(int index, int value) {
@Override
protected void _setMediumLE(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 3 <= c.endOffset) {
- c.buf.setMediumLE(index - c.offset, value);
+ c.buf.setMediumLE(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShortLE(index, (short) value);
_setByte(index + 2, (byte) (value >>> 16));
@@ -1054,14 +1149,16 @@ protected void _setMediumLE(int index, int value) {
@Override
public CompositeByteBuf setInt(int index, int value) {
- return (CompositeByteBuf) super.setInt(index, value);
+ checkIndex(index, 4);
+ _setInt(index, value);
+ return this;
}
@Override
protected void _setInt(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 4 <= c.endOffset) {
- c.buf.setInt(index - c.offset, value);
+ c.buf.setInt(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShort(index, (short) (value >>> 16));
_setShort(index + 2, (short) value);
@@ -1073,9 +1170,9 @@ protected void _setInt(int index, int value) {
@Override
protected void _setIntLE(int index, int value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 4 <= c.endOffset) {
- c.buf.setIntLE(index - c.offset, value);
+ c.buf.setIntLE(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setShortLE(index, (short) value);
_setShortLE(index + 2, (short) (value >>> 16));
@@ -1087,14 +1184,16 @@ protected void _setIntLE(int index, int value) {
@Override
public CompositeByteBuf setLong(int index, long value) {
- return (CompositeByteBuf) super.setLong(index, value);
+ checkIndex(index, 8);
+ _setLong(index, value);
+ return this;
}
@Override
protected void _setLong(int index, long value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 8 <= c.endOffset) {
- c.buf.setLong(index - c.offset, value);
+ c.buf.setLong(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setInt(index, (int) (value >>> 32));
_setInt(index + 4, (int) value);
@@ -1106,9 +1205,9 @@ protected void _setLong(int index, long value) {
@Override
protected void _setLongLE(int index, long value) {
- Component c = findComponent(index);
+ Component c = findComponent0(index);
if (index + 8 <= c.endOffset) {
- c.buf.setLongLE(index - c.offset, value);
+ c.buf.setLongLE(c.idx(index), value);
} else if (order() == ByteOrder.BIG_ENDIAN) {
_setIntLE(index, (int) value);
_setIntLE(index + 4, (int) (value >>> 32));
@@ -1125,13 +1224,11 @@ public CompositeByteBuf setBytes(int index, byte[] src, int srcIndex, int length
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.setBytes(index - adjustment, src, srcIndex, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.setBytes(c.idx(index), src, srcIndex, localLength);
index += localLength;
srcIndex += localLength;
length -= localLength;
@@ -1150,15 +1247,13 @@ public CompositeByteBuf setBytes(int index, ByteBuffer src) {
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
try {
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
src.limit(src.position() + localLength);
- s.setBytes(index - adjustment, src);
+ c.buf.setBytes(c.idx(index), src);
index += localLength;
length -= localLength;
i ++;
@@ -1176,13 +1271,11 @@ public CompositeByteBuf setBytes(int index, ByteBuf src, int srcIndex, int lengt
return this;
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.setBytes(index - adjustment, src, srcIndex, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.setBytes(c.idx(index), src, srcIndex, localLength);
index += localLength;
srcIndex += localLength;
length -= localLength;
@@ -1198,20 +1291,17 @@ public int setBytes(int index, InputStream in, int length) throws IOException {
return in.read(EmptyArrays.EMPTY_BYTES);
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
int readBytes = 0;
-
do {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
if (localLength == 0) {
// Skip empty buffer
i++;
continue;
}
- int localReadBytes = s.setBytes(index - adjustment, in, localLength);
+ int localReadBytes = c.buf.setBytes(c.idx(index), in, localLength);
if (localReadBytes < 0) {
if (readBytes == 0) {
return -1;
@@ -1220,15 +1310,11 @@ public int setBytes(int index, InputStream in, int length) throws IOException {
}
}
+ index += localReadBytes;
+ length -= localReadBytes;
+ readBytes += localReadBytes;
if (localReadBytes == localLength) {
- index += localLength;
- length -= localLength;
- readBytes += localLength;
i ++;
- } else {
- index += localReadBytes;
- length -= localReadBytes;
- readBytes += localReadBytes;
}
} while (length > 0);
@@ -1242,19 +1328,17 @@ public int setBytes(int index, ScatteringByteChannel in, int length) throws IOEx
return in.read(EMPTY_NIO_BUFFER);
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
int readBytes = 0;
do {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
if (localLength == 0) {
// Skip empty buffer
i++;
continue;
}
- int localReadBytes = s.setBytes(index - adjustment, in, localLength);
+ int localReadBytes = c.buf.setBytes(c.idx(index), in, localLength);
if (localReadBytes == 0) {
break;
@@ -1268,15 +1352,11 @@ public int setBytes(int index, ScatteringByteChannel in, int length) throws IOEx
}
}
+ index += localReadBytes;
+ length -= localReadBytes;
+ readBytes += localReadBytes;
if (localReadBytes == localLength) {
- index += localLength;
- length -= localLength;
- readBytes += localLength;
i ++;
- } else {
- index += localReadBytes;
- length -= localReadBytes;
- readBytes += localReadBytes;
}
} while (length > 0);
@@ -1290,19 +1370,17 @@ public int setBytes(int index, FileChannel in, long position, int length) throws
return in.read(EMPTY_NIO_BUFFER, position);
}
- int i = toComponentIndex(index);
+ int i = toComponentIndex0(index);
int readBytes = 0;
do {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
if (localLength == 0) {
// Skip empty buffer
i++;
continue;
}
- int localReadBytes = s.setBytes(index - adjustment, in, position + readBytes, localLength);
+ int localReadBytes = c.buf.setBytes(c.idx(index), in, position + readBytes, localLength);
if (localReadBytes == 0) {
break;
@@ -1316,15 +1394,11 @@ public int setBytes(int index, FileChannel in, long position, int length) throws
}
}
+ index += localReadBytes;
+ length -= localReadBytes;
+ readBytes += localReadBytes;
if (localReadBytes == localLength) {
- index += localLength;
- length -= localLength;
- readBytes += localLength;
i ++;
- } else {
- index += localReadBytes;
- length -= localReadBytes;
- readBytes += localReadBytes;
}
} while (length > 0);
@@ -1336,7 +1410,7 @@ public ByteBuf copy(int index, int length) {
checkIndex(index, length);
ByteBuf dst = allocBuffer(length);
if (length != 0) {
- copyTo(index, length, toComponentIndex(index), dst);
+ copyTo(index, length, toComponentIndex0(index), dst);
}
return dst;
}
@@ -1346,11 +1420,9 @@ private void copyTo(int index, int length, int componentId, ByteBuf dst) {
int i = componentId;
while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- s.getBytes(index - adjustment, dst, dstIndex, localLength);
+ Component c = components[i];
+ int localLength = Math.min(length, c.endOffset - index);
+ c.buf.getBytes(c.idx(index), dst, dstIndex, localLength);
index += localLength;
dstIndex += localLength;
length -= localLength;
@@ -1367,7 +1439,8 @@ private void copyTo(int index, int length, int componentId, ByteBuf dst) {
* @return buf the {@link ByteBuf} on the specified index
*/
public ByteBuf component(int cIndex) {
- return internalComponent(cIndex).duplicate();
+ checkComponentIndex(cIndex);
+ return components[cIndex].duplicate();
}
/**
@@ -1377,7 +1450,7 @@ public ByteBuf component(int cIndex) {
* @return the {@link ByteBuf} on the specified index
*/
public ByteBuf componentAtOffset(int offset) {
- return internalComponentAtOffset(offset).duplicate();
+ return findComponent(offset).duplicate();
}
/**
@@ -1388,7 +1461,7 @@ public ByteBuf componentAtOffset(int offset) {
*/
public ByteBuf internalComponent(int cIndex) {
checkComponentIndex(cIndex);
- return components.get(cIndex).buf;
+ return components[cIndex].slice();
}
/**
@@ -1398,21 +1471,40 @@ public ByteBuf internalComponent(int cIndex) {
* @param offset the offset for which the {@link ByteBuf} should be returned
*/
public ByteBuf internalComponentAtOffset(int offset) {
- return findComponent(offset).buf;
+ return findComponent(offset).slice();
}
+ // weak cache - check it first when looking for component
+ private Component lastAccessed;
+
private Component findComponent(int offset) {
+ Component la = lastAccessed;
+ if (la != null && offset >= la.offset && offset < la.endOffset) {
+ ensureAccessible();
+ return la;
+ }
checkIndex(offset);
+ return findIt(offset);
+ }
+
+ private Component findComponent0(int offset) {
+ Component la = lastAccessed;
+ if (la != null && offset >= la.offset && offset < la.endOffset) {
+ return la;
+ }
+ return findIt(offset);
+ }
- for (int low = 0, high = components.size(); low <= high;) {
+ private Component findIt(int offset) {
+ for (int low = 0, high = componentCount; low <= high;) {
int mid = low + high >>> 1;
- Component c = components.get(mid);
+ Component c = components[mid];
if (offset >= c.endOffset) {
low = mid + 1;
} else if (offset < c.offset) {
high = mid - 1;
} else {
- assert c.length != 0;
+ lastAccessed = c;
return c;
}
}
@@ -1422,17 +1514,16 @@ private Component findComponent(int offset) {
@Override
public int nioBufferCount() {
- switch (components.size()) {
+ int size = componentCount;
+ switch (size) {
case 0:
return 1;
case 1:
- return components.get(0).buf.nioBufferCount();
+ return components[0].buf.nioBufferCount();
default:
int count = 0;
- int componentsCount = components.size();
- for (int i = 0; i < componentsCount; i++) {
- Component c = components.get(i);
- count += c.buf.nioBufferCount();
+ for (int i = 0; i < size; i++) {
+ count += components[i].buf.nioBufferCount();
}
return count;
}
@@ -1440,11 +1531,12 @@ public int nioBufferCount() {
@Override
public ByteBuffer internalNioBuffer(int index, int length) {
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
- return components.get(0).buf.internalNioBuffer(index, length);
+ Component c = components[0];
+ return c.buf.internalNioBuffer(c.idx(index), length);
default:
throw new UnsupportedOperationException();
}
@@ -1454,19 +1546,24 @@ public ByteBuffer internalNioBuffer(int index, int length) {
public ByteBuffer nioBuffer(int index, int length) {
checkIndex(index, length);
- switch (components.size()) {
+ switch (componentCount) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
- ByteBuf buf = components.get(0).buf;
+ Component c = components[0];
+ ByteBuf buf = c.buf;
if (buf.nioBufferCount() == 1) {
- return components.get(0).buf.nioBuffer(index, length);
+ return buf.nioBuffer(c.idx(index), length);
}
}
- ByteBuffer merged = ByteBuffer.allocate(length).order(order());
ByteBuffer[] buffers = nioBuffers(index, length);
+ if (buffers.length == 1) {
+ return buffers[0].duplicate();
+ }
+
+ ByteBuffer merged = ByteBuffer.allocate(length).order(order());
for (ByteBuffer buf: buffers) {
merged.put(buf);
}
@@ -1482,29 +1579,32 @@ public ByteBuffer[] nioBuffers(int index, int length) {
return new ByteBuffer[] { EMPTY_NIO_BUFFER };
}
- List buffers = new ArrayList(components.size());
- int i = toComponentIndex(index);
- while (length > 0) {
- Component c = components.get(i);
- ByteBuf s = c.buf;
- int adjustment = c.offset;
- int localLength = Math.min(length, s.capacity() - (index - adjustment));
- switch (s.nioBufferCount()) {
+ RecyclableArrayList buffers = RecyclableArrayList.newInstance(componentCount);
+ try {
+ int i = toComponentIndex0(index);
+ while (length > 0) {
+ Component c = components[i];
+ ByteBuf s = c.buf;
+ int localLength = Math.min(length, c.endOffset - index);
+ switch (s.nioBufferCount()) {
case 0:
throw new UnsupportedOperationException();
case 1:
- buffers.add(s.nioBuffer(index - adjustment, localLength));
+ buffers.add(s.nioBuffer(c.idx(index), localLength));
break;
default:
- Collections.addAll(buffers, s.nioBuffers(index - adjustment, localLength));
+ Collections.addAll(buffers, s.nioBuffers(c.idx(index), localLength));
+ }
+
+ index += localLength;
+ length -= localLength;
+ i ++;
}
- index += localLength;
- length -= localLength;
- i ++;
+ return buffers.toArray(new ByteBuffer[0]);
+ } finally {
+ buffers.recycle();
}
-
- return buffers.toArray(new ByteBuffer[buffers.size()]);
}
/**
@@ -1512,25 +1612,20 @@ public ByteBuffer[] nioBuffers(int index, int length) {
*/
public CompositeByteBuf consolidate() {
ensureAccessible();
- final int numComponents = numComponents();
+ final int numComponents = componentCount;
if (numComponents <= 1) {
return this;
}
- final Component last = components.get(numComponents - 1);
- final int capacity = last.endOffset;
+ final int capacity = components[numComponents - 1].endOffset;
final ByteBuf consolidated = allocBuffer(capacity);
for (int i = 0; i < numComponents; i ++) {
- Component c = components.get(i);
- ByteBuf b = c.buf;
- consolidated.writeBytes(b);
- c.freeIfNecessary();
+ components[i].transferTo(consolidated);
}
-
- components.clear();
- components.add(new Component(consolidated));
- updateComponentOffsets(0);
+ lastAccessed = null;
+ components[0] = new Component(consolidated, 0, 0, capacity, consolidated);
+ removeCompRange(1, numComponents);
return this;
}
@@ -1547,19 +1642,16 @@ public CompositeByteBuf consolidate(int cIndex, int numComponents) {
}
final int endCIndex = cIndex + numComponents;
- final Component last = components.get(endCIndex - 1);
- final int capacity = last.endOffset - components.get(cIndex).offset;
+ final Component last = components[endCIndex - 1];
+ final int capacity = last.endOffset - components[cIndex].offset;
final ByteBuf consolidated = allocBuffer(capacity);
for (int i = cIndex; i < endCIndex; i ++) {
- Component c = components.get(i);
- ByteBuf b = c.buf;
- consolidated.writeBytes(b);
- c.freeIfNecessary();
+ components[i].transferTo(consolidated);
}
-
- components.removeRange(cIndex + 1, endCIndex);
- components.set(cIndex, new Component(consolidated));
+ lastAccessed = null;
+ removeCompRange(cIndex + 1, endCIndex);
+ components[cIndex] = new Component(consolidated, 0, 0, capacity, consolidated);
updateComponentOffsets(cIndex);
return this;
}
@@ -1577,25 +1669,26 @@ public CompositeByteBuf discardReadComponents() {
// Discard everything if (readerIndex = writerIndex = capacity).
int writerIndex = writerIndex();
if (readerIndex == writerIndex && writerIndex == capacity()) {
- int size = components.size();
- for (int i = 0; i < size; i++) {
- components.get(i).freeIfNecessary();
+ for (int i = 0, size = componentCount; i < size; i++) {
+ components[i].free();
}
- components.clear();
+ lastAccessed = null;
+ clearComps();
setIndex(0, 0);
adjustMarkers(readerIndex);
return this;
}
// Remove read components.
- int firstComponentId = toComponentIndex(readerIndex);
+ int firstComponentId = toComponentIndex0(readerIndex);
for (int i = 0; i < firstComponentId; i ++) {
- components.get(i).freeIfNecessary();
+ components[i].free();
}
- components.removeRange(0, firstComponentId);
+ lastAccessed = null;
+ removeCompRange(0, firstComponentId);
// Update indexes and markers.
- Component first = components.get(0);
+ Component first = components[0];
int offset = first.offset;
updateComponentOffsets(0);
setIndex(readerIndex - offset, writerIndex - offset);
@@ -1614,34 +1707,49 @@ public CompositeByteBuf discardReadBytes() {
// Discard everything if (readerIndex = writerIndex = capacity).
int writerIndex = writerIndex();
if (readerIndex == writerIndex && writerIndex == capacity()) {
- int size = components.size();
- for (int i = 0; i < size; i++) {
- components.get(i).freeIfNecessary();
+ for (int i = 0, size = componentCount; i < size; i++) {
+ components[i].free();
}
- components.clear();
+ lastAccessed = null;
+ clearComps();
setIndex(0, 0);
adjustMarkers(readerIndex);
return this;
}
// Remove read components.
- int firstComponentId = toComponentIndex(readerIndex);
+ int firstComponentId = toComponentIndex0(readerIndex);
for (int i = 0; i < firstComponentId; i ++) {
- components.get(i).freeIfNecessary();
+ Component c = components[i];
+ c.free();
+ if (lastAccessed == c) {
+ lastAccessed = null;
+ }
}
// Remove or replace the first readable component with a new slice.
- Component c = components.get(firstComponentId);
- int adjustment = readerIndex - c.offset;
- if (adjustment == c.length) {
+ Component c = components[firstComponentId];
+ if (readerIndex == c.endOffset) {
// new slice would be empty, so remove instead
+ c.free();
+ if (lastAccessed == c) {
+ lastAccessed = null;
+ }
firstComponentId++;
} else {
- Component newC = new Component(c.buf.slice(adjustment, c.length - adjustment));
- components.set(firstComponentId, newC);
+ int trimmedBytes = readerIndex - c.offset;
+ c.offset = 0;
+ c.endOffset -= readerIndex;
+ c.adjustment += readerIndex;
+ ByteBuf slice = c.slice;
+ if (slice != null) {
+ // We must replace the cached slice with a derived one to ensure that
+ // it can later be released properly in the case of PooledSlicedByteBuf.
+ c.slice = slice.slice(trimmedBytes, c.length());
+ }
}
- components.removeRange(0, firstComponentId);
+ removeCompRange(0, firstComponentId);
// Update indexes and markers.
updateComponentOffsets(0);
@@ -1658,253 +1766,338 @@ private ByteBuf allocBuffer(int capacity) {
public String toString() {
String result = super.toString();
result = result.substring(0, result.length() - 1);
- return result + ", components=" + components.size() + ')';
+ return result + ", components=" + componentCount + ')';
}
private static final class Component {
final ByteBuf buf;
- final int length;
+ int adjustment;
int offset;
int endOffset;
- Component(ByteBuf buf) {
+ private ByteBuf slice; // cached slice, may be null
+
+ Component(ByteBuf buf, int srcOffset, int offset, int len, ByteBuf slice) {
this.buf = buf;
- length = buf.readableBytes();
+ this.offset = offset;
+ this.endOffset = offset + len;
+ this.adjustment = srcOffset - offset;
+ this.slice = slice;
}
- void freeIfNecessary() {
- buf.release(); // We should not get a NPE here. If so, it must be a bug.
+ int idx(int index) {
+ return index + adjustment;
+ }
+
+ int length() {
+ return endOffset - offset;
+ }
+
+ void reposition(int newOffset) {
+ int move = newOffset - offset;
+ endOffset += move;
+ adjustment -= move;
+ offset = newOffset;
+ }
+
+ // copy then release
+ void transferTo(ByteBuf dst) {
+ dst.writeBytes(buf, idx(offset), length());
+ free();
+ }
+
+ ByteBuf slice() {
+ return slice != null ? slice : (slice = buf.slice(idx(offset), length()));
+ }
+
+ ByteBuf duplicate() {
+ return buf.duplicate().setIndex(idx(offset), idx(endOffset));
+ }
+
+ void free() {
+ // Release the slice if present since it may have a different
+ // refcount to the unwrapped buf if it is a PooledSlicedByteBuf
+ ByteBuf buffer = slice;
+ if (buffer != null) {
+ buffer.release();
+ } else {
+ buf.release();
+ }
+ // null out in either case since it could be racy if set lazily (but not
+ // in the case we care about, where it will have been set in the ctor)
+ slice = null;
}
}
@Override
public CompositeByteBuf readerIndex(int readerIndex) {
- return (CompositeByteBuf) super.readerIndex(readerIndex);
+ super.readerIndex(readerIndex);
+ return this;
}
@Override
public CompositeByteBuf writerIndex(int writerIndex) {
- return (CompositeByteBuf) super.writerIndex(writerIndex);
+ super.writerIndex(writerIndex);
+ return this;
}
@Override
public CompositeByteBuf setIndex(int readerIndex, int writerIndex) {
- return (CompositeByteBuf) super.setIndex(readerIndex, writerIndex);
+ super.setIndex(readerIndex, writerIndex);
+ return this;
}
@Override
public CompositeByteBuf clear() {
- return (CompositeByteBuf) super.clear();
+ super.clear();
+ return this;
}
@Override
public CompositeByteBuf markReaderIndex() {
- return (CompositeByteBuf) super.markReaderIndex();
+ super.markReaderIndex();
+ return this;
}
@Override
public CompositeByteBuf resetReaderIndex() {
- return (CompositeByteBuf) super.resetReaderIndex();
+ super.resetReaderIndex();
+ return this;
}
@Override
public CompositeByteBuf markWriterIndex() {
- return (CompositeByteBuf) super.markWriterIndex();
+ super.markWriterIndex();
+ return this;
}
@Override
public CompositeByteBuf resetWriterIndex() {
- return (CompositeByteBuf) super.resetWriterIndex();
+ super.resetWriterIndex();
+ return this;
}
@Override
public CompositeByteBuf ensureWritable(int minWritableBytes) {
- return (CompositeByteBuf) super.ensureWritable(minWritableBytes);
+ super.ensureWritable(minWritableBytes);
+ return this;
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuf dst) {
- return (CompositeByteBuf) super.getBytes(index, dst);
+ return getBytes(index, dst, dst.writableBytes());
}
@Override
public CompositeByteBuf getBytes(int index, ByteBuf dst, int length) {
- return (CompositeByteBuf) super.getBytes(index, dst, length);
+ getBytes(index, dst, dst.writerIndex(), length);
+ dst.writerIndex(dst.writerIndex() + length);
+ return this;
}
@Override
public CompositeByteBuf getBytes(int index, byte[] dst) {
- return (CompositeByteBuf) super.getBytes(index, dst);
+ return getBytes(index, dst, 0, dst.length);
}
@Override
public CompositeByteBuf setBoolean(int index, boolean value) {
- return (CompositeByteBuf) super.setBoolean(index, value);
+ return setByte(index, value? 1 : 0);
}
@Override
public CompositeByteBuf setChar(int index, int value) {
- return (CompositeByteBuf) super.setChar(index, value);
+ return setShort(index, value);
}
@Override
public CompositeByteBuf setFloat(int index, float value) {
- return (CompositeByteBuf) super.setFloat(index, value);
+ return setInt(index, Float.floatToRawIntBits(value));
}
@Override
public CompositeByteBuf setDouble(int index, double value) {
- return (CompositeByteBuf) super.setDouble(index, value);
+ return setLong(index, Double.doubleToRawLongBits(value));
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuf src) {
- return (CompositeByteBuf) super.setBytes(index, src);
+ super.setBytes(index, src, src.readableBytes());
+ return this;
}
@Override
public CompositeByteBuf setBytes(int index, ByteBuf src, int length) {
- return (CompositeByteBuf) super.setBytes(index, src, length);
+ super.setBytes(index, src, length);
+ return this;
}
@Override
public CompositeByteBuf setBytes(int index, byte[] src) {
- return (CompositeByteBuf) super.setBytes(index, src);
+ return setBytes(index, src, 0, src.length);
}
@Override
public CompositeByteBuf setZero(int index, int length) {
- return (CompositeByteBuf) super.setZero(index, length);
+ super.setZero(index, length);
+ return this;
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst) {
- return (CompositeByteBuf) super.readBytes(dst);
+ super.readBytes(dst, dst.writableBytes());
+ return this;
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst, int length) {
- return (CompositeByteBuf) super.readBytes(dst, length);
+ super.readBytes(dst, length);
+ return this;
}
@Override
public CompositeByteBuf readBytes(ByteBuf dst, int dstIndex, int length) {
- return (CompositeByteBuf) super.readBytes(dst, dstIndex, length);
+ super.readBytes(dst, dstIndex, length);
+ return this;
}
@Override
public CompositeByteBuf readBytes(byte[] dst) {
- return (CompositeByteBuf) super.readBytes(dst);
+ super.readBytes(dst, 0, dst.length);
+ return this;
}
@Override
public CompositeByteBuf readBytes(byte[] dst, int dstIndex, int length) {
- return (CompositeByteBuf) super.readBytes(dst, dstIndex, length);
+ super.readBytes(dst, dstIndex, length);
+ return this;
}
@Override
public CompositeByteBuf readBytes(ByteBuffer dst) {
- return (CompositeByteBuf) super.readBytes(dst);
+ super.readBytes(dst);
+ return this;
}
@Override
public CompositeByteBuf readBytes(OutputStream out, int length) throws IOException {
- return (CompositeByteBuf) super.readBytes(out, length);
+ super.readBytes(out, length);
+ return this;
}
@Override
public CompositeByteBuf skipBytes(int length) {
- return (CompositeByteBuf) super.skipBytes(length);
+ super.skipBytes(length);
+ return this;
}
@Override
public CompositeByteBuf writeBoolean(boolean value) {
- return (CompositeByteBuf) super.writeBoolean(value);
+ writeByte(value ? 1 : 0);
+ return this;
}
@Override
public CompositeByteBuf writeByte(int value) {
- return (CompositeByteBuf) super.writeByte(value);
+ ensureWritable0(1);
+ _setByte(writerIndex++, value);
+ return this;
}
@Override
public CompositeByteBuf writeShort(int value) {
- return (CompositeByteBuf) super.writeShort(value);
+ super.writeShort(value);
+ return this;
}
@Override
public CompositeByteBuf writeMedium(int value) {
- return (CompositeByteBuf) super.writeMedium(value);
+ super.writeMedium(value);
+ return this;
}
@Override
public CompositeByteBuf writeInt(int value) {
- return (CompositeByteBuf) super.writeInt(value);
+ super.writeInt(value);
+ return this;
}
@Override
public CompositeByteBuf writeLong(long value) {
- return (CompositeByteBuf) super.writeLong(value);
+ super.writeLong(value);
+ return this;
}
@Override
public CompositeByteBuf writeChar(int value) {
- return (CompositeByteBuf) super.writeChar(value);
+ super.writeShort(value);
+ return this;
}
@Override
public CompositeByteBuf writeFloat(float value) {
- return (CompositeByteBuf) super.writeFloat(value);
+ super.writeInt(Float.floatToRawIntBits(value));
+ return this;
}
@Override
public CompositeByteBuf writeDouble(double value) {
- return (CompositeByteBuf) super.writeDouble(value);
+ super.writeLong(Double.doubleToRawLongBits(value));
+ return this;
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src) {
- return (CompositeByteBuf) super.writeBytes(src);
+ super.writeBytes(src, src.readableBytes());
+ return this;
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src, int length) {
- return (CompositeByteBuf) super.writeBytes(src, length);
+ super.writeBytes(src, length);
+ return this;
}
@Override
public CompositeByteBuf writeBytes(ByteBuf src, int srcIndex, int length) {
- return (CompositeByteBuf) super.writeBytes(src, srcIndex, length);
+ super.writeBytes(src, srcIndex, length);
+ return this;
}
@Override
public CompositeByteBuf writeBytes(byte[] src) {
- return (CompositeByteBuf) super.writeBytes(src);
+ super.writeBytes(src, 0, src.length);
+ return this;
}
@Override
public CompositeByteBuf writeBytes(byte[] src, int srcIndex, int length) {
- return (CompositeByteBuf) super.writeBytes(src, srcIndex, length);
+ super.writeBytes(src, srcIndex, length);
+ return this;
}
@Override
public CompositeByteBuf writeBytes(ByteBuffer src) {
- return (CompositeByteBuf) super.writeBytes(src);
+ super.writeBytes(src);
+ return this;
}
@Override
public CompositeByteBuf writeZero(int length) {
- return (CompositeByteBuf) super.writeZero(length);
+ super.writeZero(length);
+ return this;
}
@Override
public CompositeByteBuf retain(int increment) {
- return (CompositeByteBuf) super.retain(increment);
+ super.retain(increment);
+ return this;
}
@Override
public CompositeByteBuf retain() {
- return (CompositeByteBuf) super.retain();
+ super.retain();
+ return this;
}
@Override
@@ -1934,21 +2127,25 @@ protected void deallocate() {
}
freed = true;
- int size = components.size();
// We're not using foreach to avoid creating an iterator.
// see https://github.com/netty/netty/issues/2642
- for (int i = 0; i < size; i++) {
- components.get(i).freeIfNecessary();
+ for (int i = 0, size = componentCount; i < size; i++) {
+ components[i].free();
}
}
+ @Override
+ boolean isAccessible() {
+ return !freed;
+ }
+
@Override
public ByteBuf unwrap() {
return null;
}
private final class CompositeByteBufIterator implements Iterator {
- private final int size = components.size();
+ private final int size = numComponents();
private int index;
@Override
@@ -1958,14 +2155,14 @@ public boolean hasNext() {
@Override
public ByteBuf next() {
- if (size != components.size()) {
+ if (size != numComponents()) {
throw new ConcurrentModificationException();
}
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
- return components.get(index++).buf;
+ return components[index++].slice();
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
@@ -1977,16 +2174,59 @@ public void remove() {
}
}
- private static final class ComponentList extends ArrayList {
+ // Component array manipulation - range checking omitted
+
+ private void clearComps() {
+ removeCompRange(0, componentCount);
+ }
- ComponentList(int initialCapacity) {
- super(initialCapacity);
+ private void removeComp(int i) {
+ removeCompRange(i, i + 1);
+ }
+
+ private void removeCompRange(int from, int to) {
+ if (from >= to) {
+ return;
+ }
+ final int size = componentCount;
+ assert from >= 0 && to <= size;
+ if (to < size) {
+ System.arraycopy(components, to, components, from, size - to);
}
+ int newSize = size - to + from;
+ for (int i = newSize; i < size; i++) {
+ components[i] = null;
+ }
+ componentCount = newSize;
+ }
- // Expose this methods so we not need to create a new subList just to remove a range of elements.
- @Override
- public void removeRange(int fromIndex, int toIndex) {
- super.removeRange(fromIndex, toIndex);
+ private void addComp(int i, Component c) {
+ shiftComps(i, 1);
+ components[i] = c;
+ }
+
+ private void shiftComps(int i, int count) {
+ final int size = componentCount, newSize = size + count;
+ assert i >= 0 && i <= size && count > 0;
+ if (newSize > components.length) {
+ // grow the array
+ int newArrSize = Math.max(size + (size >> 1), newSize);
+ Component[] newArr;
+ if (i == size) {
+ newArr = Arrays.copyOf(components, newArrSize, Component[].class);
+ } else {
+ newArr = new Component[newArrSize];
+ if (i > 0) {
+ System.arraycopy(components, 0, newArr, 0, i);
+ }
+ if (i < size) {
+ System.arraycopy(components, i, newArr, i + count, size - i);
+ }
+ }
+ components = newArr;
+ } else if (i < size) {
+ System.arraycopy(components, i, components, i + count, size - i);
}
+ componentCount = newSize;
}
}
diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
index b954318a7f81..cbc8a1acf8c3 100644
--- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java
@@ -16,6 +16,8 @@
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.util.ByteProcessor;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.PlatformDependent;
@@ -223,9 +225,7 @@ public ByteBuf discardSomeReadBytes() {
@Override
public ByteBuf ensureWritable(int minWritableBytes) {
- if (minWritableBytes < 0) {
- throw new IllegalArgumentException("minWritableBytes: " + minWritableBytes + " (expected: >= 0)");
- }
+ checkPositiveOrZero(minWritableBytes, "minWritableBytes");
if (minWritableBytes != 0) {
throw new IndexOutOfBoundsException();
}
@@ -234,9 +234,7 @@ public ByteBuf ensureWritable(int minWritableBytes) {
@Override
public int ensureWritable(int minWritableBytes, boolean force) {
- if (minWritableBytes < 0) {
- throw new IllegalArgumentException("minWritableBytes: " + minWritableBytes + " (expected: >= 0)");
- }
+ checkPositiveOrZero(minWritableBytes, "minWritableBytes");
if (minWritableBytes == 0) {
return 0;
@@ -1048,9 +1046,7 @@ private ByteBuf checkIndex(int index) {
}
private ByteBuf checkIndex(int index, int length) {
- if (length < 0) {
- throw new IllegalArgumentException("length: " + length);
- }
+ checkPositiveOrZero(length, "length");
if (index != 0 || length != 0) {
throw new IndexOutOfBoundsException();
}
@@ -1058,9 +1054,7 @@ private ByteBuf checkIndex(int index, int length) {
}
private ByteBuf checkLength(int length) {
- if (length < 0) {
- throw new IllegalArgumentException("length: " + length + " (expected: >= 0)");
- }
+ checkPositiveOrZero(length, "length");
if (length != 0) {
throw new IndexOutOfBoundsException();
}
diff --git a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java
index b8d650663f30..08c8d7fc6a5c 100644
--- a/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/FixedCompositeByteBuf.java
@@ -39,7 +39,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
private final int capacity;
private final ByteBufAllocator allocator;
private final ByteOrder order;
- private final Object[] buffers;
+ private final ByteBuf[] buffers;
private final boolean direct;
FixedCompositeByteBuf(ByteBufAllocator allocator, ByteBuf... buffers) {
@@ -52,8 +52,7 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
direct = false;
} else {
ByteBuf b = buffers[0];
- this.buffers = new Object[buffers.length];
- this.buffers[0] = b;
+ this.buffers = buffers;
boolean direct = true;
int nioBufferCount = b.nioBufferCount();
int capacity = b.readableBytes();
@@ -68,7 +67,6 @@ final class FixedCompositeByteBuf extends AbstractReferenceCountedByteBuf {
if (!b.isDirect()) {
direct = false;
}
- this.buffers[i] = b;
}
this.nioBufferCount = nioBufferCount;
this.capacity = capacity;
@@ -232,20 +230,14 @@ private Component findComponent(int index) {
int readable = 0;
for (int i = 0 ; i < buffers.length; i++) {
Component comp = null;
- ByteBuf b;
- Object obj = buffers[i];
- boolean isBuffer;
- if (obj instanceof ByteBuf) {
- b = (ByteBuf) obj;
- isBuffer = true;
- } else {
- comp = (Component) obj;
+ ByteBuf b = buffers[i];
+ if (b instanceof Component) {
+ comp = (Component) b;
b = comp.buf;
- isBuffer = false;
}
readable += b.readableBytes();
if (index < readable) {
- if (isBuffer) {
+ if (comp == null) {
// Create a new component and store it in the array so it not create a new object
// on the next access.
comp = new Component(i, readable - b.readableBytes(), b);
@@ -261,11 +253,8 @@ private Component findComponent(int index) {
* Return the {@link ByteBuf} stored at the given index of the array.
*/
private ByteBuf buffer(int i) {
- Object obj = buffers[i];
- if (obj instanceof ByteBuf) {
- return (ByteBuf) obj;
- }
- return ((Component) obj).buf;
+ ByteBuf b = buffers[i];
+ return b instanceof Component ? ((Component) b).buf : b;
}
@Override
@@ -604,7 +593,7 @@ public ByteBuffer[] nioBuffers(int index, int length) {
s = buffer(++i);
}
- return array.toArray(new ByteBuffer[array.size()]);
+ return array.toArray(new ByteBuffer[0]);
} finally {
array.recycle();
}
@@ -684,17 +673,16 @@ public String toString() {
return result + ", components=" + buffers.length + ')';
}
- private static final class Component {
+ private static final class Component extends WrappedByteBuf {
private final int index;
private final int offset;
- private final ByteBuf buf;
private final int endOffset;
Component(int index, int offset, ByteBuf buf) {
+ super(buf);
this.index = index;
this.offset = offset;
endOffset = offset + buf.readableBytes();
- this.buf = buf;
}
}
}
diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java
index 48593ef34d21..7d75c9b4bb29 100644
--- a/buffer/src/main/java/io/netty/buffer/PoolArena.java
+++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java
@@ -26,6 +26,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
import static java.lang.Math.max;
abstract class PoolArena implements PoolArenaMetric {
@@ -205,7 +206,7 @@ private void allocate(PoolThreadCache cache, PooledByteBuf buf, final int req
assert s.doNotDestroy && s.elemSize == normCapacity;
long handle = s.allocate();
assert handle >= 0;
- s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
+ s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
incTinySmallAllocation(tiny);
return;
}
@@ -242,9 +243,8 @@ private void allocateNormal(PooledByteBuf buf, int reqCapacity, int normCapac
// Add a new chunk.
PoolChunk c = newChunk(pageSize, maxOrder, pageShifts, chunkSize);
- long handle = c.allocate(normCapacity);
- assert handle > 0;
- c.initBuf(buf, handle, reqCapacity);
+ boolean success = c.allocate(buf, reqCapacity, normCapacity);
+ assert success;
qInit.add(c);
}
@@ -263,7 +263,7 @@ private void allocateHuge(PooledByteBuf buf, int reqCapacity) {
allocationsHuge.increment();
}
- void free(PoolChunk chunk, long handle, int normCapacity, PoolThreadCache cache) {
+ void free(PoolChunk chunk, ByteBuffer nioBuffer, long handle, int normCapacity, PoolThreadCache cache) {
if (chunk.unpooled) {
int size = chunk.chunkSize();
destroyChunk(chunk);
@@ -271,12 +271,12 @@ void free(PoolChunk chunk, long handle, int normCapacity, PoolThreadCache cac
deallocationsHuge.increment();
} else {
SizeClass sizeClass = sizeClass(normCapacity);
- if (cache != null && cache.add(this, chunk, handle, normCapacity, sizeClass)) {
+ if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {
// cached so not free it.
return;
}
- freeChunk(chunk, handle, sizeClass);
+ freeChunk(chunk, handle, sizeClass, nioBuffer);
}
}
@@ -287,7 +287,7 @@ private SizeClass sizeClass(int normCapacity) {
return isTiny(normCapacity) ? SizeClass.Tiny : SizeClass.Small;
}
- void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass) {
+ void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer) {
final boolean destroyChunk;
synchronized (this) {
switch (sizeClass) {
@@ -303,7 +303,7 @@ void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass) {
default:
throw new Error();
}
- destroyChunk = !chunk.parent.free(chunk, handle);
+ destroyChunk = !chunk.parent.free(chunk, handle, nioBuffer);
}
if (destroyChunk) {
// destroyChunk not need to be called while holding the synchronized lock.
@@ -331,9 +331,7 @@ PoolSubpage findSubpagePoolHead(int elemSize) {
}
int normalizeCapacity(int reqCapacity) {
- if (reqCapacity < 0) {
- throw new IllegalArgumentException("capacity: " + reqCapacity + " (expected: 0+)");
- }
+ checkPositiveOrZero(reqCapacity, "reqCapacity");
if (reqCapacity >= chunkSize) {
return directMemoryCacheAlignment == 0 ? reqCapacity : alignCapacity(reqCapacity);
@@ -387,6 +385,7 @@ void reallocate(PooledByteBuf buf, int newCapacity, boolean freeOldMemory) {
}
PoolChunk oldChunk = buf.chunk;
+ ByteBuffer oldNioBuffer = buf.tmpNioBuf;
long oldHandle = buf.handle;
T oldMemory = buf.memory;
int oldOffset = buf.offset;
@@ -415,7 +414,7 @@ void reallocate(PooledByteBuf buf, int newCapacity, boolean freeOldMemory) {
buf.setIndex(readerIndex, writerIndex);
if (freeOldMemory) {
- free(oldChunk, oldHandle, oldMaxLength, buf.cache);
+ free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache);
}
}
@@ -725,11 +724,16 @@ boolean isDirect() {
return true;
}
- private int offsetCacheLine(ByteBuffer memory) {
+ // mark as package-private, only for unit test
+ int offsetCacheLine(ByteBuffer memory) {
// We can only calculate the offset if Unsafe is present as otherwise directBufferAddress(...) will
// throw an NPE.
- return HAS_UNSAFE ?
- (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask) : 0;
+ int remainder = HAS_UNSAFE
+ ? (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask)
+ : 0;
+
+ // offset = alignment - address & (alignment - 1)
+ return directMemoryCacheAlignment - remainder;
}
@Override
diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java
index b3ca160223a7..0a19a5a1d492 100644
--- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java
+++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java
@@ -16,6 +16,10 @@
package io.netty.buffer;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
/**
* Description of algorithm for PageRun/PoolSubpage allocation from PoolChunk
*
@@ -94,11 +98,10 @@
* Note:
* -----
* In the implementation for improving cache coherence,
- * we store 2 pieces of information (i.e, 2 byte vals) as a short value in memoryMap
+ * we store 2 pieces of information depth_of_id and x as two byte values in memoryMap and depthMap respectively
*
- * memoryMap[id]= (depth_of_id, x)
- * where as per convention defined above
- * the second value (i.e, x) indicates that the first node which is free to be allocated is at depth x (from root)
+ * memoryMap[id]= depth_of_id is defined above
+ * depthMap[id]= x indicates that the first node which is free to be allocated is at depth x (from root)
*/
final class PoolChunk implements PoolChunkMetric {
@@ -108,7 +111,6 @@ final class PoolChunk implements PoolChunkMetric {
final T memory;
final boolean unpooled;
final int offset;
-
private final byte[] memoryMap;
private final byte[] depthMap;
private final PoolSubpage[] subpages;
@@ -123,6 +125,13 @@ final class PoolChunk implements PoolChunkMetric {
/** Used to mark memory as unusable */
private final byte unusable;
+ // Use as cache for ByteBuffer created from the memory. These are just duplicates and so are only a container
+ // around the memory itself. These are often needed for operations within the Pooled*ByteBuf and so
+ // may produce extra GC, which can be greatly reduced by caching the duplicates.
+ //
+ // This may be null if the PoolChunk is unpooled as pooling the ByteBuffer instances does not make any sense here.
+ private final Deque cachedNioBuffers;
+
private int freeBytes;
PoolChunkList parent;
@@ -164,6 +173,7 @@ final class PoolChunk implements PoolChunkMetric {
}
subpages = newSubpageArray(maxSubpageAllocs);
+ cachedNioBuffers = new ArrayDeque(8);
}
/** Creates a special chunk that is not pooled. */
@@ -183,6 +193,7 @@ final class PoolChunk implements PoolChunkMetric {
chunkSize = size;
log2ChunkSize = log2(chunkSize);
maxSubpageAllocs = 0;
+ cachedNioBuffers = null;
}
@SuppressWarnings("unchecked")
@@ -211,12 +222,20 @@ private int usage(int freeBytes) {
return 100 - freePercentage;
}
- long allocate(int normCapacity) {
+ boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) {
+ final long handle;
if ((normCapacity & subpageOverflowMask) != 0) { // >= pageSize
- return allocateRun(normCapacity);
+ handle = allocateRun(normCapacity);
} else {
- return allocateSubpage(normCapacity);
+ handle = allocateSubpage(normCapacity);
}
+
+ if (handle < 0) {
+ return false;
+ }
+ ByteBuffer nioBuffer = cachedNioBuffers != null ? cachedNioBuffers.pollLast() : null;
+ initBuf(buf, nioBuffer, handle, reqCapacity);
+ return true;
}
/**
@@ -311,8 +330,8 @@ private long allocateRun(int normCapacity) {
}
/**
- * Create/ initialize a new PoolSubpage of normCapacity
- * Any PoolSubpage created/ initialized here is added to subpage pool in the PoolArena that owns this PoolChunk
+ * Create / initialize a new PoolSubpage of normCapacity
+ * Any PoolSubpage created / initialized here is added to subpage pool in the PoolArena that owns this PoolChunk
*
* @param normCapacity normalized capacity
* @return index in memoryMap
@@ -321,8 +340,8 @@ private long allocateSubpage(int normCapacity) {
// Obtain the head of the PoolSubPage pool that is owned by the PoolArena and synchronize on it.
// This is need as we may add it back and so alter the linked-list structure.
PoolSubpage head = arena.findSubpagePoolHead(normCapacity);
+ int d = maxOrder; // subpages are only be allocated from pages i.e., leaves
synchronized (head) {
- int d = maxOrder; // subpages are only be allocated from pages i.e., leaves
int id = allocateNode(d);
if (id < 0) {
return id;
@@ -353,7 +372,7 @@ private long allocateSubpage(int normCapacity) {
*
* @param handle handle to free
*/
- void free(long handle) {
+ void free(long handle, ByteBuffer nioBuffer) {
int memoryMapIdx = memoryMapIdx(handle);
int bitmapIdx = bitmapIdx(handle);
@@ -373,26 +392,32 @@ void free(long handle) {
freeBytes += runLength(memoryMapIdx);
setValue(memoryMapIdx, depth(memoryMapIdx));
updateParentsFree(memoryMapIdx);
+
+ if (nioBuffer != null && cachedNioBuffers != null &&
+ cachedNioBuffers.size() < PooledByteBufAllocator.DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK) {
+ cachedNioBuffers.offer(nioBuffer);
+ }
}
- void initBuf(PooledByteBuf buf, long handle, int reqCapacity) {
+ void initBuf(PooledByteBuf buf, ByteBuffer nioBuffer, long handle, int reqCapacity) {
int memoryMapIdx = memoryMapIdx(handle);
int bitmapIdx = bitmapIdx(handle);
if (bitmapIdx == 0) {
byte val = value(memoryMapIdx);
assert val == unusable : String.valueOf(val);
- buf.init(this, handle, runOffset(memoryMapIdx) + offset, reqCapacity, runLength(memoryMapIdx),
- arena.parent.threadCache());
+ buf.init(this, nioBuffer, handle, runOffset(memoryMapIdx) + offset,
+ reqCapacity, runLength(memoryMapIdx), arena.parent.threadCache());
} else {
- initBufWithSubpage(buf, handle, bitmapIdx, reqCapacity);
+ initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx, reqCapacity);
}
}
- void initBufWithSubpage(PooledByteBuf buf, long handle, int reqCapacity) {
- initBufWithSubpage(buf, handle, bitmapIdx(handle), reqCapacity);
+ void initBufWithSubpage(PooledByteBuf buf, ByteBuffer nioBuffer, long handle, int reqCapacity) {
+ initBufWithSubpage(buf, nioBuffer, handle, bitmapIdx(handle), reqCapacity);
}
- private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx, int reqCapacity) {
+ private void initBufWithSubpage(PooledByteBuf buf, ByteBuffer nioBuffer,
+ long handle, int bitmapIdx, int reqCapacity) {
assert bitmapIdx != 0;
int memoryMapIdx = memoryMapIdx(handle);
@@ -402,7 +427,7 @@ private void initBufWithSubpage(PooledByteBuf buf, long handle, int bitmapIdx
assert reqCapacity <= subpage.elemSize;
buf.init(
- this, handle,
+ this, nioBuffer, handle,
runOffset(memoryMapIdx) + (bitmapIdx & 0x3FFFFFFF) * subpage.elemSize + offset,
reqCapacity, subpage.elemSize, arena.parent.threadCache());
}
diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java
index f92834d85c4f..e610be8e9b1a 100644
--- a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java
+++ b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java
@@ -25,6 +25,8 @@
import static java.lang.Math.*;
+import java.nio.ByteBuffer;
+
final class PoolChunkList implements PoolChunkListMetric {
private static final Iterator EMPTY_METRICS = Collections.emptyList().iterator();
private final PoolArena arena;
@@ -75,21 +77,14 @@ void prevList(PoolChunkList prevList) {
}
boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) {
- if (head == null || normCapacity > maxCapacity) {
+ if (normCapacity > maxCapacity) {
// Either this PoolChunkList is empty or the requested capacity is larger then the capacity which can
// be handled by the PoolChunks that are contained in this PoolChunkList.
return false;
}
- for (PoolChunk cur = head;;) {
- long handle = cur.allocate(normCapacity);
- if (handle < 0) {
- cur = cur.next;
- if (cur == null) {
- return false;
- }
- } else {
- cur.initBuf(buf, handle, reqCapacity);
+ for (PoolChunk cur = head; cur != null; cur = cur.next) {
+ if (cur.allocate(buf, reqCapacity, normCapacity)) {
if (cur.usage() >= maxUsage) {
remove(cur);
nextList.add(cur);
@@ -97,10 +92,11 @@ boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) {
return true;
}
}
+ return false;
}
- boolean free(PoolChunk chunk, long handle) {
- chunk.free(handle);
+ boolean free(PoolChunk chunk, long handle, ByteBuffer nioBuffer) {
+ chunk.free(handle, nioBuffer);
if (chunk.usage() < minUsage) {
remove(chunk);
// Move the PoolChunk down the PoolChunkList linked-list.
diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java
index 3503748c0d9b..01a69d570064 100644
--- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java
+++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java
@@ -17,6 +17,8 @@
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.buffer.PoolArena.SizeClass;
import io.netty.util.Recycler;
import io.netty.util.Recycler.Handle;
@@ -27,6 +29,7 @@
import java.nio.ByteBuffer;
import java.util.Queue;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Acts a Thread cache for allocations. This implementation is moduled after
@@ -54,6 +57,7 @@ final class PoolThreadCache {
private final int numShiftsNormalDirect;
private final int numShiftsNormalHeap;
private final int freeSweepAllocationThreshold;
+ private final AtomicBoolean freed = new AtomicBoolean();
private int allocations;
@@ -63,10 +67,7 @@ final class PoolThreadCache {
PoolThreadCache(PoolArena heapArena, PoolArena directArena,
int tinyCacheSize, int smallCacheSize, int normalCacheSize,
int maxCachedBufferCapacity, int freeSweepAllocationThreshold) {
- if (maxCachedBufferCapacity < 0) {
- throw new IllegalArgumentException("maxCachedBufferCapacity: "
- + maxCachedBufferCapacity + " (expected: >= 0)");
- }
+ checkPositiveOrZero(maxCachedBufferCapacity, "maxCachedBufferCapacity");
this.freeSweepAllocationThreshold = freeSweepAllocationThreshold;
this.heapArena = heapArena;
this.directArena = directArena;
@@ -198,12 +199,13 @@ private boolean allocate(MemoryRegionCache> cache, PooledByteBuf buf, int reqC
* Returns {@code true} if it fit into the cache {@code false} otherwise.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
- boolean add(PoolArena> area, PoolChunk chunk, long handle, int normCapacity, SizeClass sizeClass) {
+ boolean add(PoolArena> area, PoolChunk chunk, ByteBuffer nioBuffer,
+ long handle, int normCapacity, SizeClass sizeClass) {
MemoryRegionCache> cache = cache(area, normCapacity, sizeClass);
if (cache == null) {
return false;
}
- return cache.add(chunk, handle);
+ return cache.add(chunk, nioBuffer, handle);
}
private MemoryRegionCache> cache(PoolArena> area, int normCapacity, SizeClass sizeClass) {
@@ -219,27 +221,42 @@ private MemoryRegionCache> cache(PoolArena> area, int normCapacity, SizeClas
}
}
+ /// TODO: In the future when we move to Java9+ we should use java.lang.ref.Cleaner.
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ super.finalize();
+ } finally {
+ free();
+ }
+ }
+
/**
* Should be called if the Thread that uses this cache is about to exist to release resources out of the cache
*/
void free() {
- int numFreed = free(tinySubPageDirectCaches) +
- free(smallSubPageDirectCaches) +
- free(normalDirectCaches) +
- free(tinySubPageHeapCaches) +
- free(smallSubPageHeapCaches) +
- free(normalHeapCaches);
-
- if (numFreed > 0 && logger.isDebugEnabled()) {
- logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed, Thread.currentThread().getName());
- }
+ // As free() may be called either by the finalizer or by FastThreadLocal.onRemoval(...) we need to ensure
+ // we only call this one time.
+ if (freed.compareAndSet(false, true)) {
+ int numFreed = free(tinySubPageDirectCaches) +
+ free(smallSubPageDirectCaches) +
+ free(normalDirectCaches) +
+ free(tinySubPageHeapCaches) +
+ free(smallSubPageHeapCaches) +
+ free(normalHeapCaches);
+
+ if (numFreed > 0 && logger.isDebugEnabled()) {
+ logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed,
+ Thread.currentThread().getName());
+ }
- if (directArena != null) {
- directArena.numThreadCaches.getAndDecrement();
- }
+ if (directArena != null) {
+ directArena.numThreadCaches.getAndDecrement();
+ }
- if (heapArena != null) {
- heapArena.numThreadCaches.getAndDecrement();
+ if (heapArena != null) {
+ heapArena.numThreadCaches.getAndDecrement();
+ }
}
}
@@ -329,8 +346,8 @@ private static final class SubPageMemoryRegionCache extends MemoryRegionCache
@Override
protected void initBuf(
- PoolChunk chunk, long handle, PooledByteBuf buf, int reqCapacity) {
- chunk.initBufWithSubpage(buf, handle, reqCapacity);
+ PoolChunk chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf buf, int reqCapacity) {
+ chunk.initBufWithSubpage(buf, nioBuffer, handle, reqCapacity);
}
}
@@ -344,8 +361,8 @@ private static final class NormalMemoryRegionCache extends MemoryRegionCache<
@Override
protected void initBuf(
- PoolChunk chunk, long handle, PooledByteBuf buf, int reqCapacity) {
- chunk.initBuf(buf, handle, reqCapacity);
+ PoolChunk chunk, ByteBuffer nioBuffer, long handle, PooledByteBuf buf, int reqCapacity) {
+ chunk.initBuf(buf, nioBuffer, handle, reqCapacity);
}
}
@@ -364,15 +381,15 @@ private abstract static class MemoryRegionCache {
/**
* Init the {@link PooledByteBuf} using the provided chunk and handle with the capacity restrictions.
*/
- protected abstract void initBuf(PoolChunk chunk, long handle,
+ protected abstract void initBuf(PoolChunk chunk, ByteBuffer nioBuffer, long handle,
PooledByteBuf buf, int reqCapacity);
/**
* Add to cache if not already full.
*/
@SuppressWarnings("unchecked")
- public final boolean add(PoolChunk chunk, long handle) {
- Entry entry = newEntry(chunk, handle);
+ public final boolean add(PoolChunk chunk, ByteBuffer nioBuffer, long handle) {
+ Entry entry = newEntry(chunk, nioBuffer, handle);
boolean queued = queue.offer(entry);
if (!queued) {
// If it was not possible to cache the chunk, immediately recycle the entry
@@ -390,7 +407,7 @@ public final boolean allocate(PooledByteBuf buf, int reqCapacity) {
if (entry == null) {
return false;
}
- initBuf(entry.chunk, entry.handle, buf, reqCapacity);
+ initBuf(entry.chunk, entry.nioBuffer, entry.handle, buf, reqCapacity);
entry.recycle();
// allocations is not thread-safe which is fine as this is only called from the same thread all time.
@@ -436,16 +453,18 @@ public final void trim() {
private void freeEntry(Entry entry) {
PoolChunk chunk = entry.chunk;
long handle = entry.handle;
+ ByteBuffer nioBuffer = entry.nioBuffer;
// recycle now so PoolChunk can be GC'ed.
entry.recycle();
- chunk.arena.freeChunk(chunk, handle, sizeClass);
+ chunk.arena.freeChunk(chunk, handle, sizeClass, nioBuffer);
}
static final class Entry {
final Handle> recyclerHandle;
PoolChunk chunk;
+ ByteBuffer nioBuffer;
long handle = -1;
Entry(Handle> recyclerHandle) {
@@ -454,15 +473,17 @@ static final class Entry {
void recycle() {
chunk = null;
+ nioBuffer = null;
handle = -1;
recyclerHandle.recycle(this);
}
}
@SuppressWarnings("rawtypes")
- private static Entry newEntry(PoolChunk> chunk, long handle) {
+ private static Entry newEntry(PoolChunk> chunk, ByteBuffer nioBuffer, long handle) {
Entry entry = RECYCLER.get();
entry.chunk = chunk;
+ entry.nioBuffer = nioBuffer;
entry.handle = handle;
return entry;
}
diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java
index 56a4be387232..beffbb07ee5a 100644
--- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java
@@ -33,7 +33,7 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf {
protected int length;
int maxLength;
PoolThreadCache cache;
- private ByteBuffer tmpNioBuf;
+ ByteBuffer tmpNioBuf;
private ByteBufAllocator allocator;
@SuppressWarnings("unchecked")
@@ -42,27 +42,29 @@ protected PooledByteBuf(Recycler.Handle extends PooledByteBuf> recyclerHand
this.recyclerHandle = (Handle>) recyclerHandle;
}
- void init(PoolChunk chunk, long handle, int offset, int length, int maxLength, PoolThreadCache cache) {
- init0(chunk, handle, offset, length, maxLength, cache);
+ void init(PoolChunk chunk, ByteBuffer nioBuffer,
+ long handle, int offset, int length, int maxLength, PoolThreadCache cache) {
+ init0(chunk, nioBuffer, handle, offset, length, maxLength, cache);
}
void initUnpooled(PoolChunk chunk, int length) {
- init0(chunk, 0, chunk.offset, length, length, null);
+ init0(chunk, null, 0, chunk.offset, length, length, null);
}
- private void init0(PoolChunk chunk, long handle, int offset, int length, int maxLength, PoolThreadCache cache) {
+ private void init0(PoolChunk chunk, ByteBuffer nioBuffer,
+ long handle, int offset, int length, int maxLength, PoolThreadCache cache) {
assert handle >= 0;
assert chunk != null;
this.chunk = chunk;
memory = chunk.memory;
+ tmpNioBuf = nioBuffer;
allocator = chunk.arena.parent;
this.cache = cache;
this.handle = handle;
this.offset = offset;
this.length = length;
this.maxLength = maxLength;
- tmpNioBuf = null;
}
/**
@@ -166,8 +168,8 @@ protected final void deallocate() {
final long handle = this.handle;
this.handle = -1;
memory = null;
+ chunk.arena.free(chunk, tmpNioBuf, handle, maxLength, cache);
tmpNioBuf = null;
- chunk.arena.free(chunk, handle, maxLength, cache);
chunk = null;
recycle();
}
diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java
index b613fea9e1cb..bcfc9ceb1a13 100644
--- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java
+++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java
@@ -16,6 +16,8 @@
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.util.NettyRuntime;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.FastThreadLocalThread;
@@ -45,6 +47,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements
private static final int DEFAULT_CACHE_TRIM_INTERVAL;
private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
+ static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK;
private static final int MIN_PAGE_SIZE = 4096;
private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
@@ -116,6 +119,11 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements
DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
"io.netty.allocator.directMemoryCacheAlignment", 0);
+ // Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array
+ // of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful.
+ DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
+ "io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);
+
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
@@ -136,6 +144,8 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements
logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
+ logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
+ DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
}
}
@@ -207,17 +217,10 @@ public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectA
this.normalCacheSize = normalCacheSize;
chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
- if (nHeapArena < 0) {
- throw new IllegalArgumentException("nHeapArena: " + nHeapArena + " (expected: >= 0)");
- }
- if (nDirectArena < 0) {
- throw new IllegalArgumentException("nDirectArea: " + nDirectArena + " (expected: >= 0)");
- }
+ checkPositiveOrZero(nHeapArena, "nHeapArena");
+ checkPositiveOrZero(nDirectArena, "nDirectArena");
- if (directMemoryCacheAlignment < 0) {
- throw new IllegalArgumentException("directMemoryCacheAlignment: "
- + directMemoryCacheAlignment + " (expected: >= 0)");
- }
+ checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
}
@@ -580,7 +583,7 @@ final long usedDirectMemory() {
return usedMemory(directArenas);
}
- private static long usedMemory(PoolArena>... arenas) {
+ private static long usedMemory(PoolArena>[] arenas) {
if (arenas == null) {
return -1;
}
diff --git a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java
index 3c43509c0545..9601150b319d 100644
--- a/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/PooledDirectByteBuf.java
@@ -351,8 +351,8 @@ public ByteBuf setBytes(int index, ByteBuffer src) {
@Override
public int setBytes(int index, InputStream in, int length) throws IOException {
checkIndex(index, length);
- byte[] tmp = new byte[length];
- int readBytes = in.read(tmp);
+ byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
+ int readBytes = in.read(tmp, 0, length);
if (readBytes <= 0) {
return readBytes;
}
diff --git a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java
index 1dcc3702c4b7..e2dc22cb07db 100644
--- a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java
@@ -49,9 +49,9 @@ private PooledUnsafeDirectByteBuf(Recycler.Handle rec
}
@Override
- void init(PoolChunk chunk, long handle, int offset, int length, int maxLength,
- PoolThreadCache cache) {
- super.init(chunk, handle, offset, length, maxLength, cache);
+ void init(PoolChunk chunk, ByteBuffer nioBuffer,
+ long handle, int offset, int length, int maxLength, PoolThreadCache cache) {
+ super.init(chunk, nioBuffer, handle, offset, length, maxLength, cache);
initMemoryAddress();
}
diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java
index 406514f384b0..c7cda05fd979 100644
--- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java
@@ -355,11 +355,11 @@ public ByteBuf getBytes(int index, OutputStream out, int length) throws IOExcept
if (buffer.hasArray()) {
out.write(buffer.array(), index + buffer.arrayOffset(), length);
} else {
- byte[] tmp = new byte[length];
+ byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
ByteBuffer tmpBuf = internalNioBuffer();
tmpBuf.clear().position(index);
- tmpBuf.get(tmp);
- out.write(tmp);
+ tmpBuf.get(tmp, 0, length);
+ out.write(tmp, 0, length);
}
return this;
}
diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
index 5d54a1f9f14c..abf27663f7a9 100644
--- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java
@@ -997,6 +997,11 @@ public int refCnt() {
return buf.refCnt();
}
+ @Override
+ final boolean isAccessible() {
+ return buf.isAccessible();
+ }
+
@Override
public ByteBuf retain() {
buf.retain();
diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java
index 3639a1776848..d7df1928857e 100644
--- a/buffer/src/main/java/io/netty/buffer/Unpooled.java
+++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java
@@ -15,14 +15,14 @@
*/
package io.netty.buffer;
+import io.netty.buffer.CompositeByteBuf.ByteWrapper;
import io.netty.util.internal.PlatformDependent;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Arrays;
/**
@@ -219,7 +219,7 @@ public static ByteBuf wrappedBuffer(long memoryAddress, int size, boolean doFree
* Creates a new buffer which wraps the specified buffer's readable bytes.
* A modification on the specified buffer's content will be visible to the
* returned buffer.
- * @param buffer The buffer to wrap. Reference count ownership of this variable is transfered to this method.
+ * @param buffer The buffer to wrap. Reference count ownership of this variable is transferred to this method.
* @return The readable portion of the {@code buffer}, or an empty buffer if there is no readable portion.
* The caller is responsible for releasing this buffer.
*/
@@ -238,18 +238,18 @@ public static ByteBuf wrappedBuffer(ByteBuf buffer) {
* content will be visible to the returned buffer.
*/
public static ByteBuf wrappedBuffer(byte[]... arrays) {
- return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, arrays);
+ return wrappedBuffer(arrays.length, arrays);
}
/**
* Creates a new big-endian composite buffer which wraps the readable bytes of the
* specified buffers without copying them. A modification on the content
* of the specified buffers will be visible to the returned buffer.
- * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method.
+ * @param buffers The buffers to wrap. Reference count ownership of all variables is transferred to this method.
* @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer.
*/
public static ByteBuf wrappedBuffer(ByteBuf... buffers) {
- return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers);
+ return wrappedBuffer(buffers.length, buffers);
}
/**
@@ -258,50 +258,49 @@ public static ByteBuf wrappedBuffer(ByteBuf... buffers) {
* specified buffers will be visible to the returned buffer.
*/
public static ByteBuf wrappedBuffer(ByteBuffer... buffers) {
- return wrappedBuffer(AbstractByteBufAllocator.DEFAULT_MAX_COMPONENTS, buffers);
+ return wrappedBuffer(buffers.length, buffers);
}
- /**
- * Creates a new big-endian composite buffer which wraps the specified
- * arrays without copying them. A modification on the specified arrays'
- * content will be visible to the returned buffer.
- */
- public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) {
- switch (arrays.length) {
+ static ByteBuf wrappedBuffer(int maxNumComponents, ByteWrapper wrapper, T[] array) {
+ switch (array.length) {
case 0:
break;
case 1:
- if (arrays[0].length != 0) {
- return wrappedBuffer(arrays[0]);
+ if (!wrapper.isEmpty(array[0])) {
+ return wrapper.wrap(array[0]);
}
break;
default:
- // Get the list of the component, while guessing the byte order.
- final List components = new ArrayList(arrays.length);
- for (byte[] a: arrays) {
- if (a == null) {
- break;
+ for (int i = 0, len = array.length; i < len; i++) {
+ T bytes = array[i];
+ if (bytes == null) {
+ return EMPTY_BUFFER;
}
- if (a.length > 0) {
- components.add(wrappedBuffer(a));
+ if (!wrapper.isEmpty(bytes)) {
+ return new CompositeByteBuf(ALLOC, false, maxNumComponents, wrapper, array, i);
}
}
-
- if (!components.isEmpty()) {
- return new CompositeByteBuf(ALLOC, false, maxNumComponents, components);
- }
}
return EMPTY_BUFFER;
}
+ /**
+ * Creates a new big-endian composite buffer which wraps the specified
+ * arrays without copying them. A modification on the specified arrays'
+ * content will be visible to the returned buffer.
+ */
+ public static ByteBuf wrappedBuffer(int maxNumComponents, byte[]... arrays) {
+ return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_ARRAY_WRAPPER, arrays);
+ }
+
/**
* Creates a new big-endian composite buffer which wraps the readable bytes of the
* specified buffers without copying them. A modification on the content
* of the specified buffers will be visible to the returned buffer.
* @param maxNumComponents Advisement as to how many independent buffers are allowed to exist before
* consolidation occurs.
- * @param buffers The buffers to wrap. Reference count ownership of all variables is transfered to this method.
+ * @param buffers The buffers to wrap. Reference count ownership of all variables is transferred to this method.
* @return The readable portion of the {@code buffers}. The caller is responsible for releasing this buffer.
*/
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) {
@@ -320,7 +319,7 @@ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) {
for (int i = 0; i < buffers.length; i++) {
ByteBuf buf = buffers[i];
if (buf.isReadable()) {
- return new CompositeByteBuf(ALLOC, false, maxNumComponents, buffers, i, buffers.length);
+ return new CompositeByteBuf(ALLOC, false, maxNumComponents, buffers, i);
}
buf.release();
}
@@ -335,32 +334,7 @@ public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuf... buffers) {
* specified buffers will be visible to the returned buffer.
*/
public static ByteBuf wrappedBuffer(int maxNumComponents, ByteBuffer... buffers) {
- switch (buffers.length) {
- case 0:
- break;
- case 1:
- if (buffers[0].hasRemaining()) {
- return wrappedBuffer(buffers[0].order(BIG_ENDIAN));
- }
- break;
- default:
- // Get the list of the component, while guessing the byte order.
- final List components = new ArrayList(buffers.length);
- for (ByteBuffer b: buffers) {
- if (b == null) {
- break;
- }
- if (b.remaining() > 0) {
- components.add(wrappedBuffer(b.order(BIG_ENDIAN)));
- }
- }
-
- if (!components.isEmpty()) {
- return new CompositeByteBuf(ALLOC, false, maxNumComponents, components);
- }
- }
-
- return EMPTY_BUFFER;
+ return wrappedBuffer(maxNumComponents, CompositeByteBuf.BYTE_BUFFER_WRAPPER, buffers);
}
/**
@@ -399,7 +373,7 @@ public static ByteBuf copiedBuffer(byte[] array, int offset, int length) {
if (length == 0) {
return EMPTY_BUFFER;
}
- byte[] copy = new byte[length];
+ byte[] copy = PlatformDependent.allocateUninitializedArray(length);
System.arraycopy(array, offset, copy, 0, length);
return wrappedBuffer(copy);
}
@@ -415,7 +389,7 @@ public static ByteBuf copiedBuffer(ByteBuffer buffer) {
if (length == 0) {
return EMPTY_BUFFER;
}
- byte[] copy = new byte[length];
+ byte[] copy = PlatformDependent.allocateUninitializedArray(length);
// Duplicate the buffer so we not adjust the position during our get operation.
// See https://github.com/netty/netty/issues/3896
ByteBuffer duplicate = buffer.duplicate();
@@ -472,7 +446,7 @@ public static ByteBuf copiedBuffer(byte[]... arrays) {
return EMPTY_BUFFER;
}
- byte[] mergedArray = new byte[length];
+ byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length);
for (int i = 0, j = 0; i < arrays.length; i ++) {
byte[] a = arrays[i];
System.arraycopy(a, 0, mergedArray, j, a.length);
@@ -526,7 +500,7 @@ public static ByteBuf copiedBuffer(ByteBuf... buffers) {
return EMPTY_BUFFER;
}
- byte[] mergedArray = new byte[length];
+ byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length);
for (int i = 0, j = 0; i < buffers.length; i ++) {
ByteBuf b = buffers[i];
int bLen = b.readableBytes();
@@ -581,7 +555,7 @@ public static ByteBuf copiedBuffer(ByteBuffer... buffers) {
return EMPTY_BUFFER;
}
- byte[] mergedArray = new byte[length];
+ byte[] mergedArray = PlatformDependent.allocateUninitializedArray(length);
for (int i = 0, j = 0; i < buffers.length; i ++) {
// Duplicate the buffer so we not adjust the position during our get operation.
// See https://github.com/netty/netty/issues/3896
@@ -881,9 +855,36 @@ public static ByteBuf unreleasableBuffer(ByteBuf buf) {
/**
* Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will
* not try to slice the given {@link ByteBuf}s to reduce GC-Pressure.
+ *
+ * @deprecated Use {@link #wrappedUnmodifiableBuffer(ByteBuf...)}.
*/
+ @Deprecated
public static ByteBuf unmodifiableBuffer(ByteBuf... buffers) {
- return new FixedCompositeByteBuf(ALLOC, buffers);
+ return wrappedUnmodifiableBuffer(true, buffers);
+ }
+
+ /**
+ * Wrap the given {@link ByteBuf}s in an unmodifiable {@link ByteBuf}. Be aware the returned {@link ByteBuf} will
+ * not try to slice the given {@link ByteBuf}s to reduce GC-Pressure.
+ *
+ * The returned {@link ByteBuf} may wrap the provided array directly, and so should not be subsequently modified.
+ */
+ public static ByteBuf wrappedUnmodifiableBuffer(ByteBuf... buffers) {
+ return wrappedUnmodifiableBuffer(false, buffers);
+ }
+
+ private static ByteBuf wrappedUnmodifiableBuffer(boolean copy, ByteBuf... buffers) {
+ switch (buffers.length) {
+ case 0:
+ return EMPTY_BUFFER;
+ case 1:
+ return buffers[0].asReadOnly();
+ default:
+ if (copy) {
+ buffers = Arrays.copyOf(buffers, buffers.length, ByteBuf[].class);
+ }
+ return new FixedCompositeByteBuf(ALLOC, buffers);
+ }
}
private Unpooled() {
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java
index 4edf0dcd3d93..6fe188ed8bdb 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java
+++ b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java
@@ -140,14 +140,14 @@ private static final class InstrumentedUnpooledUnsafeHeapByteBuf extends Unpoole
}
@Override
- byte[] allocateArray(int initialCapacity) {
+ protected byte[] allocateArray(int initialCapacity) {
byte[] bytes = super.allocateArray(initialCapacity);
((UnpooledByteBufAllocator) alloc()).incrementHeap(bytes.length);
return bytes;
}
@Override
- void freeArray(byte[] array) {
+ protected void freeArray(byte[] array) {
int length = array.length;
super.freeArray(array);
((UnpooledByteBufAllocator) alloc()).decrementHeap(length);
@@ -160,14 +160,14 @@ private static final class InstrumentedUnpooledHeapByteBuf extends UnpooledHeapB
}
@Override
- byte[] allocateArray(int initialCapacity) {
+ protected byte[] allocateArray(int initialCapacity) {
byte[] bytes = super.allocateArray(initialCapacity);
((UnpooledByteBufAllocator) alloc()).incrementHeap(bytes.length);
return bytes;
}
@Override
- void freeArray(byte[] array) {
+ protected void freeArray(byte[] array) {
int length = array.length;
super.freeArray(array);
((UnpooledByteBufAllocator) alloc()).decrementHeap(length);
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java
index f93e256588cd..bcf816c6d2e7 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java
@@ -15,6 +15,8 @@
*/
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.util.internal.PlatformDependent;
import java.io.IOException;
@@ -52,12 +54,8 @@ public UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int ma
if (alloc == null) {
throw new NullPointerException("alloc");
}
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
- }
- if (maxCapacity < 0) {
- throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
- }
+ checkPositiveOrZero(initialCapacity, "initialCapacity");
+ checkPositiveOrZero(maxCapacity, "maxCapacity");
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
@@ -561,8 +559,8 @@ public int setBytes(int index, InputStream in, int length) throws IOException {
if (buffer.hasArray()) {
return in.read(buffer.array(), buffer.arrayOffset() + index, length);
} else {
- byte[] tmp = new byte[length];
- int readBytes = in.read(tmp);
+ byte[] tmp = ByteBufUtil.threadLocalTempArray(length);
+ int readBytes = in.read(tmp, 0, length);
if (readBytes <= 0) {
return readBytes;
}
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java
index 26b1c5122b5d..f37ceb0559aa 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java
@@ -84,11 +84,11 @@ protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int m
setIndex(0, initialArray.length);
}
- byte[] allocateArray(int initialCapacity) {
+ protected byte[] allocateArray(int initialCapacity) {
return new byte[initialCapacity];
}
- void freeArray(byte[] array) {
+ protected void freeArray(byte[] array) {
// NOOP
}
@@ -536,7 +536,7 @@ protected void _setLongLE(int index, long value) {
@Override
public ByteBuf copy(int index, int length) {
checkIndex(index, length);
- byte[] copiedArray = new byte[length];
+ byte[] copiedArray = PlatformDependent.allocateUninitializedArray(length);
System.arraycopy(array, index, copiedArray, 0, length);
return new UnpooledHeapByteBuf(alloc(), copiedArray, maxCapacity());
}
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java
index 5ff0c222d27b..0658139d7501 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java
@@ -15,6 +15,8 @@
*/
package io.netty.buffer;
+import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
+
import io.netty.util.internal.PlatformDependent;
import java.io.IOException;
@@ -53,12 +55,8 @@ public UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity,
if (alloc == null) {
throw new NullPointerException("alloc");
}
- if (initialCapacity < 0) {
- throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
- }
- if (maxCapacity < 0) {
- throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
- }
+ checkPositiveOrZero(initialCapacity, "initialCapacity");
+ checkPositiveOrZero(maxCapacity, "maxCapacity");
if (initialCapacity > maxCapacity) {
throw new IllegalArgumentException(String.format(
"initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java
index 51786f1567c4..0fbe856466f1 100644
--- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeHeapByteBuf.java
@@ -30,7 +30,7 @@ class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf {
}
@Override
- byte[] allocateArray(int initialCapacity) {
+ protected byte[] allocateArray(int initialCapacity) {
return PlatformDependent.allocateUninitializedArray(initialCapacity);
}
diff --git a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java
index 7d866d5b433a..05918a8ebb18 100644
--- a/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java
+++ b/buffer/src/main/java/io/netty/buffer/UnsafeByteBufUtil.java
@@ -584,7 +584,9 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out
buf.checkIndex(index, length);
if (length != 0) {
int len = Math.min(length, ByteBufUtil.WRITE_CHUNK_SIZE);
- if (buf.alloc().isDirectBufferPooled()) {
+ if (len <= ByteBufUtil.MAX_TL_ARRAY_LEN || !buf.alloc().isDirectBufferPooled()) {
+ getBytes(addr, ByteBufUtil.threadLocalTempArray(len), 0, len, out, length);
+ } else {
// if direct buffers are pooled chances are good that heap buffers are pooled as well.
ByteBuf tmpBuf = buf.alloc().heapBuffer(len);
try {
@@ -594,8 +596,6 @@ static void getBytes(AbstractByteBuf buf, long addr, int index, OutputStream out
} finally {
tmpBuf.release();
}
- } else {
- getBytes(addr, new byte[len], 0, len, out, length);
}
}
}
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
index 45aa60ce889b..33570e200447 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java
@@ -1033,4 +1033,9 @@ public boolean release() {
public boolean release(int decrement) {
return buf.release(decrement);
}
+
+ @Override
+ final boolean isAccessible() {
+ return buf.isAccessible();
+ }
}
diff --git a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java
index 8f5161620f98..c300ea087e25 100644
--- a/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java
+++ b/buffer/src/main/java/io/netty/buffer/WrappedCompositeByteBuf.java
@@ -423,6 +423,11 @@ public final int refCnt() {
return wrapped.refCnt();
}
+ @Override
+ final boolean isAccessible() {
+ return wrapped.isAccessible();
+ }
+
@Override
public ByteBuf duplicate() {
return wrapped.duplicate();
diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java
index 1af64a7392d6..59194ab374d4 100644
--- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java
+++ b/buffer/src/test/java/io/netty/buffer/AbstractByteBufTest.java
@@ -30,6 +30,7 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.CharBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
@@ -3648,11 +3649,23 @@ public void testSetUtf16CharSequence() {
testSetGetCharSequence(CharsetUtil.UTF_16);
}
+ private static final CharBuffer EXTENDED_ASCII_CHARS, ASCII_CHARS;
+
+ static {
+ char[] chars = new char[256];
+ for (char c = 0; c < chars.length; c++) {
+ chars[c] = c;
+ }
+ EXTENDED_ASCII_CHARS = CharBuffer.wrap(chars);
+ ASCII_CHARS = CharBuffer.wrap(chars, 0, 128);
+ }
+
private void testSetGetCharSequence(Charset charset) {
- ByteBuf buf = newBuffer(16);
- String sequence = "AB";
+ ByteBuf buf = newBuffer(1024);
+ CharBuffer sequence = CharsetUtil.US_ASCII.equals(charset)
+ ? ASCII_CHARS : EXTENDED_ASCII_CHARS;
int bytes = buf.setCharSequence(1, sequence, charset);
- assertEquals(sequence, buf.getCharSequence(1, bytes, charset));
+ assertEquals(sequence, CharBuffer.wrap(buf.getCharSequence(1, bytes, charset)));
buf.release();
}
@@ -3677,12 +3690,13 @@ public void testWriteReadUtf16CharSequence() {
}
private void testWriteReadCharSequence(Charset charset) {
- ByteBuf buf = newBuffer(16);
- String sequence = "AB";
+ ByteBuf buf = newBuffer(1024);
+ CharBuffer sequence = CharsetUtil.US_ASCII.equals(charset)
+ ? ASCII_CHARS : EXTENDED_ASCII_CHARS;
buf.writerIndex(1);
int bytes = buf.writeCharSequence(sequence, charset);
buf.readerIndex(1);
- assertEquals(sequence, buf.readCharSequence(bytes, charset));
+ assertEquals(sequence, CharBuffer.wrap(buf.readCharSequence(bytes, charset)));
buf.release();
}
diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java
index f51475bf9ae6..ffeebaac9c46 100644
--- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java
+++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java
@@ -16,6 +16,7 @@
package io.netty.buffer;
import io.netty.util.ReferenceCountUtil;
+import io.netty.util.internal.PlatformDependent;
import org.junit.Assume;
import org.junit.Test;
@@ -51,6 +52,8 @@
*/
public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
+ private static final ByteBufAllocator ALLOC = UnpooledByteBufAllocator.DEFAULT;
+
private final ByteOrder order;
protected AbstractCompositeByteBufTest(ByteOrder order) {
@@ -87,7 +90,7 @@ protected ByteBuf newBuffer(int length, int maxCapacity) {
buffers.add(EMPTY_BUFFER);
}
- ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[buffers.size()])).order(order);
+ ByteBuf buffer = wrappedBuffer(Integer.MAX_VALUE, buffers.toArray(new ByteBuf[0])).order(order);
// Truncate to the requested capacity.
buffer.capacity(length);
@@ -132,6 +135,41 @@ public void testComponentAtOffset() {
buf.release();
}
+ @Test
+ public void testToComponentIndex() {
+ CompositeByteBuf buf = (CompositeByteBuf) wrappedBuffer(new byte[]{1, 2, 3, 4, 5},
+ new byte[]{4, 5, 6, 7, 8, 9, 26}, new byte[]{10, 9, 8, 7, 6, 5, 33});
+
+ // spot checks
+ assertEquals(0, buf.toComponentIndex(4));
+ assertEquals(1, buf.toComponentIndex(5));
+ assertEquals(2, buf.toComponentIndex(15));
+
+ //Loop through each byte
+
+ byte index = 0;
+
+ while (index < buf.capacity()) {
+ int cindex = buf.toComponentIndex(index++);
+ assertTrue(cindex >= 0 && cindex < buf.numComponents());
+ }
+
+ buf.release();
+ }
+
+ @Test
+ public void testToByteIndex() {
+ CompositeByteBuf buf = (CompositeByteBuf) wrappedBuffer(new byte[]{1, 2, 3, 4, 5},
+ new byte[]{4, 5, 6, 7, 8, 9, 26}, new byte[]{10, 9, 8, 7, 6, 5, 33});
+
+ // spot checks
+ assertEquals(0, buf.toByteIndex(0));
+ assertEquals(5, buf.toByteIndex(1));
+ assertEquals(12, buf.toByteIndex(2));
+
+ buf.release();
+ }
+
@Test
public void testDiscardReadBytes3() {
ByteBuf a, b;
@@ -744,6 +782,20 @@ public void testRemoveLastComponentWithOthersLeft() {
buf.release();
}
+ @Test
+ public void testRemoveComponents() {
+ CompositeByteBuf buf = compositeBuffer();
+ for (int i = 0; i < 10; i++) {
+ buf.addComponent(wrappedBuffer(new byte[]{1, 2}));
+ }
+ assertEquals(10, buf.numComponents());
+ assertEquals(20, buf.capacity());
+ buf.removeComponents(4, 3);
+ assertEquals(7, buf.numComponents());
+ assertEquals(14, buf.capacity());
+ buf.release();
+ }
+
@Test
public void testGatheringWritesHeap() throws Exception {
testGatheringWrites(buffer().order(order), buffer().order(order));
@@ -1017,6 +1069,33 @@ public void testAddEmptyBufferInMiddle() {
cbuf.release();
}
+ @Test
+ public void testInsertEmptyBufferInMiddle() {
+ CompositeByteBuf cbuf = compositeBuffer();
+ ByteBuf buf1 = buffer().writeByte((byte) 1);
+ cbuf.addComponent(true, buf1);
+ ByteBuf buf2 = buffer().writeByte((byte) 2);
+ cbuf.addComponent(true, buf2);
+
+ // insert empty one between the first two
+ cbuf.addComponent(true, 1, EMPTY_BUFFER);
+
+ assertEquals(2, cbuf.readableBytes());
+ assertEquals((byte) 1, cbuf.readByte());
+ assertEquals((byte) 2, cbuf.readByte());
+
+ assertEquals(2, cbuf.capacity());
+ assertEquals(3, cbuf.numComponents());
+
+ byte[] dest = new byte[2];
+ // should skip over the empty one, not throw a java.lang.Error :)
+ cbuf.getBytes(0, dest);
+
+ assertArrayEquals(new byte[] {1, 2}, dest);
+
+ cbuf.release();
+ }
+
@Test
public void testIterator() {
CompositeByteBuf cbuf = compositeBuffer();
@@ -1116,6 +1195,97 @@ public void testReleasesItsComponents() {
assertEquals(0, buffer.refCnt());
}
+ @Test
+ public void testReleasesItsComponents2() {
+ // It is important to use a pooled allocator here to ensure
+ // the slices returned by readRetainedSlice are of type
+ // PooledSlicedByteBuf, which maintains an independent refcount
+ // (so that we can be sure to cover this case)
+ ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); // 1
+
+ buffer.writeBytes(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
+
+ // use readRetainedSlice this time - produces different kind of slices
+ ByteBuf s1 = buffer.readRetainedSlice(2); // 2
+ ByteBuf s2 = s1.readRetainedSlice(2); // 3
+ ByteBuf s3 = s2.readRetainedSlice(2); // 4
+ ByteBuf s4 = s3.readRetainedSlice(2); // 5
+
+ ByteBuf composite = Unpooled.compositeBuffer()
+ .addComponent(s1)
+ .addComponents(s2, s3, s4)
+ .order(ByteOrder.LITTLE_ENDIAN);
+
+ assertEquals(1, composite.refCnt());
+ assertEquals(2, buffer.refCnt());
+
+ // releasing composite should release the 4 components
+ composite.release();
+ assertEquals(0, composite.refCnt());
+ assertEquals(1, buffer.refCnt());
+
+ // last remaining ref to buffer
+ buffer.release();
+ assertEquals(0, buffer.refCnt());
+ }
+
+ @Test
+ public void testReleasesOnShrink() {
+
+ ByteBuf b1 = Unpooled.buffer(2).writeShort(1);
+ ByteBuf b2 = Unpooled.buffer(2).writeShort(2);
+
+ // composite takes ownership of s1 and s2
+ ByteBuf composite = Unpooled.compositeBuffer()
+ .addComponents(b1, b2);
+
+ assertEquals(4, composite.capacity());
+
+ // reduce capacity down to two, will drop the second component
+ composite.capacity(2);
+ assertEquals(2, composite.capacity());
+
+ // releasing composite should release the components
+ composite.release();
+ assertEquals(0, composite.refCnt());
+ assertEquals(0, b1.refCnt());
+ assertEquals(0, b2.refCnt());
+ }
+
+ @Test
+ public void testReleasesOnShrink2() {
+ // It is important to use a pooled allocator here to ensure
+ // the slices returned by readRetainedSlice are of type
+ // PooledSlicedByteBuf, which maintains an independent refcount
+ // (so that we can be sure to cover this case)
+ ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer();
+
+ buffer.writeShort(1).writeShort(2);
+
+ ByteBuf b1 = buffer.readRetainedSlice(2);
+ ByteBuf b2 = b1.retainedSlice(b1.readerIndex(), 2);
+
+ // composite takes ownership of b1 and b2
+ ByteBuf composite = Unpooled.compositeBuffer()
+ .addComponents(b1, b2);
+
+ assertEquals(4, composite.capacity());
+
+ // reduce capacity down to two, will drop the second component
+ composite.capacity(2);
+ assertEquals(2, composite.capacity());
+
+ // releasing composite should release the components
+ composite.release();
+ assertEquals(0, composite.refCnt());
+ assertEquals(0, b1.refCnt());
+ assertEquals(0, b2.refCnt());
+
+ // release last remaining ref to buffer
+ buffer.release();
+ assertEquals(0, buffer.refCnt());
+ }
+
@Test
public void testAllocatorIsSameWhenCopy() {
testAllocatorIsSameWhenCopy(false);
@@ -1136,4 +1306,76 @@ private void testAllocatorIsSameWhenCopy(boolean withIndexAndLength) {
buffer.release();
copy.release();
}
+
+ @Test
+ public void testDecomposeMultiple() {
+ testDecompose(150, 500, 3);
+ }
+
+ @Test
+ public void testDecomposeOne() {
+ testDecompose(310, 50, 1);
+ }
+
+ @Test
+ public void testDecomposeNone() {
+ testDecompose(310, 0, 0);
+ }
+
+ private static void testDecompose(int offset, int length, int expectedListSize) {
+ byte[] bytes = new byte[1024];
+ PlatformDependent.threadLocalRandom().nextBytes(bytes);
+ ByteBuf buf = wrappedBuffer(bytes);
+
+ CompositeByteBuf composite = compositeBuffer();
+ composite.addComponents(true,
+ buf.retainedSlice(100, 200),
+ buf.retainedSlice(300, 400),
+ buf.retainedSlice(700, 100));
+
+ ByteBuf slice = composite.slice(offset, length);
+ List bufferList = composite.decompose(offset, length);
+ assertEquals(expectedListSize, bufferList.size());
+ ByteBuf wrapped = wrappedBuffer(bufferList.toArray(new ByteBuf[0]));
+
+ assertEquals(slice, wrapped);
+ composite.release();
+ buf.release();
+
+ for (ByteBuf buffer: bufferList) {
+ assertEquals(0, buffer.refCnt());
+ }
+ }
+
+ @Test
+ public void testComponentsLessThanLowerBound() {
+ try {
+ new CompositeByteBuf(ALLOC, true, 0);
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("maxNumComponents: 0 (expected: >= 1)", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testComponentsEqualToLowerBound() {
+ assertCompositeBufCreated(1);
+ }
+
+ @Test
+ public void testComponentsGreaterThanLowerBound() {
+ assertCompositeBufCreated(5);
+ }
+
+ /**
+ * Assert that a new {@linkplain CompositeByteBuf} was created successfully with the desired number of max
+ * components.
+ */
+ private static void assertCompositeBufCreated(int expectedMaxComponents) {
+ CompositeByteBuf buf = new CompositeByteBuf(ALLOC, true, expectedMaxComponents);
+
+ assertEquals(expectedMaxComponents, buf.maxNumComponents());
+ assertTrue(buf.release());
+ }
+
}
diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java
index f05594d1c12e..222ddcf7ee73 100644
--- a/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java
+++ b/buffer/src/test/java/io/netty/buffer/ByteBufStreamTest.java
@@ -188,11 +188,13 @@ public void testReadLine() throws Exception {
String s = in.readLine();
assertNull(s);
- int charCount = 5; //total chars in the string below without new line characters
- byte[] abc = "a\nb\r\nc\nd\ne".getBytes(utf8);
+ int charCount = 7; //total chars in the string below without new line characters
+ byte[] abc = "\na\n\nb\r\nc\nd\ne".getBytes(utf8);
buf.writeBytes(abc);
in.mark(charCount);
+ assertEquals("", in.readLine());
assertEquals("a", in.readLine());
+ assertEquals("", in.readLine());
assertEquals("b", in.readLine());
assertEquals("c", in.readLine());
assertEquals("d", in.readLine());
diff --git a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java
index 481c70fc8430..b6260f7848bf 100644
--- a/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java
+++ b/buffer/src/test/java/io/netty/buffer/FixedCompositeByteBufTest.java
@@ -401,7 +401,7 @@ public void testHasMemoryAddressWhenEmpty() {
buf.release();
}
- @Test(expected = UnsupportedOperationException.class)
+ @Test
public void testHasNoMemoryAddressWhenMultipleBuffers() {
ByteBuf buf1 = directBuffer(10);
if (!buf1.hasMemoryAddress()) {
@@ -415,6 +415,8 @@ public void testHasNoMemoryAddressWhenMultipleBuffers() {
try {
buf.memoryAddress();
fail();
+ } catch (UnsupportedOperationException expected) {
+ // expected
} finally {
buf.release();
}
diff --git a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java
index 3fafef9c04ef..0e18eaf3f049 100644
--- a/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java
+++ b/buffer/src/test/java/io/netty/buffer/PoolArenaTest.java
@@ -16,6 +16,7 @@
package io.netty.buffer;
+import io.netty.util.internal.PlatformDependent;
import org.junit.Assert;
import org.junit.Test;
@@ -43,6 +44,25 @@ public void testNormalizeAlignedCapacity() throws Exception {
}
}
+ @Test
+ public void testDirectArenaOffsetCacheLine() throws Exception {
+ int capacity = 5;
+ int alignment = 128;
+
+ for (int i = 0; i < 1000; i++) {
+ ByteBuffer bb = PlatformDependent.useDirectBufferNoCleaner()
+ ? PlatformDependent.allocateDirectNoCleaner(capacity + alignment)
+ : ByteBuffer.allocateDirect(capacity + alignment);
+
+ PoolArena.DirectArena arena = new PoolArena.DirectArena(null, 0, 0, 9, 9, alignment);
+ int offset = arena.offsetCacheLine(bb);
+ long address = PlatformDependent.directBufferAddress(bb);
+
+ Assert.assertEquals(0, (offset + address) & (alignment - 1));
+ PlatformDependent.freeDirectWithCleaner(bb);
+ }
+ }
+
@Test
public final void testAllocationCounter() {
final PooledByteBufAllocator allocator = new PooledByteBufAllocator(
diff --git a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java
index 495bb765406a..39a613249387 100644
--- a/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java
+++ b/buffer/src/test/java/io/netty/buffer/PooledByteBufAllocatorTest.java
@@ -430,8 +430,14 @@ public void testConcurrentUsage() throws Throwable {
Thread.sleep(100);
}
} finally {
+ // First mark all AllocationThreads to complete their work and then wait until these are complete
+ // and rethrow if there was any error.
for (AllocationThread t : threads) {
- t.finish();
+ t.markAsFinished();
+ }
+
+ for (AllocationThread t: threads) {
+ t.joinAndCheckForError();
}
}
}
@@ -461,7 +467,7 @@ private static final class AllocationThread extends Thread {
private final ByteBufAllocator allocator;
private final AtomicReference