package com.srbenoit.buffer;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.SocketChannel;
import com.srbenoit.pool.AbstractPoolObject;
/**
* A buffer that holds byte data and that can be stored in a Pool
. Each buffer
* contains a pointer to a subsequent buffer (null for the last buffer in a chain), allowing
* buffers to be assembled into a linked list.
*/
public class PooledByteBuffer extends AbstractPooledBuffer {
/** the next buffer in a linked list */
private transient PooledByteBuffer next;
/**
* Constructs a new PooledByteBuffer
.
*
* @param capacity the capacity of a buffer, in bytes
*/
public PooledByteBuffer(final int capacity) {
super(ByteBuffer.allocate(capacity));
this.next = null;
}
/**
* Gets the buffer.
*
* @return the buffer
*/
@Override public ByteBuffer getBuffer() {
return (ByteBuffer) super.getBuffer();
}
/**
* Allocates a new buffer of a specified capacity;
*
* @param capacity the capacity of the new buffer
*/
@Override public void newBuffer(final int capacity) {
setBuffer(ByteBuffer.allocate(capacity));
}
/**
* Sets the next buffer in the linked list.
*
* @param nextBuffer the next buffer
*/
public void setNext(final PooledByteBuffer nextBuffer) {
synchronized (this.synch) {
this.next = nextBuffer;
}
}
/**
* Gets the next buffer in the linked list.
*
* @return the next buffer
*/
public PooledByteBuffer getNext() {
synchronized (this.synch) {
return this.next;
}
}
/**
* Reads the byte at the backing buffer's current position, and then increments that position.
*
* @return the byte at the backing buffer's current position
* @throws BufferUnderflowException if the backing buffer's current position is not smaller
* than its limit
*/
public byte get() throws BufferUnderflowException {
synchronized (this.synch) {
return getBuffer().get();
}
}
/**
* Writes the given byte into the backing buffer at the current position, and then increments
* that position.
*
* @param data the byte to be written
* @throws BufferOverflowException if the backing buffer's current position is not smaller
* than its limit
* @throws ReadOnlyBufferException if the backing buffer is read-only
*/
public void put(final byte data) throws BufferOverflowException, ReadOnlyBufferException {
synchronized (this.synch) {
getBuffer().put(data);
}
}
/**
* Reads the byte from the backing buffer at the given index.
*
* @param index the index from which the byte will be read
* @return the byte at the given index
* @throws IndexOutOfBoundsException if index
is negative or not smaller than
* the backing buffer's limit
*/
public byte get(final int index) {
synchronized (this.synch) {
return getBuffer().get(index);
}
}
/**
* Writes the given byte into the backing buffer at the given index.
*
* @param index the index at which the byte will be written
* @param data the byte value to be written
* @throws IndexOutOfBoundsException if index
is negative or not smaller than
* the buffer's limit
* @throws ReadOnlyBufferException if this buffer is read-only
*/
public void put(final int index, final byte data) throws IndexOutOfBoundsException,
ReadOnlyBufferException {
synchronized (this.synch) {
getBuffer().put(index, data);
}
}
/**
* Transfers bytes from the backing buffer into the given destination array. If there are fewer
* bytes remaining in the buffer than are required to satisfy the request, that is, if
* length > remaining()
, then no bytes are transferred and a
* BufferUnderflowException
is thrown.
*
*
Otherwise, this method copies length
bytes from this buffer into the given
* array, starting at the current position of this buffer and at the given offset in the array.
* The position of this buffer is then incremented by length
.
*
* @param dst the array into which bytes are to be written
* @param offset the offset within the array of the first byte to be written; must be
* non-negative and no larger than dst.length
* @param length the maximum number of bytes to be written to the given array; must be
* non-negative and no larger than dst.length - offset
* @throws BufferUnderflowException if there are fewer than length
bytes
* remaining in the backing buffer
* @throws IndexOutOfBoundsException if the preconditions on the offset
and
* length
parameters do not hold
*/
public void get(final byte[] dst, final int offset, final int length) {
synchronized (this.synch) {
getBuffer().get(dst, offset, length);
}
}
/**
* Transfers bytes from the backing buffer into the given destination array.
*
* @param dst the array into which bytes are to be written
* @throws BufferUnderflowException if there are fewer than length
bytes
* remaining in the backing buffer
*/
public void get(final byte[] dst) {
synchronized (this.synch) {
getBuffer().get(dst);
}
}
/**
* Transfers bytes into the backing buffer from the given source array. If there are more bytes
* to be copied from the array than remain in this buffer, that is, if length >
* remaining()
, then no bytes are transferred and a BufferOverflowException
* is thrown.
*
*
Otherwise, this method copies length
bytes from the given array into the
* backing buffer, starting at the given offset in the array and at the current position of the
* backing buffer. The position of this buffer is then incremented by length
.
*
* @param src the array from which bytes are to be read
* @param offset the offset within the array of the first byte to be read; must be
* non-negative and no larger than array.length
* @param length the number of bytes to be read from the given array; must be non-negative
* and no larger than array.length - offset
* @throws BufferOverflowException if there is insufficient space in this buffer
* @throws IndexOutOfBoundsException if the preconditions on the offset
and
* length
parameters do not hold
* @throws ReadOnlyBufferException if this buffer is read-only
*/
public void put(final byte[] src, final int offset, final int length)
throws BufferOverflowException, IndexOutOfBoundsException, ReadOnlyBufferException {
synchronized (this.synch) {
getBuffer().put(src, offset, length);
}
}
/**
* This method transfers the entire content of the given source byte array into the backing
* buffer.
*
* @param src the array from which bytes are to be read
* @throws BufferOverflowException if there is insufficient space in the backing buffer
* @throws ReadOnlyBufferException if this buffer is read-only
*/
public void put(final byte[] src) {
synchronized (this.synch) {
getBuffer().put(src);
}
}
/**
* Reads data into the byte buffer from a socket channel.
*
* @param channel the channel from which to read
* @return the number of bytes read, possibly zero, or -1 if the channel has reached
* end-of-stream
* @throws IOException if there was an I/O error while reading data
* @throws NotYetConnectedException if the channel is not yet connected
*/
public int readFrom(final SocketChannel channel) throws IOException, NotYetConnectedException {
synchronized (this.synch) {
return channel.read(getBuffer());
}
}
/**
* Writes data from the byte buffer to a socket channel.
*
* @param channel the channel from which to read
* @return the number of bytes written, possibly zero
* @throws IOException if there was an I/O error while reading data
* @throws NotYetConnectedException if the channel is not yet connected
*/
public int writeTo(final SocketChannel channel) throws IOException, NotYetConnectedException {
synchronized (this.synch) {
return channel.write(getBuffer());
}
}
/**
* Creates a copy of the object, but with an independent backing buffer - used to create new
* objects when the pool is empty. This is more efficient than creating a new object.
*
* @return the copy
*/
@Override public AbstractPoolObject copy() {
PooledByteBuffer copy;
synchronized (this.synch) {
try {
copy = (PooledByteBuffer) super.clone();
copy.newBuffer(capacity());
} catch (CloneNotSupportedException e) {
copy = new PooledByteBuffer(capacity());
}
}
return copy;
}
/**
* Resets this buffer to a virgin state (used before the buffer is returned to a pool).
*/
@Override public void toVirginState() {
synchronized (this.synch) {
this.next = null;
super.toVirginState();
}
}
/**
* Informs the pool object that it is being released for garbage collection and should free any
* internal resources. The object may not be reused after this method is called.
*/
@Override public void die() {
synchronized (this.synch) {
this.next = null;
super.die();
}
}
}