3.3.6. 数据类型
FAST为编码消息字段定义了几种数据类型。模板为每个模板字段声明一个数据类型。数据类型的选择控制了编码字段的二进制格式。
对于解码器,数据类型控制如下:
- 从编码的消息中提取字段值。
- 停止位规则。
- 空值表示和对可选字段的非空值的值调整。
在我们的代码中,我们将这些步骤结合起来,并将其视为一个过程。可用的数据类型有:
- 无符号整数
- 有符号整数
- 小数(Decimal)
- ASCII字符串
- 字节向量和Unicode字符串
下一节详细介绍上述数据类型,并描述解码器如何提取每种类型、停止位规则和空值表示。
3.3.6.1. 整数
整数表示为停止位编码的实体。
整数字段的停止位解码过程为:
- 通过停止位算法确定长度。
- 从每个字节中删除停止位。
- 组合7位字(不带停止位)以确定实际整数。
此外,可空字段继续此处理:
- 如果该值为零(0),则输出值为NULL。
- 如果值为正,则从该值中减去1作为输出值。
2.3.6.1.1. 无符号整数
使用FAST 7位二进制编码表示无符号整数。
解码过程的示例如下图所示
强制(mandatory)字段的解码代码示例:
public int processUInt32Decoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
// The primari value is only 0 as this is unsigned
long value = 0l;
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value = (value << 7) | (buffer[offset++]);
}
value = (value << 7) | (buffer[offset++] & Byte.MAX_VALUE);
fieldValue.setFieldValue(value);
return offset;
}
可空(nullable)字段的解码代码示例:
public int processUInt32Decoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
// The primari value is only 0 as this is unsigned
long value = 0l;
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value = (value << 7) | (buffer[offset++]);
}
value = (value << 7) | (buffer[offset++] & Byte.MAX_VALUE);
if (value == 0) {
fieldValue.setFieldValue(null);
} else {
fieldValue.setFieldValue(value - 1);
}
return offset;
}
2.3.6.1.2. 有符号整数
用于使用FAST 7位二进制编码表示有符号(+/-)整数。
下图演示了负数的有符号整数解码。
非空字段的解码代码示例:
public int processInt32Decoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
int value = 0;
if (PresenceMap.hasBitOnPosition(buffer[offset], 1)) {
value = 0xFFFFFFFF;
}
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value = (value << 7) | (buffer[offset++]);
}
value = (value << 7) | (buffer[offset++] & Byte.MAX_VALUE);
fieldValue.setFieldValue(value);
return offset;
}
可空字段的解码代码示例:
public int processInt32Decoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
int value = 0;
if (PresenceMap.hasBitOnPosition(buffer[offset], 1)) {
value = 0xFFFFFFFF;
}
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value = (value << 7) | (buffer[offset++]);
}
value = (value << 7) | (buffer[offset++] & Byte.MAX_VALUE);
if (value == 0) {
fieldValue.setFieldValue(null);
} else {
if (value > 0) {
value -= 1;
}
fieldValue.setFieldValue(value);
}
return offset;
}
3.3.6.2. 小数(Decimal)
在FAST中,小数(decimal)由两个有符号整数表示,即指数(exponent)和尾数(mantissa)。这与IEEE标准类似,但不相同。FAST变长编码不同于IEEE,后者具有固定大小的编码,包含一组字节数和其他特殊值,如无穷大。
小数类型的数值是通过从编码的消息中提取已编码的有符号整数指数(int32)和后面跟着的有符号整数尾数值(int64),并将尾数乘以指数的以10为基数的幂来获得的。
输出值=尾数* 10指数
例子:
例如,编码后的小数值: 0xFE 0x9 0xD2 = 11111110 00001001 11010010
。
下图显示了这个小数字段的解码过程。
使用这些值,我们计算完整的小数值。
decimalValue=尾数* 10指数= 1234 * 10 -2 = 12.34
注:
- 指数(exponent)的存在属性(presence =“optional” or “mandatory”)等于小数(decimal)字段的存在属性。尾数(mantissa)必须始终强制存在。因此,指数可以为空(nullable)或不可为空,尾数总是不可为空的。
- 如果指数不是NULL,则流中会跟随出现尾数。
- 如果模板字段是可选的(decimal is optional),那么该数据类型的字段可以有空值(NULL),并且不会被添加到输出消息中。这由指数中的NULL表示(在这种情况下,没有编码或解码尾数字段)。
下面是代码示例:
public int decode(byte[] buffer, int offset, PresenceMap presenceMap, OutputMessage message) {
offset = exponentField.decode(buffer, offset, presenceMap, message);
if (exponentField.getValue() != null) {
offset = mantissaField.decode(buffer, offset, presenceMap, message);
Object decimal = calculateDecimalValue(exponentField.getValue(),
mantissaField.getValue());
message.addValue(tagId, decimal);
}
return offset;
}
让我们更详细地考虑另一个小数字段解码的例子:
我们的FAST消息模板:
<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1" xmlns:scp="http://www.fixprotocol.org/ns/fast/scp/1.1">
<template name="HelloWorld" id="1">
<decimal name="MDEntryPx" id="270" presence="mandatory">
<exponent>
<constant value="-3"/>
</exponent>
<mantissa>
<copy/>
</mantissa>
</decimal>
</template>
</templates>
注意: 小数
decimal
字段的存在presence
属性是由模板指定的,它是必选的mandatory
。根据decimal
字段的定义,指数exponent
的presence
属性与完整decimal
字段的presence
属性相同,也是必须的。尾数mantissa
的存在属性总是强制的。
输入编码消息:
HEX format: 0xE0 0x81 0x03 0x3B 0xD5
Binary format: 11100000 10000001 00000011 00111011 11010101
此消息的解码过程如下:
在解码字段后,输出消息将是: 270=56.789<SOH>
3.3.6.3. ASCII字符串
ASCII字符串表示为停止位编码的实体。实体值被解释为一个7位ASCII字符序列。
这些字段的停止位解码执行如下:
- 使用停止位算法确定字符串值的字节数。
- 在最后一个字节中,将停止位值从“1”更改为“0”。
- 生成的字节数组是输出字符串值。
下面显示了这个解码过程的一个示例。
强制字符串字段的解码代码示例:
public int processStringDecoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
// Handle empty string
if(buffer[offset] == (byte)0x80) {
fieldValue.setFieldValue("");
return (offset+1);
}
// Extract the bytes from the buffer until a stop bit is encountered
StringBuffer value = new StringBuffer();
while(!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value.append((char)(buffer[offset++]));
}
// Extract the last byte and get rid of the stop bit
value.append((char)((buffer[offset++] & Byte.MAX_VALUE)));
fieldValue.setFieldValue(value);
return offset;
}
注意:
- 由
0x80
表示的字段值表示解码后的字符串值为空值 - " "。
可为空的字符串字段的解码代码示例:
public int processStringDecoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
// Handle null value
if(buffer[offset] == (byte)0x80) {
fieldValue.setFieldValue(null);
return (offset+1);
}
// Handle empty string value
if(buffer[offset] == 0x00 && buffer[offset+1] == (byte)0x80) {
fieldValue.setFieldValue("");
return (offset+2);
}
// Extract the bytes from the buffer until a stop bit is encountered
StringBuffer value = new StringBuffer();
while(!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
value.append((char)(buffer[offset++]));
}
// Extract the last byte and get rid of the stop bit
value.append((char)((buffer[offset++] & Byte.MAX_VALUE)));
fieldValue.setFieldValue(value);
return (offset);
}
注意:
- 由
0x80
表示的字段值表示NULL解码的字符串值(而不是上面的“”)。 - 用
0x00 0x80
的2个字节表示的字段值表示解码后的字符串值为空值 - " "。
3.3.6.4. 字节向量和Unicode字符串
字节向量(byte vector)和Unicode字符串字段表示为无符号整数的长度的前导,后面跟着前导指定的原始字节数。
数据部分中的每个字节有8个有效数据位。因此,数据部分没有进行停止位编码。
可为空的字段具有可为空值的长度前导。NULL大小的前导值表示该字段是NULL值。
下图演示了一个unicode-8字符串解码示例。
以下示例代码显示了字节向量和非空字段的unicode字符串的解码过程:
public int processByteVectorDecoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
int valueByteCount = 0;
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
valueByteCount = (valueByteCount << 7) | (buffer[offset++]);
}
valueByteCount = (valueByteCount << 7) | (buffer[offset++] & Byte.MAX_VALUE);
String strValue = null;
if (valueByteCount == 0) {
strValue = "";
}
if (valueByteCount > 0) {
strValue = new String(buffer, offset, valueByteCount);
}
fieldValue.setFieldValue(strValue);
return offset;
}
下一个代码示例显示了相同类型(字节向量和unicode字符串)但可为空的字段的解码过程:
public int processByteVectorDecoding(byte[] buffer, int offset, TemplateFieldValue fieldValue) {
int valueByteCount = 0;
while (!PresenceMap.hasBitOnPosition(buffer[offset], STOP_BIT_POSTION)) {
valueByteCount = (valueByteCount << 7) | (buffer[offset++]);
}
valueByteCount = (valueByteCount << 7) | (buffer[offset++] & Byte.MAX_VALUE);
String strValue = null;
valueByteCount--;
if (valueByteCount == 0) {
strValue = "";
}
if (valueByteCount > 0) {
strValue = new String(buffer, offset, valueByteCount);
}
fieldValue.setFieldValue(strValue);
return offset;
}
全文完。
附赠:FIX协议家族