Implement internal memory access methods of CompositeByteBuf correctly

Motivation:

When a CompositeByteBuf is empty (i.e. has no component), its internal
memory access operations do not always behave as expected.

Modifications:

Check if the nunmber of components is zero. If so, return an empty
array or an empty NIO buffer, etc.

Result:

More robustness
This commit is contained in:
Trustin Lee 2014-12-30 15:52:57 +09:00
parent a666acce6d
commit 155c0e2f36

View File

@ -39,13 +39,13 @@ import java.util.ListIterator;
*/ */
public class CompositeByteBuf extends AbstractReferenceCountedByteBuf { public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
private static final ByteBuffer EMPTY_NIO_BUFFER = Unpooled.EMPTY_BUFFER.nioBuffer();
private final ResourceLeak leak; private final ResourceLeak leak;
private final ByteBufAllocator alloc; private final ByteBufAllocator alloc;
private final boolean direct; private final boolean direct;
private final List<Component> components = new ArrayList<Component>(); private final List<Component> components = new ArrayList<Component>();
private final int maxNumComponents; private final int maxNumComponents;
private static final ByteBuffer FULL_BYTEBUFFER = (ByteBuffer) ByteBuffer.allocate(1).position(1);
private static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocateDirect(0);
private boolean freed; private boolean freed;
@ -444,50 +444,71 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
@Override @Override
public boolean hasArray() { public boolean hasArray() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return true;
case 1:
return components.get(0).buf.hasArray(); return components.get(0).buf.hasArray();
default:
return false;
} }
return false;
} }
@Override @Override
public byte[] array() { public byte[] array() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return EmptyArrays.EMPTY_BYTES;
case 1:
return components.get(0).buf.array(); return components.get(0).buf.array();
default:
throw new UnsupportedOperationException();
} }
throw new UnsupportedOperationException();
} }
@Override @Override
public int arrayOffset() { public int arrayOffset() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return 0;
case 1:
return components.get(0).buf.arrayOffset(); return components.get(0).buf.arrayOffset();
default:
throw new UnsupportedOperationException();
} }
throw new UnsupportedOperationException();
} }
@Override @Override
public boolean hasMemoryAddress() { public boolean hasMemoryAddress() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return Unpooled.EMPTY_BUFFER.hasMemoryAddress();
case 1:
return components.get(0).buf.hasMemoryAddress(); return components.get(0).buf.hasMemoryAddress();
default:
return false;
} }
return false;
} }
@Override @Override
public long memoryAddress() { public long memoryAddress() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return Unpooled.EMPTY_BUFFER.memoryAddress();
case 1:
return components.get(0).buf.memoryAddress(); return components.get(0).buf.memoryAddress();
default:
throw new UnsupportedOperationException();
} }
throw new UnsupportedOperationException();
} }
@Override @Override
public int capacity() { public int capacity() {
if (components.isEmpty()) { final int numComponents = components.size();
if (numComponents == 0) {
return 0; return 0;
} }
return components.get(components.size() - 1).endOffset; return components.get(numComponents - 1).endOffset;
} }
@Override @Override
@ -964,7 +985,7 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException {
checkIndex(index, length); checkIndex(index, length);
if (length == 0) { if (length == 0) {
return in.read(FULL_BYTEBUFFER); return in.read(EMPTY_NIO_BUFFER);
} }
int i = toComponentIndex(index); int i = toComponentIndex(index);
@ -1093,9 +1114,12 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
@Override @Override
public int nioBufferCount() { public int nioBufferCount() {
if (components.size() == 1) { switch (components.size()) {
case 0:
return 1;
case 1:
return components.get(0).buf.nioBufferCount(); return components.get(0).buf.nioBufferCount();
} else { default:
int count = 0; int count = 0;
int componentsCount = components.size(); int componentsCount = components.size();
for (int i = 0; i < componentsCount; i++) { for (int i = 0; i < componentsCount; i++) {
@ -1108,26 +1132,35 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
@Override @Override
public ByteBuffer internalNioBuffer(int index, int length) { public ByteBuffer internalNioBuffer(int index, int length) {
if (components.size() == 1) { switch (components.size()) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
return components.get(0).buf.internalNioBuffer(index, length); return components.get(0).buf.internalNioBuffer(index, length);
default:
throw new UnsupportedOperationException();
} }
throw new UnsupportedOperationException();
} }
@Override @Override
public ByteBuffer nioBuffer(int index, int length) { public ByteBuffer nioBuffer(int index, int length) {
if (components.size() == 1) { checkIndex(index, length);
switch (components.size()) {
case 0:
return EMPTY_NIO_BUFFER;
case 1:
ByteBuf buf = components.get(0).buf; ByteBuf buf = components.get(0).buf;
if (buf.nioBufferCount() == 1) { if (buf.nioBufferCount() == 1) {
return components.get(0).buf.nioBuffer(index, length); return components.get(0).buf.nioBuffer(index, length);
} }
} }
ByteBuffer merged = ByteBuffer.allocate(length).order(order()); ByteBuffer merged = ByteBuffer.allocate(length).order(order());
ByteBuffer[] buffers = nioBuffers(index, length); ByteBuffer[] buffers = nioBuffers(index, length);
//noinspection ForLoopReplaceableByForEach for (ByteBuffer buf: buffers) {
for (int i = 0; i < buffers.length; i++) { merged.put(buf);
merged.put(buffers[i]);
} }
merged.flip(); merged.flip();
@ -1138,7 +1171,7 @@ public class CompositeByteBuf extends AbstractReferenceCountedByteBuf {
public ByteBuffer[] nioBuffers(int index, int length) { public ByteBuffer[] nioBuffers(int index, int length) {
checkIndex(index, length); checkIndex(index, length);
if (length == 0) { if (length == 0) {
return new ByteBuffer[] { EMPTY_BYTEBUFFER }; return new ByteBuffer[] { EMPTY_NIO_BUFFER };
} }
List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(components.size()); List<ByteBuffer> buffers = new ArrayList<ByteBuffer>(components.size());