/* $Id$ */
package ibis.ipl.impl.nio;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class SendBuffer implements Config {
// primitives are send in order of size, largest first
private static final int HEADER = 0;
private static final int LONGS = 1;
private static final int DOUBLES = 2;
private static final int INTS = 3;
private static final int FLOATS = 4;
private static final int SHORTS = 5;
private static final int CHARS = 6;
private static final int BYTES = 7;
private static final int PADDING = 8;
private static final int NR_OF_BUFFERS = 9;
/**
* The header contains 1 byte for the byte order, one byte indicating the
* length of the padding at the end of the packet (in bytes), and 7 shorts
* (14 bytes) for the number of each primitive send (in bytes!)
*/
private static final int SIZEOF_HEADER = 16;
private static final int SIZEOF_PADDING = 8;
public static final int SIZEOF_BYTE = 1;
public static final int SIZEOF_CHAR = 2;
public static final int SIZEOF_SHORT = 2;
public static final int SIZEOF_INT = 4;
public static final int SIZEOF_LONG = 8;
public static final int SIZEOF_FLOAT = 4;
public static final int SIZEOF_DOUBLE = 8;
static final int BUFFER_CACHE_SIZE = 128;
static SendBuffer[] cache = new SendBuffer[BUFFER_CACHE_SIZE];
static int cacheSize = 0;
private static Logger logger = LoggerFactory.getLogger(SendBuffer.class);
/**
* Static method to get a sendbuffer out of the cache
*/
synchronized static SendBuffer get() {
if (cacheSize > 0) {
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: got empty buffer from cache");
}
cacheSize--;
cache[cacheSize].clear();
return cache[cacheSize];
}
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: got new empty buffer");
}
return new SendBuffer();
}
/**
* static method to put a buffer in the cache
*/
synchronized static void recycle(SendBuffer buffer) {
if (buffer.parent == null) {
if (buffer.copies != 0) {
// throw new Error("tried to recycle buffer with children!");
return;
}
if (cacheSize >= BUFFER_CACHE_SIZE) {
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: cache full"
+ " apon recycling buffer, throwing away");
}
return;
}
cache[cacheSize] = buffer;
cacheSize++;
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: recycled buffer");
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: recycling child buffer");
}
buffer.parent.copies--;
if (buffer.parent.copies == 0) {
if (cacheSize >= BUFFER_CACHE_SIZE) {
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: cache full"
+ " apon recycling parent of child buffer,"
+ " throwing away");
}
return;
}
cache[cacheSize] = buffer.parent;
cacheSize++;
if (logger.isDebugEnabled()) {
logger.debug("SendBuffer: recycled parent buffer");
}
}
}
}
/**
* create copies of a buffer, records how may copies are made so far
*/
synchronized static SendBuffer[] replicate(SendBuffer original, int copies) {
SendBuffer[] result = new SendBuffer[copies];
for (int i = 0; i < copies; i++) {
result[i] = new SendBuffer(original);
}
original.copies += copies;
return result;
}
// number of copies that exist of this buffer
private int copies = 0;
// original buffer this buffer is a copy of (if applicable)
SendBuffer parent = null;
private static long nextSequenceNr = 0;
ShortBuffer header;
LongBuffer longs;
DoubleBuffer doubles;
IntBuffer ints;
FloatBuffer floats;
ShortBuffer shorts;
CharBuffer chars;
ByteBuffer bytes;
ByteBuffer[] byteBuffers;
/**
* Used to keep track of a buffer. Recycling a buffer will reset its
* sequence number.
*/
long sequenceNr;
SendBuffer() {
ByteOrder order = ByteOrder.nativeOrder();
byteBuffers = new ByteBuffer[NR_OF_BUFFERS];
byteBuffers[HEADER] = ByteBuffer.allocateDirect(SIZEOF_HEADER).order(
order);
byteBuffers[PADDING] = ByteBuffer.allocateDirect(SIZEOF_PADDING).order(
order);
// put the byte order in the first byte of the header
if (order == ByteOrder.BIG_ENDIAN) {
byteBuffers[HEADER].put(0, (byte) 1);
} else {
byteBuffers[HEADER].put(0, (byte) 0);
}
for (int i = 1; i < (NR_OF_BUFFERS - 1); i++) {
byteBuffers[i] = ByteBuffer.allocateDirect(PRIMITIVE_BUFFER_SIZE)
.order(order);
}
header = byteBuffers[HEADER].asShortBuffer();
longs = byteBuffers[LONGS].asLongBuffer();
doubles = byteBuffers[DOUBLES].asDoubleBuffer();
ints = byteBuffers[INTS].asIntBuffer();
floats = byteBuffers[FLOATS].asFloatBuffer();
shorts = byteBuffers[SHORTS].asShortBuffer();
chars = byteBuffers[CHARS].asCharBuffer();
bytes = byteBuffers[BYTES].duplicate();
clear();
}
/**
* Copy constructor. Acutally only copies byteBuffers;
*/
SendBuffer(SendBuffer parent) {
this.parent = parent;
byteBuffers = new ByteBuffer[NR_OF_BUFFERS];
for (int i = 0; i < NR_OF_BUFFERS; i++) {
byteBuffers[i] = parent.byteBuffers[i].duplicate();
}
}
/**
*
*
* /** Resets a buffer as though it's a newly created buffer. Sets the
* sequencenr to a new value
*/
void clear() {
// header.clear();
longs.clear();
doubles.clear();
ints.clear();
floats.clear();
shorts.clear();
chars.clear();
bytes.clear();
parent = null;
copies = 0;
// FIXME: this isn't thread safe
sequenceNr = nextSequenceNr;
nextSequenceNr++;
}
/**
* Make a (partially) filled buffer ready for sending
*/
void flip() {
int paddingLength;
// fill header with the size of the primitive arrays (in bytes)
short[] headerArray = new short[NR_OF_BUFFERS - 1];
headerArray[LONGS] = (short) (longs.position() * SIZEOF_LONG);
headerArray[DOUBLES] = (short) (doubles.position() * SIZEOF_DOUBLE);
headerArray[INTS] = (short) (ints.position() * SIZEOF_INT);
headerArray[FLOATS] = (short) (floats.position() * SIZEOF_FLOAT);
headerArray[SHORTS] = (short) (shorts.position() * SIZEOF_SHORT);
headerArray[CHARS] = (short) (chars.position() * SIZEOF_CHAR);
headerArray[BYTES] = (short) (bytes.position() * SIZEOF_BYTE);
header.clear();
header.put(headerArray);
byteBuffers[HEADER].position(0);
byteBuffers[HEADER].limit(SIZEOF_HEADER);
byteBuffers[LONGS].position(0);
byteBuffers[LONGS].limit(headerArray[LONGS]);
byteBuffers[DOUBLES].position(0);
byteBuffers[DOUBLES].limit(headerArray[DOUBLES]);
byteBuffers[INTS].position(0);
byteBuffers[INTS].limit(headerArray[INTS]);
byteBuffers[FLOATS].position(0);
byteBuffers[FLOATS].limit(headerArray[FLOATS]);
byteBuffers[SHORTS].position(0);
byteBuffers[SHORTS].limit(headerArray[SHORTS]);
byteBuffers[CHARS].position(0);
byteBuffers[CHARS].limit(headerArray[CHARS]);
byteBuffers[BYTES].position(0);
byteBuffers[BYTES].limit(headerArray[BYTES]);
// add padding to make the total nr of bytes send a multiple of eight
// find out length of padding we need
int totalLength = SIZEOF_HEADER + headerArray[LONGS]
+ headerArray[DOUBLES] + headerArray[INTS]
+ headerArray[FLOATS] + headerArray[SHORTS]
+ headerArray[CHARS] + headerArray[BYTES];
paddingLength = (8 - (totalLength % 8));
byteBuffers[PADDING].position(0).limit(paddingLength);
// put a byte in the header indicating the length of the paddding
byteBuffers[HEADER].put(1, (byte) paddingLength);
if (logger.isDebugEnabled()) {
logger.debug("flipping buffer, sending: l[" + longs.position()
+ "] d[" + doubles.position() + "] i[" + ints.position()
+ "] f[" + floats.position() + "] s[" + shorts.position()
+ "] c[" + chars.position() + "] b[" + bytes.position()
+ "] total size: " + remaining() + " padding size: "
+ paddingLength);
}
}
/**
* set a mark on all Byte Buffers
*/
void mark() {
for (int i = 0; i < byteBuffers.length; i++) {
byteBuffers[i].mark();
}
}
/**
* reset all Byte Buffers
*/
void reset() {
for (int i = 0; i < byteBuffers.length; i++) {
byteBuffers[i].reset();
}
}
/**
* returns the number of remaining bytes in the bytebuffers
*/
long remaining() {
long result = 0;
for (int i = 0; i < byteBuffers.length; i++) {
result += byteBuffers[i].remaining();
}
return result;
}
/**
* returns if this buffer is empty (before flipping)
*/
boolean isEmpty() {
return ((longs.position() == 0) && (doubles.position() == 0)
&& (ints.position() == 0) && (floats.position() == 0)
&& (shorts.position() == 0) && (chars.position() == 0) && (bytes
.position() == 0));
}
/**
* Returns if this buffer has any data remaining in it. Only works _after_
* it has been flipped!
*/
boolean hasRemaining() {
return byteBuffers[PADDING].hasRemaining();
}
}