Introduce ByteBuf#isContiguous() method (#9735)
Motivation There's currently no way to determine whether an arbitrary ByteBuf behaves internally like a "singluar" buffer or a composite one, and this can be important to know when making decisions about how to manipulate it in an efficient way. An example of this is the ByteBuf#discardReadBytes() method which increases the writable bytes for a contiguous buffer (by readerIndex) but does not for a composite one. Unfortunately !(buf instanceof CompositeByteBuf) is not reliable, since for example this will be true in the case of a sliced CompositeByteBuf or some third-party composite implementation. isContiguous was chosen over isComposite since we want to assume "not contiguous" in the unknown/default case - the doc will it clear that false does not imply composite. Modifications - Add ByteBuf#isContiguous() which returns true by default - Override the "concrete" ByteBuf impls to return true and ensure wrapped/derived impls delegate it appropriately - Include some basic unit tests Result Better assumptions/decisions possible when manipulating arbitrary ByteBufs, for example when combining/cumulating them.
This commit is contained in:
parent
d13ac5290a
commit
625981a296
@ -117,4 +117,9 @@ public abstract class AbstractDerivedByteBuf extends AbstractByteBuf {
|
|||||||
public ByteBuffer nioBuffer(int index, int length) {
|
public ByteBuffer nioBuffer(int index, int length) {
|
||||||
return unwrap().nioBuffer(index, length);
|
return unwrap().nioBuffer(index, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return unwrap().isContiguous();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,11 @@ abstract class AbstractPooledDerivedByteBuf extends AbstractReferenceCountedByte
|
|||||||
return unwrap().hasMemoryAddress();
|
return unwrap().hasMemoryAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return unwrap().isContiguous();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int nioBufferCount() {
|
public final int nioBufferCount() {
|
||||||
return unwrap().nioBufferCount();
|
return unwrap().nioBufferCount();
|
||||||
|
@ -2338,6 +2338,19 @@ public abstract class ByteBuf implements ReferenceCounted, Comparable<ByteBuf> {
|
|||||||
*/
|
*/
|
||||||
public abstract long memoryAddress();
|
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}.
|
||||||
|
* <p>
|
||||||
|
* 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 <i>not</i> backed by a single memory region.
|
||||||
|
*/
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes this buffer's readable bytes into a string with the specified
|
* Decodes this buffer's readable bytes into a string with the specified
|
||||||
* character set name. This method is identical to
|
* character set name. This method is identical to
|
||||||
|
@ -939,6 +939,11 @@ public final class EmptyByteBuf extends ByteBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Charset charset) {
|
public String toString(Charset charset) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -213,6 +213,11 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
|
|||||||
return new ByteBuffer[] { nioBuffer(index, length) };
|
return new ByteBuffer[] { nioBuffer(index, length) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isContiguous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
|
public final int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
|
||||||
return out.write(duplicateInternalNioBuffer(index, length));
|
return out.write(duplicateInternalNioBuffer(index, length));
|
||||||
|
@ -453,6 +453,11 @@ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf {
|
|||||||
return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
|
return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isContiguous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasArray() {
|
public boolean hasArray() {
|
||||||
return buffer.hasArray();
|
return buffer.hasArray();
|
||||||
|
@ -956,6 +956,11 @@ public class SwappedByteBuf extends ByteBuf {
|
|||||||
return buf.hasMemoryAddress();
|
return buf.hasMemoryAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return buf.isContiguous();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long memoryAddress() {
|
public long memoryAddress() {
|
||||||
return buf.memoryAddress();
|
return buf.memoryAddress();
|
||||||
|
@ -595,6 +595,11 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
|
|||||||
return new ByteBuffer[] { nioBuffer(index, length) };
|
return new ByteBuffer[] { nioBuffer(index, length) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isContiguous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf copy(int index, int length) {
|
public ByteBuf copy(int index, int length) {
|
||||||
ensureAccessible();
|
ensureAccessible();
|
||||||
|
@ -320,6 +320,11 @@ public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
|
|||||||
return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
|
return (ByteBuffer) internalNioBuffer().clear().position(index).limit(index + length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isContiguous() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte getByte(int index) {
|
public byte getByte(int index) {
|
||||||
ensureAccessible();
|
ensureAccessible();
|
||||||
|
@ -52,6 +52,11 @@ class WrappedByteBuf extends ByteBuf {
|
|||||||
return buf.hasMemoryAddress();
|
return buf.hasMemoryAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isContiguous() {
|
||||||
|
return buf.isContiguous();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final long memoryAddress() {
|
public final long memoryAddress() {
|
||||||
return buf.memoryAddress();
|
return buf.memoryAddress();
|
||||||
|
@ -109,6 +109,13 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
ByteBuf buf = newBuffer(4);
|
||||||
|
assertFalse(buf.isContiguous());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the "getBufferFor" method
|
* Tests the "getBufferFor" method
|
||||||
*/
|
*/
|
||||||
|
@ -114,4 +114,11 @@ public abstract class AbstractPooledByteBufTest extends AbstractByteBufTest {
|
|||||||
assertEquals(buffer.writableBytes(), buffer.maxFastWritableBytes());
|
assertEquals(buffer.writableBytes(), buffer.maxFastWritableBytes());
|
||||||
buffer.release();
|
buffer.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
ByteBuf buf = newBuffer(4);
|
||||||
|
assertTrue(buf.isContiguous());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests big-endian direct channel buffers
|
* Tests big-endian direct channel buffers
|
||||||
*/
|
*/
|
||||||
@ -35,4 +37,11 @@ public class BigEndianDirectByteBufTest extends AbstractByteBufTest {
|
|||||||
protected ByteBuf newDirectBuffer(int length, int maxCapacity) {
|
protected ByteBuf newDirectBuffer(int length, int maxCapacity) {
|
||||||
return new UnpooledDirectByteBuf(UnpooledByteBufAllocator.DEFAULT, length, maxCapacity);
|
return new UnpooledDirectByteBuf(UnpooledByteBufAllocator.DEFAULT, length, maxCapacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
ByteBuf buf = newBuffer(4);
|
||||||
|
assertTrue(buf.isContiguous());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,13 @@ public class DuplicatedByteBufTest extends AbstractByteBufTest {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
ByteBuf buf = newBuffer(4);
|
||||||
|
assertEquals(buf.unwrap().isContiguous(), buf.isContiguous());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = NullPointerException.class)
|
@Test(expected = NullPointerException.class)
|
||||||
public void shouldNotAllowNullInConstructor() {
|
public void shouldNotAllowNullInConstructor() {
|
||||||
new DuplicatedByteBuf(null);
|
new DuplicatedByteBuf(null);
|
||||||
|
@ -22,6 +22,12 @@ import static org.junit.Assert.*;
|
|||||||
|
|
||||||
public class EmptyByteBufTest {
|
public class EmptyByteBufTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
EmptyByteBuf empty = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
assertTrue(empty.isContiguous());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsWritable() {
|
public void testIsWritable() {
|
||||||
EmptyByteBuf empty = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT);
|
EmptyByteBuf empty = new EmptyByteBuf(UnpooledByteBufAllocator.DEFAULT);
|
||||||
|
@ -37,6 +37,13 @@ public class ReadOnlyDirectByteBufferBufTest {
|
|||||||
return ByteBuffer.allocateDirect(size);
|
return ByteBuffer.allocateDirect(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsContiguous() {
|
||||||
|
ByteBuf buf = buffer(allocate(4).asReadOnlyBuffer());
|
||||||
|
Assert.assertTrue(buf.isContiguous());
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testConstructWithWritable() {
|
public void testConstructWithWritable() {
|
||||||
buffer(allocate(1));
|
buffer(allocate(1));
|
||||||
|
@ -47,6 +47,13 @@ public class SlicedByteBufTest extends AbstractByteBufTest {
|
|||||||
return buffer.slice(offset, length);
|
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)
|
@Test(expected = NullPointerException.class)
|
||||||
public void shouldNotAllowNullInConstructor() {
|
public void shouldNotAllowNullInConstructor() {
|
||||||
new SlicedByteBuf(null, 0, 0);
|
new SlicedByteBuf(null, 0, 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user