package edu.washington.cs.oneswarm.f2f.servicesharing;
import org.gudy.azureus2.core3.util.DirectByteBuffer;
import org.gudy.azureus2.core3.util.DirectByteBufferPool;
import com.aelitis.azureus.core.peermanager.messaging.MessageException;
import edu.washington.cs.oneswarm.f2f.messaging.OSF2FChannelDataMsg;
public class OSF2FServiceDataMsg extends OSF2FChannelDataMsg {
/**
* Service Message Header:
* [version_][control_][subchannel________]
* [sequence number_______________________]
* [options____________________________...]
* [data_______________________________...]
* Control byte holds [length*4 ack syn rst reserved]
* When the ack bit is set, 'sequence number' + all data words
* are interpreted as acknowledgments.
*/
private final byte version;
private byte control = 0;
private final int[] options;
private final short subchannel;
private final int sequenceNumber;
// private final byte[] options;
private DirectByteBuffer serviceHeader;
private static final byte VERSION_NUM = 42;
private static final byte ss = 1;
// with no options: 1 word channel, 2 word header.
public static final int BASE_LENGTH = 12;
public OSF2FServiceDataMsg(byte _version, int channelID, int sequenceNumber, short subchannel,
int[] options, DirectByteBuffer data) {
super(_version, channelID, data);
this.version = VERSION_NUM;
this.subchannel = subchannel;
this.options = options;
this.sequenceNumber = sequenceNumber;
}
private OSF2FServiceDataMsg(byte _version, int channelID, int sequenceNumber, short subchannel,
int[] options, DirectByteBuffer data, byte control) {
this(_version, channelID, sequenceNumber, subchannel, options, data);
this.control = control;
}
static OSF2FServiceDataMsg acknowledge(byte _version, int channelID, short subchannel,
int[] acknowledgements, boolean datagram) {
int payloadSize = acknowledgements.length - 1;
DirectByteBuffer data = null;
if (payloadSize > 0) {
data = DirectByteBufferPool.getBuffer(ss, 4 * payloadSize);
for (int i = 0; i < payloadSize; i++) {
data.putInt(ss, acknowledgements[i + 1]);
}
data.flip(ss);
}
OSF2FServiceDataMsg msg = new OSF2FServiceDataMsg(_version, channelID, acknowledgements[0],
subchannel, new int[0], data, (byte) 8);
msg.setDatagram(datagram);
return msg;
}
@Override
public String getDescription() {
DirectByteBuffer payload = this.getPayload();
return "ServiceDataMessage[channel = " + this.getChannelId() + "." + this.subchannel
+ ", data = " + (payload == null ? "null" : payload.remaining(ss)) + ", flags="
+ (this.isAck() ? "ACK " : "") + (this.isSyn() ? "SYN " : "")
+ (this.isRst() ? "RST " : "") + (this.isDatagram() ? "UDP " : "");
}
@Override
public DirectByteBuffer[] getData() {
DirectByteBuffer[] channelmsg = super.getData();
DirectByteBuffer[] fullmsg;
if (channelmsg[1] == null) {
fullmsg = new DirectByteBuffer[2];
} else {
fullmsg = new DirectByteBuffer[3];
fullmsg[2] = channelmsg[1];
}
if (serviceHeader == null) {
int length = 2 + options.length;
serviceHeader = DirectByteBufferPool.getBuffer(DirectByteBuffer.AL_MSG, 4 * length);
serviceHeader.put(DirectByteBuffer.SS_MSG, version);
byte control = this.control;
control += length << 4;
serviceHeader.put(DirectByteBuffer.SS_MSG, control);
serviceHeader.putShort(DirectByteBuffer.SS_MSG, subchannel);
serviceHeader.putInt(DirectByteBuffer.SS_MSG, sequenceNumber);
for (int option : options) {
serviceHeader.putInt(DirectByteBuffer.SS_MSG, option);
}
serviceHeader.flip(DirectByteBuffer.SS_MSG);
// System.err.println(String.format(
// "OUT: %d, CONTROL: %d, WORDS: %d, WINDOW: %d, NUM:%d, REMAINING: %d",
// version,
// control, length, window, sequenceNumber,
// getPayload().remaining(SS_MSG)));
}
fullmsg[0] = channelmsg[0];
fullmsg[1] = serviceHeader;
return fullmsg;
}
@Override
public void destroy() {
super.destroy();
if (serviceHeader != null) {
serviceHeader.returnToPool();
}
}
public int getSequenceNumber() {
return sequenceNumber;
}
public short getSubchannel() {
return subchannel;
}
public static OSF2FServiceDataMsg fromChannelMessage(OSF2FChannelDataMsg msg)
throws MessageException {
if (msg instanceof OSF2FServiceDataMsg) {
throw new MessageException("message already OSF2FServiceDataMsg!");
}
checkIfServiceMessage(msg);
DirectByteBuffer payload = msg.transferPayload();
byte version = payload.get(SS_MSG);
byte control = payload.get(SS_MSG);
int words = (control & 0xf0) >> 4;
short subchannel = payload.getShort(SS_MSG);
words--;
int num = payload.getInt(SS_MSG);
words--;
// System.err.println(String.format(
// "VERSION: %d, CONTROL: %d, WORDS: %d, WINDOW: %d, NUM:%d REMAINING: %d",
// version, control, words, subchannel, num,
// payload.remaining(SS_MSG)));
int[] options = new int[words];
for (int i = 0; i < words; i++) {
options[i] = payload.getInt(SS_MSG);
}
return new OSF2FServiceDataMsg(version, msg.getChannelId(), num, subchannel, options,
payload,
(byte) (control & 0x0f));
}
private static void checkIfServiceMessage(OSF2FChannelDataMsg msg) throws MessageException {
DirectByteBuffer payload = msg.getPayload();
if (payload.remaining(SS_MSG) < 8) {
throw new MessageException("Not a Service Message - no Service Header");
}
int oldPos = payload.position(SS_MSG);
byte version = payload.get(SS_MSG);
payload.position(SS_MSG, oldPos);
if (version != VERSION_NUM) {
throw new MessageException("Incorrect Service Version Number.");
}
}
@Override
public int getMessageSize() {
return super.getMessageSize() + 4 * (2 + options.length);
}
public boolean isAck() {
return (this.control & 8) == 8;
}
public boolean isSyn() {
return (this.control & 4) == 4;
}
public void setControlFlag(int flag) {
if (flag < 0 || flag > 15) {
return;
}
this.control |= flag;
}
public boolean isRst() {
return (this.control & 2) == 2;
}
}