diff --git a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java index 40844020dd..3fd10aacfd 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java @@ -117,4 +117,9 @@ public abstract class AbstractDerivedByteBuf extends AbstractByteBuf { public ByteBuffer nioBuffer(int index, int length) { return unwrap().nioBuffer(index, length); } + + @Override + public boolean isContiguous() { + return unwrap().isContiguous(); + } } diff --git a/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java index b106f57887..8cf8295a00 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractPooledDerivedByteBuf.java @@ -123,6 +123,11 @@ abstract class AbstractPooledDerivedByteBuf extends AbstractReferenceCountedByte return unwrap().hasMemoryAddress(); } + @Override + public boolean isContiguous() { + return unwrap().isContiguous(); + } + @Override public final int nioBufferCount() { return unwrap().nioBufferCount(); diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index 20f2bb1c66..850c85a21e 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -2383,6 +2383,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable { */ public abstract long memoryAddress(); + /** + * Returns {@code true} if this {@link ByteBuf} implementation is backed by a single memory region. + * Composite buffer implementations must return false even if they currently hold ≤ 1 components. + * For buffers that return {@code true}, it's guaranteed that a successful call to {@link #discardReadBytes()} + * will increase the value of {@link #maxFastWritableBytes()} by the current {@code readerIndex}. + *

+ * This method will return {@code false} by default, and a {@code false} return value does not necessarily + * mean that the implementation is composite or that it is not backed by a single memory region. + */ + public boolean isContiguous() { + return false; + } + /** * Decodes this buffer's readable bytes into a string with the specified * character set name. This method is identical to diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java index 76549583d0..2505c667de 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java @@ -962,6 +962,11 @@ public final class EmptyByteBuf extends ByteBuf { } } + @Override + public boolean isContiguous() { + return true; + } + @Override public String toString(Charset charset) { return ""; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 8af5fac8a9..9e33c0c594 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -214,6 +214,11 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { return new ByteBuffer[] { nioBuffer(index, length) }; } + @Override + public final boolean isContiguous() { + return true; + } + @Override public final int getBytes(int index, GatheringByteChannel out, int length) throws IOException { return out.write(duplicateInternalNioBuffer(index, length)); diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java index b5271d8d8e..bc79fb0d4c 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java @@ -454,6 +454,11 @@ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf { return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length); } + @Override + public final boolean isContiguous() { + return true; + } + @Override public boolean hasArray() { return buffer.hasArray(); diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java index 556dcfadfe..18b84b6341 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java @@ -982,6 +982,11 @@ public class SwappedByteBuf extends ByteBuf { return buf.hasMemoryAddress(); } + @Override + public boolean isContiguous() { + return buf.isContiguous(); + } + @Override public long memoryAddress() { return buf.memoryAddress(); diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index daad1408e7..829089be10 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -600,6 +600,11 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { return new ByteBuffer[] { nioBuffer(index, length) }; } + @Override + public final boolean isContiguous() { + return true; + } + @Override public ByteBuf copy(int index, int length) { ensureAccessible(); diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java index 0db078b665..d84cf4e5b7 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java @@ -320,6 +320,11 @@ public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf { return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length); } + @Override + public final boolean isContiguous() { + return true; + } + @Override public byte getByte(int index) { ensureAccessible(); diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java index ff602ab342..3a78b953e5 100644 --- a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java @@ -52,6 +52,11 @@ class WrappedByteBuf extends ByteBuf { return buf.hasMemoryAddress(); } + @Override + public boolean isContiguous() { + return buf.isContiguous(); + } + @Override public final long memoryAddress() { return buf.memoryAddress(); diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index a0fdd2843f..3158743b3a 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -109,6 +109,13 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { return false; } + @Test + public void testIsContiguous() { + ByteBuf buf = newBuffer(4); + assertFalse(buf.isContiguous()); + buf.release(); + } + /** * Tests the "getBufferFor" method */ diff --git a/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java index 90f1bf4584..c7f5815c2a 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractPooledByteBufTest.java @@ -118,4 +118,11 @@ public abstract class AbstractPooledByteBufTest extends AbstractByteBufTest { assertEquals(buffer.writableBytes(), buffer.maxFastWritableBytes()); buffer.release(); } + + @Test + public void testIsContiguous() { + ByteBuf buf = newBuffer(4); + assertTrue(buf.isContiguous()); + buf.release(); + } } diff --git a/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java b/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java index 6943c2f58e..873129503c 100644 --- a/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/BigEndianDirectByteBufTest.java @@ -19,6 +19,8 @@ import static org.junit.Assert.*; import java.nio.ByteOrder; +import org.junit.Test; + /** * Tests big-endian direct channel buffers */ @@ -35,4 +37,11 @@ public class BigEndianDirectByteBufTest extends AbstractByteBufTest { protected ByteBuf newDirectBuffer(int length, int maxCapacity) { return new UnpooledDirectByteBuf(UnpooledByteBufAllocator.DEFAULT, length, maxCapacity); } + + @Test + public void testIsContiguous() { + ByteBuf buf = newBuffer(4); + assertTrue(buf.isContiguous()); + buf.release(); + } } diff --git a/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java b/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java index 2e88657b66..f96b9227ea 100644 --- a/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/DuplicatedByteBufTest.java @@ -33,6 +33,13 @@ public class DuplicatedByteBufTest extends AbstractByteBufTest { return buffer; } + @Test + public void testIsContiguous() { + ByteBuf buf = newBuffer(4); + assertEquals(buf.unwrap().isContiguous(), buf.isContiguous()); + buf.release(); + } + @Test(expected = NullPointerException.class) public void shouldNotAllowNullInConstructor() { new DuplicatedByteBuf(null); diff --git a/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java b/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java index c2d5764cb1..0d9fc254c5 100644 --- a/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/EmptyByteBufTest.java @@ -22,6 +22,12 @@ import static org.junit.Assert.*; public class EmptyByteBufTest { + @Test + public void testIsContiguous() { + EmptyByteBuf empty = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT); + assertTrue(empty.isContiguous()); + } + @Test public void testIsWritable() { EmptyByteBuf empty = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT); diff --git a/buffer/src/test/java/io/netty/buffer/ReadOnlyDirectByteBufferBufTest.java b/buffer/src/test/java/io/netty/buffer/ReadOnlyDirectByteBufferBufTest.java index f92d576682..1e88bdae29 100644 --- a/buffer/src/test/java/io/netty/buffer/ReadOnlyDirectByteBufferBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/ReadOnlyDirectByteBufferBufTest.java @@ -37,6 +37,13 @@ public class ReadOnlyDirectByteBufferBufTest { return ByteBuffer.allocateDirect(size); } + @Test + public void testIsContiguous() { + ByteBuf buf = buffer(allocate(4).asReadOnlyBuffer()); + Assert.assertTrue(buf.isContiguous()); + buf.release(); + } + @Test(expected = IllegalArgumentException.class) public void testConstructWithWritable() { buffer(allocate(1)); diff --git a/buffer/src/test/java/io/netty/buffer/SlicedByteBufTest.java b/buffer/src/test/java/io/netty/buffer/SlicedByteBufTest.java index 4079571feb..11e9f687cd 100644 --- a/buffer/src/test/java/io/netty/buffer/SlicedByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/SlicedByteBufTest.java @@ -47,6 +47,13 @@ public class SlicedByteBufTest extends AbstractByteBufTest { return buffer.slice(offset, length); } + @Test + public void testIsContiguous() { + ByteBuf buf = newBuffer(4); + assertEquals(buf.unwrap().isContiguous(), buf.isContiguous()); + buf.release(); + } + @Test(expected = NullPointerException.class) public void shouldNotAllowNullInConstructor() { new SlicedByteBuf(null, 0, 0);