Specialized EMPTY_BUFFER class

EmptyChannelBuffer never writes its indices,
which avoids contention when the EMPTY_BUFFER
singleton is used concurrently.
This commit is contained in:
Daniel Norberg 2013-12-04 18:30:29 -05:00 committed by Norman Maurer
parent a30be3e73e
commit fb9aa5fa5c
4 changed files with 1186 additions and 1 deletions

View File

@ -100,7 +100,7 @@ public final class ChannelBuffers {
/**
* A buffer whose capacity is {@code 0}.
*/
public static final ChannelBuffer EMPTY_BUFFER = new BigEndianHeapChannelBuffer(0);
public static final ChannelBuffer EMPTY_BUFFER = new EmptyChannelBuffer();
private static final char[] HEXDUMP_TABLE = new char[256 * 4];

View File

@ -0,0 +1,216 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.buffer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
/**
* An immutable empty buffer implementation. Typically used as a singleton via
* {@link ChannelBuffers#EMPTY_BUFFER} and returned by {@link ChannelBuffers#buffer(int)} etc when
* an empty buffer is requested.
*
* <p>Note: For backwards compatibility, this class extends {@link BigEndianHeapChannelBuffer}.
* However, it never makes any writes to the reader and writer indices, which avoids contention
* when the singleton instance is used concurrently.
*/
public class EmptyChannelBuffer extends BigEndianHeapChannelBuffer {
private static final byte[] BUFFER = {};
EmptyChannelBuffer() {
super(BUFFER);
}
@Override
public void clear() {
}
@Override
public void readerIndex(int readerIndex) {
if (readerIndex != 0) {
throw new IndexOutOfBoundsException("Invalid readerIndex: "
+ readerIndex + " - Maximum is 0");
}
}
@Override
public void writerIndex(int writerIndex) {
if (writerIndex != 0) {
throw new IndexOutOfBoundsException("Invalid writerIndex: "
+ writerIndex + " - Maximum is 0");
}
}
@Override
public void setIndex(int readerIndex, int writerIndex) {
if (writerIndex != 0 || readerIndex != 0) {
throw new IndexOutOfBoundsException("Invalid writerIndex: "
+ writerIndex + " - Maximum is " + readerIndex + " or "
+ capacity());
}
}
@Override
public void markReaderIndex() {
}
@Override
public void resetReaderIndex() {
}
@Override
public void markWriterIndex() {
}
@Override
public void resetWriterIndex() {
}
@Override
public void discardReadBytes() {
}
@Override
public ChannelBuffer readBytes(int length) {
checkReadableBytes(length);
return this;
}
@Override
public ChannelBuffer readSlice(int length) {
checkReadableBytes(length);
return this;
}
@Override
public void readBytes(byte[] dst, int dstIndex, int length) {
checkReadableBytes(length);
}
@Override
public void readBytes(byte[] dst) {
checkReadableBytes(dst.length);
}
@Override
public void readBytes(ChannelBuffer dst) {
checkReadableBytes(dst.writableBytes());
}
@Override
public void readBytes(ChannelBuffer dst, int length) {
checkReadableBytes(length);
}
@Override
public void readBytes(ChannelBuffer dst, int dstIndex, int length) {
checkReadableBytes(length);
}
@Override
public void readBytes(ByteBuffer dst) {
checkReadableBytes(dst.remaining());
}
@Override
public int readBytes(GatheringByteChannel out, int length) throws IOException {
checkReadableBytes(length);
return 0;
}
@Override
public void readBytes(OutputStream out, int length) throws IOException {
checkReadableBytes(length);
}
@Override
public void skipBytes(int length) {
checkReadableBytes(length);
}
@Override
public void writeBytes(byte[] src, int srcIndex, int length) {
checkWritableBytes(length);
}
@Override
public void writeBytes(ChannelBuffer src, int length) {
checkWritableBytes(length);
}
@Override
public void writeBytes(ChannelBuffer src, int srcIndex, int length) {
checkWritableBytes(length);
}
@Override
public void writeBytes(ByteBuffer src) {
checkWritableBytes(src.remaining());
}
@Override
public int writeBytes(InputStream in, int length) throws IOException {
checkWritableBytes(length);
return 0;
}
@Override
public int writeBytes(ScatteringByteChannel in, int length) throws IOException {
checkWritableBytes(length);
return 0;
}
@Override
public void writeZero(int length) {
checkWritableBytes(length);
}
/**
* Throws an {@link IndexOutOfBoundsException} the length is not 0.
*/
private void checkWritableBytes(int length) {
if (length == 0) {
return;
}
if (length > 0) {
throw new IndexOutOfBoundsException("Writable bytes exceeded - Need "
+ length + ", maximum is " + 0);
} else {
throw new IndexOutOfBoundsException("length < 0");
}
}
/**
* Throws an {@link IndexOutOfBoundsException} the length is not 0.
*/
protected void checkReadableBytes(int length) {
if (length == 0) {
return;
}
if (length > 0) {
throw new IndexOutOfBoundsException("Not enough readable bytes - Need "
+ length + ", maximum is " + readableBytes());
} else {
throw new IndexOutOfBoundsException("length < 0");
}
}
}

View File

@ -440,4 +440,9 @@ public class ChannelBuffersTest {
String hexDump2 = hexDump(buffer2);
assertEquals(hexDump, hexDump2);
}
@Test
public void testEmptyBuffer() {
assertTrue(EMPTY_BUFFER instanceof EmptyChannelBuffer);
}
}

