3.3.4.6. 默认操作符(Default Operator)
关联此操作符的默认值被添加到输出消息中,除非该值已包含在编码的消息中。
下图显示了用默认操作符定义的字段的解码算法:
该算法的实现:
public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
Object decodedValue = null;
if (presenceMap.hasNextPresenceBit()) {
offset = dataTypeDecoder.decode(encodedData, offset, templateField);
decodedValue = getFieldValue();
} else {
decodedValue = getInitialValue();
}
if (decodedValue != null) {
message.addValue(tagId, decodedValue);
} else {
if (isMandatory) {
throw new RuntimeException("Mandatory field decoded value can not be NULL");
}
return offset;
}
public void reset() {
// Default fields never change their initial value so there is no need to reset them
}
public boolean isPMapBitExist() {
// Default fields require presence map bit
return true;
}
public boolean isFieldNullable() {
// If isMandatory false the Default field could has the null representation of field value and does not apear in the output fix message
return !isMandatory;
}
3.3.4.7. 差值操作符(Delta Operator)
差值操作符(可变增量操作符)将编码消息中的传入值与“前值”结合起来。因此,如果上次解码的值(前值)为150(对于整数字段),并且编码消息中的传入值为-5,则输出值为145。
下图说明了该运算符的算法。
该算法的代码示例:
public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
offset = dataTypeDecoder.decode(encodedData, offset, templateField);
if (getFieldValue() != null) {
if (getPreviousValue() != null) {
calculateDeltaFieldValue();
Object fieldValue = getFieldValue();
message.addValue(tagId, fieldValue);
setPreviousValue(fieldValue);
} else {
throw new RuntimeException("Mandatory decoded field can not be NULL");
}
} else {
if (isMandatory) {
throw new RuntimeException("Mandatory field decoded value can not be NULL");
}
}
return offset;
}
public void reset() {
// This functionality delivers previous value from the undefinite status
Object prevValue = getInitialValue();
if (prevValue == null) {
prevValue = getFieldBaseValue();
}
setPreviousValue(prevValue);
}
public boolean isPMapBitExist() {
// Delta fields not require presence map bit. In any casy it should be in encoded message
return false;
}
public boolean isFieldNullable() {
// If isMandatory false the Delta field could has the null representation of field value and does not apear in the output fix message
return !isMandatory;
}
2.3.4.8. 递增操作符(Increment Operator)
如果该字段值出现在编码的输入消息中(其PMap位已设置),则将解码出来的值用作输出值。否则,递增操作符(固定增量操作符)使用“前值”并对其加1以产生输出值。为下一次操作,该输出值被更新为“前值”。
该运算符仅适用于整数。
下图演示了递增操作符解码算法:
该算法的实现:
public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
if (presenceMap.hasNextPresenceBit()) {
offset = dataTypeDecoder.decode(encodedData, offset, templateField);
Object decodedValue = getFieldValue();
if (decodedValue != null) {
message.addValue(tagId, decodedValue);
setPreviousValue(decodedValue);
isUndefinedPrevValue = false;
} else {
if (isMandatory) {
throw new RuntimeException("Mandatory field decoded value can not be NULL");
} else {
setPreviousValue(null);
}
}
} else {
if (getPreviousValue() != null) {
if (!isUndefinedPrevValue) {
incrementPrevFieldValue();
}
// add field into the message
message.addValue(tagId, getPreviousValue());
} else {
if (isMandatory) {
throw new RuntimeException("Mandatory field decoded value can not be NULL");
}
}
}
return offset;
}
public void reset() {
// The field delivers previous value from the undefinite status
setPreviousValue(getInitialValue());
isUndefinedPrevValue = true;
}
public boolean isPMapBitExist() {
// Increment fields require presence map bit
return true;
}
public boolean isFieldNullable() {
// If isMandatory false the Increment field could has the null representation of field value and does not apear in the output fix message
return !isMandatory;
}
3.3.4.9. 尾部操作符(Tail Operator)
该操作符指定要删除的字符数和要追加到“前值”的字符。也就是说,输出值是通过组合“前值”- ‘n
’个字符(其中’n
’是来自输入消息的int) +来自输入编码消息的解码字段值(尾值 - tail)获得的。
对于该操作符,尾值是可选的。
此操作符可应用于稍后定义的数据类型字符串(string)和字节向量(byte vector)。
这张图展示了尾部操作符的解码算法:
该算法的实现:
public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
if (presenceMap.hasNextPresenceBit()) {
offset = dataTypeDecoder.decode(encodedData, offset, templateField);
Object decodedValue = getFieldValue();
if (decodedValue != null) {
if (getPreviousValue() == null) {
Object prevValue = getInitialValue();
if (prevValue == null) {
prevValue = getFieldBaseValue();
}
setPreviousValue(prevValue);
}
calculateCombineValue();
decodedValue = getFieldValue();
message.addValue(tagId, decodedValue);
setPreviousValue(decodedValue);
} else {
if (isMandatory) {
logger.logError("Mandatory field decoded value can not be NULL");
} else {
setPreviousValue(null);
}
}
} else {
Object prevValue = getPreviousValue();
if (prevValue != null) {
message.addValue(tagId, prevValue);
} else {
if (isMandatory) {
logger.logError("Mandatory field decoded value can not be NULL");
}
}
}
return offset;
}
public void reset() {
// The field delivers previous value from the undefinite status
setPreviousValue(getInitialValue());
}
public boolean isPMapBitExist() {
// ~~ Tail fields require presence map bit
return true;
}
public boolean isFieldNullable() {
// If isMandatory false the Delta field could has the null representation of field value and does not apear in the output fix message
return !isMandatory;
}
3.3.5. 序列(Sequence)
如前所述,FAST消息模板由模板单元组成。模板单元要么是模板字段,要么是序列。
序列是一组字段,包括:
- 如果后续有此序列的实例,则表示其数目的序列长度(length)。
- 字段组(field group)重复“序列长度”次,每个都从自己的PMap开始。
序列的解码算法如下:
随着“序列”概念的引入,我们需要扩展字段解码算法,如图所示。
下面的例子展示了" sequence "的TemplateUnit
接口的一个例子实现:
private TemplateField sequenceLength;
private TemplateUnit[] templateFields;
public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
offset = sequenceLength.decode(buffer, offset, presenceMap, message);
if (sequenceLength.getFieldValue() != null) {
int groupsCount = sequenceLength.getFieldValue();
if (groupsCount > 0) {
message.createFieldsSequence(sequenceLength.getTagId(), groupsCount);
for (int iter = 0; iter < groupsCount; iter++) {
presenceMap = new PresenceMap();
if (isPMapRequired) { // 注意此段代码
presenceMap.init(buffer, offset);
offset = presenceMap.getLastPMapBytePosition() + 1;
}
for (int i = 1; i < templateFields.length; i++) {
offset = templateFields[i].decode(buffer, offset, presenceMap, message);
}
}
}
} else {
if (isMandatory) {
throw new RuntimeException("A mandatory sequence is missing from the stream.");
}
}
return offset;
}
public void reset() {
for (int i = 1; i < templateFields.length; i++) {
templateFields[i].reset();
}
}
注意:
在上面的代码中,如果序列中的任何模板字段需要PMap位,则布尔变量isPMapRequired为true。根据FAST,如果不需要PMap位(即,所有字段将始终存在于已编码的消息中或强制常量),则字段组之前将没有PMap,因此将使用空PMap。
方法isPMapRequired()可以用以下代码片段实现:
isPMapRequired = false;
for (TemplateField field : templateFields) {
if (field.isPMapBitExist()) {
isPMapRequired = true;
break;
}
}
序列解码示例:
让我们考虑一个具有以下模板的小消息
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1" xmlns:scp="http://www.fixprotocol.org/ns/fast/scp/1.1">
<template name="Market Data Incremental" id="35">
<string name="MsgType" id="35">
<constant value="X"/>
</string>
<sequence name="MDIncGrp">
<length name="NoMDEntries" id="268">
<constant/>
</length>
<string name="TradingSessionID" id="336">
<default value="2" />
</string>
<uInt32 name="MDUpdateAction" id="279">
<copy/>
</uInt32>
</sequence>
</template>
</templates>
我们输入的FAST消息: 0xC0 0xA3 0x81 0xA0 0x80 = 11000000 10100011 10000001 10100000 10000000
下表显示了此消息的完整解码:
收集我们的字段后,输出消息将是:35=X|268=1|336=2|279=0