/* $Id$ */ package ibis.ipl.impl.nio; import ibis.io.DataOutputStream; import ibis.ipl.impl.ReceivePortIdentifier; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; 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 java.nio.channels.GatheringByteChannel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nio Accumulator. Writes data to java.nio.ByteBuffers. * * A NioAccumulator may not send any stream header or trailer data. */ public abstract class NioAccumulator extends DataOutputStream implements Config { 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 INITIAL_CONNECTIONS_SIZE = 8; private static Logger logger = LoggerFactory.getLogger(NioAccumulator.class); private SendBuffer buffer; private ByteBuffer bytes; private CharBuffer chars; private ShortBuffer shorts; private IntBuffer ints; private LongBuffer longs; private FloatBuffer floats; private DoubleBuffer doubles; NioAccumulatorConnection[] connections = new NioAccumulatorConnection[INITIAL_CONNECTIONS_SIZE]; int nrOfConnections = 0; NioSendPort port; long count = 0; protected NioAccumulator(NioSendPort port) { this.port = port; buffer = SendBuffer.get(); bytes = buffer.bytes; chars = buffer.chars; shorts = buffer.shorts; ints = buffer.ints; longs = buffer.longs; floats = buffer.floats; doubles = buffer.doubles; } public int bufferSize() { return PRIMITIVE_BUFFER_SIZE; } synchronized public long bytesWritten() { return count; } synchronized public void resetBytesWritten() { count = 0; } synchronized public long getAndResetBytesWritten() { long result = count; count = 0; return result; } synchronized NioAccumulatorConnection add(GatheringByteChannel channel, ReceivePortIdentifier receiver) throws IOException { if (nrOfConnections == connections.length) { NioAccumulatorConnection[] newConnections = new NioAccumulatorConnection[connections.length * 2]; for (int i = 0; i < connections.length; i++) { newConnections[i] = connections[i]; } connections = newConnections; } NioAccumulatorConnection c = newConnection(channel, receiver); connections[nrOfConnections] = c; nrOfConnections++; return c; } synchronized void removeConnection(ReceivePortIdentifier receiver) throws IOException { for (int i = 0; i < nrOfConnections; i++) { if (connections[i].target == receiver) { nrOfConnections--; connections[i] = connections[nrOfConnections]; connections[nrOfConnections] = null; return; } } throw new IOException("tried to remove non existing connections"); } synchronized private void send() throws IOException { if (buffer.isEmpty()) { return; } buffer.flip(); count += buffer.remaining(); if (doSend(buffer)) { // buffer was completely send, just clear it and use it again buffer.clear(); } else { // get a new buffer buffer = SendBuffer.get(); bytes = buffer.bytes; chars = buffer.chars; shorts = buffer.shorts; ints = buffer.ints; longs = buffer.longs; floats = buffer.floats; doubles = buffer.doubles; } } /* * makes sure all data given to the accumulator is send ,or at least copied. */ synchronized public void flush() throws IOException { send(); doFlush(); } synchronized public void close() throws IOException { if (!buffer.isEmpty()) { doSend(buffer); doFlush(); } else { SendBuffer.recycle(buffer); doFlush(); } buffer = null; } public void writeBoolean(boolean value) throws IOException { if (value) { writeByte((byte) 1); } else { writeByte((byte) 0); } } public void writeByte(byte value) throws IOException { if (logger.isDebugEnabled()) { logger.debug("writeByte(" + value + ")"); } try { bytes.put(value); } catch (BufferOverflowException e) { // buffer was full, send send(); // and try again bytes.put(value); } } public void write(int value) throws IOException { writeByte((byte) value); } public void writeChar(char value) throws IOException { try { chars.put(value); } catch (BufferOverflowException e) { send(); chars.put(value); } } public void writeShort(short value) throws IOException { try { shorts.put(value); } catch (BufferOverflowException e) { send(); shorts.put(value); } } public void writeInt(int value) throws IOException { try { ints.put(value); } catch (BufferOverflowException e) { send(); ints.put(value); } } public void writeLong(long value) throws IOException { try { longs.put(value); } catch (BufferOverflowException e) { send(); longs.put(value); } } public void writeFloat(float value) throws IOException { try { floats.put(value); } catch (BufferOverflowException e) { send(); floats.put(value); } } public void writeDouble(double value) throws IOException { try { doubles.put(value); } catch (BufferOverflowException e) { send(); doubles.put(value); } } public void writeArray(boolean[] array, int off, int len) throws IOException { for (int i = off; i < (off + len); i++) { if (array[i]) { writeByte((byte) 1); } else { writeByte((byte) 0); } } } public void writeArray(byte[] array, int off, int len) throws IOException { if (logger.isDebugEnabled()) { String message = "NioAccumulator.writeArray(byte[], off = " + off + " len = " + len + ") Contents: "; for (int i = off; i < (off + len); i++) { message = message + array[i] + " "; } if (logger.isDebugEnabled()) { logger.debug(message); } } try { bytes.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!bytes.hasRemaining()) { send(); } int size = Math.min(len, bytes.remaining()); bytes.put(array, off, size); off += size; len -= size; } } } public void write(byte[] b) throws IOException { write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { writeArray(b, off, len); } public void writeArray(char[] array, int off, int len) throws IOException { try { chars.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!chars.hasRemaining()) { send(); } int size = Math.min(len, chars.remaining()); chars.put(array, off, size); off += size; len -= size; } } } public void writeArray(short[] array, int off, int len) throws IOException { try { shorts.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!shorts.hasRemaining()) { send(); } int size = Math.min(len, shorts.remaining()); shorts.put(array, off, size); off += size; len -= size; } } } public void writeArray(int[] array, int off, int len) throws IOException { try { ints.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!ints.hasRemaining()) { send(); } int size = Math.min(len, ints.remaining()); ints.put(array, off, size); off += size; len -= size; } } } public void writeArray(long[] array, int off, int len) throws IOException { try { longs.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!longs.hasRemaining()) { send(); } int size = Math.min(len, longs.remaining()); longs.put(array, off, size); off += size; len -= size; } } } public void writeArray(float[] array, int off, int len) throws IOException { try { floats.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!floats.hasRemaining()) { send(); } int size = Math.min(len, floats.remaining()); floats.put(array, off, size); off += size; len -= size; } } } public void writeArray(double[] array, int off, int len) throws IOException { try { doubles.put(array, off, len); } catch (BufferOverflowException e) { // do this the hard way while (len > 0) { if (!doubles.hasRemaining()) { send(); } int size = Math.min(len, doubles.remaining()); doubles.put(array, off, size); off += size; len -= size; } } } public void writeByteBuffer(ByteBuffer b) throws IOException { try { bytes.put(b); } catch (BufferOverflowException e) { // do this the hard way int len = b.limit() - b.position(); while (len > 0) { if (!bytes.hasRemaining()) { send(); } int size = Math.min(len, bytes.remaining()); b.limit(b.position() + size); b.put(b); len -= size; } } } abstract NioAccumulatorConnection newConnection( GatheringByteChannel channel, ReceivePortIdentifier peer) throws IOException; /** * @return is the buffer already send or not. If it is not, the * implementation will recycle it when it's done with it. */ abstract boolean doSend(SendBuffer buffer) throws IOException; abstract void doFlush() throws IOException; }