View File

@ -0,0 +1,964 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jboss.netty.buffer;
import org.jboss.netty.util.CharsetUtil;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.theories.Theory;
import org.junit.experimental.theories.suppliers.TestedOn;
import org.junit.rules.ExpectedException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel;
import static java.nio.ByteOrder.BIG_ENDIAN;
import static java.util.Arrays.asList;
import static org.hamcrest.core.IsNot.not;
import static org.jboss.netty.buffer.ChannelBuffers.*;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeThat;
public class EmptyChannelBufferTest {
@Rule
public ExpectedException thrown = ExpectedException.none();
public static final ScatteringByteChannel SINGLE_BYTE_CHANNEL = new ScatteringByteChannel() {
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
dsts[0].put((byte) 0);
return 1;
}
public long read(ByteBuffer[] dsts) throws IOException {
dsts[0].put((byte) 0);
return 1;
}
public int read(ByteBuffer dst) throws IOException {
dst.put((byte) 0);
return 1;
}
public boolean isOpen() {
return true;
}
public void close() throws IOException {
}
};
private static final ScatteringByteChannel EMPTY_BYTE_CHANNEL = new ScatteringByteChannel() {
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
return 0;
}
public long read(ByteBuffer[] dsts) throws IOException {
return 0;
}
public int read(ByteBuffer dst) throws IOException {
return 0;
}
public boolean isOpen() {
return true;
}
public void close() throws IOException {
}
};
private static final GatheringByteChannel BYTE_CHANNEL_SINK = new GatheringByteChannel() {
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
int n = 0;
for (ByteBuffer src : srcs) {
n += write(src);
}
return n;
}
public long write(ByteBuffer[] srcs) throws IOException {
return write(srcs, 0, 0);
}
public int write(ByteBuffer src) throws IOException {
int n = src.remaining();
src.get(new byte[src.remaining()]);
return n;
}
public boolean isOpen() {
return true;
}
public void close() throws IOException {
}
};
public static final byte[] SINGLE_BYTE = new byte[]{0};
public static final ChannelBufferIndexFinder POSITIVE_INDEX_FINDER = new ChannelBufferIndexFinder() {
public boolean find(ChannelBuffer buffer, int guessedIndex) {
return true;
}
};
public static final ChannelBuffer UNWRITTEN_BUFFER = buffer(1);
final ChannelBuffer b = EMPTY_BUFFER;
@Test
public void testDiscardReadBytes() {
b.discardReadBytes();
}
@Test
public void testClear() {
b.clear();
}
@Test
public void testWriteBytes() throws IOException {
b.writeBytes(new byte[]{});
b.writeBytes(new byte[]{}, 0, 0);
b.writeBytes(ByteBuffer.wrap(new byte[]{}));
b.writeBytes(buffer(0));
b.writeBytes(buffer(0), 0);
b.writeBytes(buffer(0), 0, 0);
b.writeBytes(new ChannelBufferInputStream(buffer(0)), 0);
b.writeBytes(EMPTY_BYTE_CHANNEL, 0);
}
@Theory
public void testWriteBytesIndexOutOfBounds(@TestedOn(ints = {-1, 1}) int length) throws IOException {
try {
b.writeBytes(SINGLE_BYTE);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(SINGLE_BYTE, 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(ByteBuffer.wrap(SINGLE_BYTE));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(wrappedBuffer(SINGLE_BYTE));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(wrappedBuffer(SINGLE_BYTE), length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(wrappedBuffer(SINGLE_BYTE), 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(new ChannelBufferInputStream(wrappedBuffer(SINGLE_BYTE)), length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(SINGLE_BYTE_CHANNEL, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeBytes(SINGLE_BYTE, 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testWriteZero() {
b.writeZero(0);
}
@Theory
public void testWriteZeroOutOfBounds(@TestedOn(ints = {-1, 1}) int length) {
thrown.expect(IndexOutOfBoundsException.class);
b.writeZero(length);
}
@Theory
public void testSetZero(@TestedOn(ints = {-1, 1}) int offset) {
b.setZero(0, 0);
b.setZero(1, 0);
b.setZero(-1, 0);
}
@Theory
public void testSetZeroOutOfBounds(@TestedOn(ints = {-1, 1}) int offset,
@TestedOn(ints = {-1, 1}) int length) {
thrown.expect(IndexOutOfBoundsException.class);
b.setZero(offset, length);
}
@Test
public void testSetBytes() {
b.setBytes(0, wrappedBuffer(new byte[]{}), 0);
b.setBytes(0, wrappedBuffer(new byte[]{}), 0, 0);
b.setBytes(0, ByteBuffer.wrap(new byte[]{}));
b.setBytes(0, buffer(0));
b.setBytes(0, buffer(0));
b.setBytes(0, buffer(0), 0);
b.setBytes(0, buffer(0), 0, 0);
}
@Theory
public void testSetBytesIndexOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
try {
b.setBytes(offset, SINGLE_BYTE);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setBytes(offset, SINGLE_BYTE, 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setBytes(offset, wrappedBuffer(SINGLE_BYTE), length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setBytes(offset, wrappedBuffer(SINGLE_BYTE), 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setBytes(offset, ByteBuffer.wrap(SINGLE_BYTE));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Theory
public void testSetPrimitives(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int value) {
try {
b.setByte(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setChar(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setShort(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setMedium(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setInt(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setLong(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setFloat(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setDouble(offset, value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Theory
public void testGetPrimitives(@TestedOn(ints = {-1, 0, 1}) int offset) {
try {
b.getByte(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getChar(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getShort(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getMedium(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getInt(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getLong(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getUnsignedByte(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getUnsignedShort(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getUnsignedMedium(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getUnsignedInt(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getDouble(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getFloat(offset);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testCopy() {
assertEquals(EMPTY_BUFFER, b.copy());
assertEquals(EMPTY_BUFFER, b.copy(0, 0));
}
@Theory
void testCopyOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
thrown.expect(IndexOutOfBoundsException.class);
b.copy(offset, length);
}
@Test
public void testReadPrimitives() {
try {
b.readByte();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readChar();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readDouble();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readFloat();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readInt();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readLong();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readMedium();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readShort();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readUnsignedByte();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readUnsignedInt();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readUnsignedMedium();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readUnsignedShort();
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Theory
public void testWritePrimitives(@TestedOn(ints = {-1, 0, 1}) int value) {
try {
b.writeByte(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeChar(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeDouble(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeFloat(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeInt(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeLong(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeMedium(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.writeShort(value);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testReadBytes() throws IOException {
assertEquals(EMPTY_BUFFER, b.readBytes(0));
b.readBytes(new byte[]{});
b.readBytes(new byte[]{}, 0, 0);
b.readBytes(ByteBuffer.allocate(0));
b.readBytes(buffer(0));
b.readBytes(buffer(0), 0);
b.readBytes(buffer(0), 0, 0);
b.readBytes(BYTE_CHANNEL_SINK, 0);
}
@Theory
public void testReadBytesOutOfBounds(@TestedOn(ints = {-1, 1}) int length) throws IOException {
try {
b.readBytes(length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(new byte[1]);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(new byte[1], 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(ByteBuffer.allocate(1));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(buffer(1));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(buffer(1), length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(buffer(1), 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readBytes(BYTE_CHANNEL_SINK, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testIndexes() {
assertEquals(0, b.readerIndex());
assertEquals(0, b.writerIndex());
assertEquals(0, b.readableBytes());
assertEquals(0, b.writableBytes());
assertFalse(b.readable());
assertFalse(b.writable());
b.markReaderIndex();
b.markWriterIndex();
b.resetReaderIndex();
b.resetWriterIndex();
b.writerIndex(0);
b.readerIndex(0);
b.setIndex(0, 0);
}
@Theory
public void testIndexesOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int writer,
@TestedOn(ints = {-1, 0, 1}) int reader) {
assumeThat(asList(writer, reader), not(asList(0, 0)));
try {
b.writerIndex(writer);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.readerIndex(reader);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.setIndex(reader, writer);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Theory
public void testIndexOf(@TestedOn(ints = {-1, 0, 1}) int from,
@TestedOn(ints = {-1, 0, 1}) int to,
@TestedOn(ints = {-1, 0, 1}) int value) {
assertEquals(-1, b.indexOf(from, to, (byte) value));
}
@Test
public void testEquals() {
assertTrue(b.equals(EMPTY_BUFFER));
assertTrue(b.equals(ChannelBuffers.buffer(1)));
assertFalse(b.equals(wrappedBuffer(SINGLE_BYTE)));
}
@Test
public void testFactory() {
assertEquals(HeapChannelBufferFactory.getInstance(BIG_ENDIAN), b.factory());
}
@Test
public void testEnsureWritableBytes() {
b.ensureWritableBytes(0);
try {
b.ensureWritableBytes(1);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testSlice() {
assertEquals(EMPTY_BUFFER, b.slice());
assertEquals(EMPTY_BUFFER, b.slice(0, 0));
}
@Theory
public void testSliceOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
thrown.expect(IndexOutOfBoundsException.class);
b.slice(offset, length);
}
@Test
public void testReadSlice() {
assertEquals(EMPTY_BUFFER, b.readSlice(0));
}
@Theory
public void testReadSliceOutOfBounds(@TestedOn(ints = {-1, 1}) int length) {
thrown.expect(IndexOutOfBoundsException.class);
b.readSlice(length);
}
@Test
public void testDuplicate() {
assertEquals(EMPTY_BUFFER, b.duplicate());
}
@Theory
public void testBytesBefore(@TestedOn(ints = {-1, 0, 1}) int value) {
assertEquals(-1, b.bytesBefore((byte) value));
assertEquals(-1, b.bytesBefore(POSITIVE_INDEX_FINDER));
assertEquals(-1, b.bytesBefore(0, (byte) value));
assertEquals(-1, b.bytesBefore(0, (byte) value));
assertEquals(-1, b.bytesBefore(0, 0, (byte) value));
assertEquals(-1, b.bytesBefore(0, 0, POSITIVE_INDEX_FINDER));
}
@Theory
public void testBytesBeforeOutOfBounds1(@TestedOn(ints = {-1, 1}) int offset) {
thrown.expect(IndexOutOfBoundsException.class);
b.bytesBefore(offset, POSITIVE_INDEX_FINDER);
}
@Theory
public void testBytesBeforeOutOfBounds2(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
thrown.expect(IndexOutOfBoundsException.class);
b.bytesBefore(offset, length, POSITIVE_INDEX_FINDER);
}
@Test
public void testOrder() {
assertEquals(ChannelBuffers.BIG_ENDIAN, b.order());
}
@Test
public void testArray() {
assertEquals(0, b.array().length);
assertEquals(0, b.arrayOffset());
assertTrue(b.hasArray());
}
@Test
public void testCapacity() {
assertEquals(0, b.capacity());
}
@Test
public void testCompareTo() {
assertEquals(-1, b.compareTo(wrappedBuffer(SINGLE_BYTE)));
assertEquals(0, b.compareTo(buffer(0)));
assertEquals(0, b.compareTo(new BigEndianHeapChannelBuffer(0)));
assertEquals(0, b.compareTo(new BigEndianHeapChannelBuffer(1)));
}
@Test
public void testIsDirect() {
assertFalse(b.isDirect());
}
@Test
public void testToByteBuffer() {
assertEquals(ByteBuffer.allocate(0), b.toByteBuffer());
assertEquals(ByteBuffer.allocate(0), b.toByteBuffer(0, 0));
}
@Theory
public void testToByteBufferOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
thrown.expect(IndexOutOfBoundsException.class);
b.toByteBuffer(offset, length);
}
@Test
public void testToByteBuffers() {
assertArrayEquals(new ByteBuffer[]{ByteBuffer.allocate(0)}, b.toByteBuffers());
assertArrayEquals(new ByteBuffer[]{ByteBuffer.allocate(0)}, b.toByteBuffers(0, 0));
}
@Theory
public void testToByteBuffersOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) {
assumeThat(asList(offset, length), not(asList(0, 0)));
thrown.expect(IndexOutOfBoundsException.class);
b.toByteBuffers(offset, length);
}
@Test
public void testGetBytes() throws IOException {
b.getBytes(0, new byte[0]);
b.getBytes(0, new byte[0], 0, 0);
b.getBytes(0, ByteBuffer.allocate(0));
b.getBytes(0, buffer(0));
b.getBytes(0, buffer(0), 0);
b.getBytes(0, buffer(0), 0, 0);
b.getBytes(0, BYTE_CHANNEL_SINK, 0);
b.getBytes(0, new ChannelBufferOutputStream(buffer(0)), 0);
}
@Theory
public void testGetBytesOutOfBounds(@TestedOn(ints = {-1, 0, 1}) int offset,
@TestedOn(ints = {-1, 0, 1}) int length) throws IOException {
assumeThat(asList(offset, length), not(asList(0, 0)));
try {
b.getBytes(offset, new byte[1]);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, new byte[1], 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, ByteBuffer.allocate(1));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, buffer(1));
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, buffer(1), 0);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, buffer(1), 0, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, BYTE_CHANNEL_SINK, length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
try {
b.getBytes(offset, new ChannelBufferOutputStream(buffer(length)), length);
fail();
} catch (IndexOutOfBoundsException e) {
// Expected
}
}
@Test
public void testSkipBytes() {
b.skipBytes(0);
}
@Theory
public void testSkipBytesOutOfBounds(@TestedOn(ints = {-1, 1}) int length) {
thrown.expect(IndexOutOfBoundsException.class);
b.skipBytes(length);
}
@Test
public void testHashCode() {
assertEquals(ChannelBuffers.hashCode(UNWRITTEN_BUFFER), b.hashCode());
}
@Test
public void testToString() {
b.toString();
assertEquals("", b.toString(CharsetUtil.UTF_8));
assertEquals("", b.toString(0, 0, CharsetUtil.UTF_8));
}
}