package thaw.fcp;
import thaw.core.Logger;
import thaw.core.ThawThread;
import thaw.core.ThawRunnable;
/**
* Only used by FCPConnection. Except special situation, you shouldn't have to use it directly.
* Currently only used for output. (shouldn't be really usefull for input).
* Some data are sent each 'INTERVAL' (in ms).
*/
public class FCPBufferedStream implements ThawRunnable {
private FCPConnection connection;
private int maxUploadSpeed;
private byte outputBuffer[];
public final static int OUTPUT_BUFFER_SIZE = 102400;
public final static int INTERVAL = 200;
private int waiting = 0; /* amount of data stored in the buffer */
private int readCursor = 0; /* indicates where the nex read will be */
private int writeCursor = 0; /* indicates where the next write will be */
private Thread tractopelle = null;
private boolean running = true;
private int packetSize = 0;
public FCPBufferedStream(final FCPConnection connection,
final int maxUploadSpeed) {
this.connection = connection;
this.maxUploadSpeed = maxUploadSpeed;
if(maxUploadSpeed >= 0) {
outputBuffer = new byte[FCPBufferedStream.OUTPUT_BUFFER_SIZE];
packetSize = (maxUploadSpeed * 1024) / (1000/FCPBufferedStream.INTERVAL);
}
}
/**
* Add to the buffer. Can block if buffer is full !
* Never send more than OUTPUT_BUFFER_SIZE.
*/
public synchronized boolean write(final byte[] data) {
if(maxUploadSpeed == -1)
return connection.realRawWrite(data);
while(waiting + data.length > FCPBufferedStream.OUTPUT_BUFFER_SIZE) {
sleep(FCPBufferedStream.INTERVAL);
}
waiting += data.length;
for(int i = 0 ; i < data.length ; i++) {
outputBuffer[writeCursor] = data[i];
writeCursor++;
if(writeCursor >= FCPBufferedStream.OUTPUT_BUFFER_SIZE)
writeCursor = 0;
}
return true;
}
/**
* @see #write(byte[])
*/
public boolean write(final String data) {
try {
return this.write(data.getBytes("UTF-8"));
} catch(final java.io.UnsupportedEncodingException e) {
Logger.error(this, "UNSUPPORTED ENCODING EXCEPTION : UTF-8");
return this.write(data.getBytes());
}
}
/**
* extract from the buffer
*/
private boolean readOutputBuffer(final byte[] data) {
for(int i = 0; i < data.length ; i++) {
data[i] = outputBuffer[readCursor];
readCursor++;
if(readCursor >= FCPBufferedStream.OUTPUT_BUFFER_SIZE)
readCursor = 0;
}
waiting -= data.length;
return true;
}
/**
* wait for the buffer being empty.
*/
public void flush() {
while(waiting > 0) {
sleep(FCPBufferedStream.INTERVAL);
}
}
public void run() {
byte[] data;
while(running) { /* Wild and freeeeeee */
if(waiting > 0) {
int to_read = packetSize;
if(waiting < to_read)
to_read = waiting;
data = new byte[to_read];
readOutputBuffer(data);
connection.realRawWrite(data);
}
sleep(FCPBufferedStream.INTERVAL);
}
}
public void stop() {
running = false;
}
/**
* Start the thread sending data from the buffer to the OutputStream (socket).
*/
public boolean startSender() {
running = true;
if(maxUploadSpeed < 0) {
Logger.notice(this, "startSender(): No upload limit. Not needed");
return false;
}
if(tractopelle == null) {
tractopelle = new ThawThread(this, "Upload limiter", this);
tractopelle.start();
return true;
} else {
Logger.notice(this, "startSender(): Already started");
return false;
}
}
public boolean stopSender() {
running = false;
tractopelle = null;
return true;
}
public boolean isOutputBufferEmpty() {
return (waiting == 0);
}
public boolean isOutputBufferFull() {
return ((maxUploadSpeed < 0) || (waiting >= (FCPBufferedStream.OUTPUT_BUFFER_SIZE-1)));
}
/**
* Just ignore the InterruptedException.
*/
private void sleep(final int ms) {
try {
Thread.sleep(ms);
} catch(final java.lang.InterruptedException e) {
/* just iggnnnnnooored */
}
}
}