package io.craft.atom.protocol.rpc;
import io.craft.atom.protocol.AbstractProtocolDecoder;
import io.craft.atom.protocol.ProtocolDecoder;
import io.craft.atom.protocol.ProtocolException;
import io.craft.atom.protocol.rpc.api.SerializationRegistry;
import io.craft.atom.protocol.rpc.model.RpcBody;
import io.craft.atom.protocol.rpc.model.RpcHeader;
import io.craft.atom.protocol.rpc.model.RpcMessage;
import io.craft.atom.protocol.rpc.spi.Serialization;
import io.craft.atom.util.ByteUtil;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
/**
* A {@link ProtocolDecoder} which decodes bytes into {@code RpcMessage} object.
* <p>
* Not thread safe.
*
* @author mindwind
* @version 1.0, Jul 24, 2014
*/
public class RpcDecoder extends AbstractProtocolDecoder implements ProtocolDecoder<RpcMessage> {
private static final int MAGIC = 11;
private static final int HEADER_SIZE = 12;
private static final int VERSION = 13;
private static final int BIT_FLAG = 14;
private static final int STATUS_CODE = 15;
private static final int RESERVED = 16;
private static final int MESSAGE_ID = 17;
private static final int BODY_SIZE = 18;
private static final int BODY = 20;
private SerializationRegistry registry = SerializationRegistry.getInstance();
@Getter private RpcMessage rm ;
// ~ --------------------------------------------------------------------------------------------------------------
public RpcDecoder() {}
// ~ --------------------------------------------------------------------------------------------------------------
@Override
public void reset() {
super.reset();
rm = null;
}
@Override
public List<RpcMessage> decode(byte[] bytes) throws ProtocolException {
List<RpcMessage> msgs = new ArrayList<RpcMessage>();
adapt();
buf.append(bytes);
while (searchIndex < buf.length() || state == END) {
switch (state) {
case START : state4START() ; break;
case MAGIC : state4MAGIC() ; break;
case HEADER_SIZE: state4HEADER_SIZE(); break;
case VERSION : state4VERSION() ; break;
case BIT_FLAG : state4BIT_FLAG() ; break;
case STATUS_CODE: state4STATUS_CODE(); break;
case RESERVED : state4RESERVED() ; break;
case MESSAGE_ID : state4MESSAGE_ID() ; break;
case BODY_SIZE : state4BODY_SIZE() ; break;
case BODY : state4BODY() ; break;
case END : state4END(msgs) ; break;
default : throw new IllegalStateException("Invalid decoder state!");
}
}
return msgs;
}
private void state4END(List<RpcMessage> msgs) {
msgs.add(rm);
splitIndex = searchIndex;
rm = null;
state = START;
}
private void state4BODY() {
// need more bytes
int hs = rm.getHeader().getHeaderSize();
int bs = rm.getHeader().getBodySize();
if (buf.length() < hs + bs + splitIndex) { searchIndex = buf.length(); return; }
Serialization<RpcBody> deserializer = registry.lookup(rm.getHeader().getSt());
if (deserializer == null) throw new ProtocolException("No mapping `deserializer`!");
RpcBody rb = deserializer.deserialize(buf.buffer(), 20 + splitIndex);
rm.setBody(rb);
searchIndex = hs + bs + splitIndex;
state = END;
}
private void state4BODY_SIZE() {
// need more bytes
if (buf.length() < 20 + splitIndex) { searchIndex = buf.length(); return; }
int bs = ByteUtil.bytes2int(buf.buffer(), 16 + splitIndex);
rm.getHeader().setBodySize(bs);
state = BODY;
searchIndex = 20 + splitIndex;
}
private void state4MESSAGE_ID() {
// need more bytes
if (buf.length() < 16 + splitIndex) { searchIndex = buf.length(); return; }
long id = ByteUtil.bytes2long(buf.buffer(), 8 + splitIndex);
rm.getHeader().setId(id);
state = BODY_SIZE;
searchIndex = 16 + splitIndex;
}
private void state4RESERVED() {
// need more bytes
if (buf.length() < 8 + splitIndex) { searchIndex = buf.length(); return; }
rm.getHeader().setReserved(buf.byteAt(7 + splitIndex));
state = MESSAGE_ID;
searchIndex = 8 + splitIndex;
}
private void state4STATUS_CODE() {
// need more bytes
if (buf.length() < 7 + splitIndex) { searchIndex = buf.length(); return; }
rm.getHeader().setStatusCode(buf.byteAt(6 + splitIndex));
state = RESERVED;
searchIndex = 7 + splitIndex;
}
private void state4BIT_FLAG() {
// need more bytes
if (buf.length() < 6 + splitIndex) { searchIndex = buf.length(); return; }
rm.getHeader().setSt(buf.byteAt(5 + splitIndex));
rm.getHeader().setHb(buf.byteAt(5 + splitIndex));
rm.getHeader().setOw(buf.byteAt(5 + splitIndex));
rm.getHeader().setRp(buf.byteAt(5 + splitIndex));
state = STATUS_CODE;
searchIndex = 6 + splitIndex;
}
private void state4VERSION() {
// need more bytes
if (buf.length() < 5 + splitIndex) { searchIndex = buf.length(); return; }
rm.getHeader().setVersion(buf.byteAt(4 + splitIndex));
state = BIT_FLAG;
searchIndex = 5 + splitIndex;
}
private void state4HEADER_SIZE() {
// need more bytes
if (buf.length() < 4 + splitIndex) { searchIndex = buf.length(); return; }
short hs = ByteUtil.bytes2short(buf.buffer(), 2 + splitIndex);
rm.getHeader().setHeaderSize(hs);
state = VERSION;
searchIndex = 4 + splitIndex;
}
private void state4MAGIC() {
// need more bytes
if (buf.length() < 2 + splitIndex) { searchIndex = buf.length(); return; }
if (RpcHeader.MAGIC_0 == buf.byteAt(0 + splitIndex) && RpcHeader.MAGIC_1 == buf.byteAt(1 + splitIndex)) {
RpcHeader rh = new RpcHeader();
rm = new RpcMessage();
rm.setHeader(rh);
state = HEADER_SIZE;
searchIndex = 2 + splitIndex;
} else {
throw new ProtocolException("Invalid bytes format!");
}
}
private void state4START() {
if (buf.length() > 0 + splitIndex) {
state = MAGIC;
}
}
}