FIX FAST 教程 (2) 消息解码过程

3. FAST 消息解码过程

解码FAST消息时,我们需要确定模板ID,检索模板,并将消息内容与模板定义和操作符结合起来,重新生成原始消息。

请添加图片描述
FAST解码器使用以下算法解码每条消息:

请添加图片描述
FAST解码器使用以下算法解码每个字段:
请添加图片描述
综上所述,我们遵循以下步骤解码FAST消息:

  • 3.1提取Presence Map
  • 3.2确定消息模板ID
  • 3.3解码每个消息字段

3.1提取Presence Map

PMap通常出现在每条FAST消息的开头,有时还会出现在一条FAST消息中间。它有若干表示消息中的每个字段(不是“必需的”)是否存在于消息体中的bit位组成。如果我们的FAST消息包含一个重复序列(FIX中的Group,一组可重复的字段的集合),若其中任何字段需要PMap位,那么PMap将在序列的每一组之前出现。

完整的FAST消息的一般结构如图

请添加图片描述
PMap是采用停止位编码的实体。它是一个位序列,每个位按位的顺序表示一个模板的字段(按模板定义的顺序)。PresenceMap指出模板中的哪些字段有数据,哪些字段有隐含的数据。有数据的字段将PMap位设置为“1”。带有隐含数据的字段将PMap位设置为“0”(不存在于流中)。所以如果PMap位设置为“1”,我们应该解码值为适当的位,比如:

    if (presenceMap.hasNextPresenceBit()) {
        offset = dataTypeDecoder.decode(encodedData, offset, templateField);
        ...
    }

从逻辑上讲,一个PresenceMap有一个无限的0后缀。一个结尾每位都为零的序列的PresenceMap可以被截断。这个概念如下所示。
请添加图片描述
解码器的实现应该能够确定PMap是否具有特定的值。例如:

    public boolean hasNextPresenceBit() {
        if (isEmptyPMap) {
            return false;
        }
        if (offset > lastPMapBytePosition) {
            //Implies that presence map is empty and has the infinitive suffix of zeros
            isEmptyPMap = true;
            return false;
        }
        
        boolean bitOnPosition = hasBitOnPosition(currentByte, currentBitPosition);
        currentBitPosition++;
        if (currentBitPosition == NUMBER_BITS_IN_BYTE) {
            currentBitPosition = 1;		//注意此处
            offset++;
            currentByte = data[offset];
        }
        return bitOnPosition;
    }

我们的Presence Map类示例不执行停止位解码来存储PMap数据。编码的字节以实际编码的格式存储。PMap不使用停止位值,所以它传递这些值。

检查是否为特定位置设置了位,可以很容易地定义如下:

    public static boolean isPositionSet(byte byteToCheck, int bitPosition) {
        if (bitPosition > 0) {
            byteToCheck = (byte) (byteToCheck << bitPosition);
        }
        if (byteToCheck < 0) {
            return true;
        }
        return false;
    }

在上面的示例中,我们将字节做左位移运算将所需位移动到第一个位,如果移位的字节中的第一个位为1,则返回true

例如,我们有一个FAST消息,它只包含templateID字段: 0xC0 0xA9
这里它的比特表示: 11000000 10101001
使用停止位定义,我们通过以下步骤定义编码FAST消息PMap的字节量:

  • 逐个获取字节。
    第一个是0xC0,有1100000位表示。
  • 检查当前字节的停止位值。如果它等于" 1 ",我们的PMap由这个字节完成。
    在我们的例子中,0xC0的第一个位等于“1”,所以我们的PMap是1100000
    如果当前字节的第一个比特不等于“1”,我们将PMap的长度增加1,并使用下一个字节从第1步开始重复我们的过程。

3.2 确定消息模板ID

任何消息中的Presence Map的第一个位(在停止位之后)显示templateID的存在,这是为我们的消息定义模板所必需的。模板ID使用隐式复制操作符。因此,如果第一个位为1,则流中存在Template ID,如果位为0,则Template ID与前一个消息的Template ID相同。在任何情况下,我们必须始终确定templateID,因为它是解码FAST消息所必需的。

在我们的例子中,模板ID出现在流中,如下所示:请添加图片描述
模板ID字段跟在PMap后面,因此提取模板ID是FAST解码过程的第二步。解码器将使用提取的模板ID检索正确的FAST消息模板,然后使用该模板解码FAST消息的其余部分。
相同的FAST消息模板在FAST编码器和解码器(通常是消息生产者和消费者)之间共享。这种关系如下图所示。

请添加图片描述

3.2.1 模板

通常,FAST消息模板存储在单个XML文件中,并定义如何编码和解码消息内容。这些模板对于编码器和解码器都必须是相同的。模板XML文件采用的语法应该是“标准XML”。

DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    File xmlFile = new File(templateFileName);
    Document doc = null;
    try {
        doc = docBuilder.parse(xmlFile);
    } catch (Exception ex) {
        //handle exception ...
    }

模板xml文件包含一组描述编码消息格式的FAST消息模板。

3.2.2 消息模板

为了演示消息模板处理,我们将使用FIX市场数据增量更新消息(FIX消息类型X)的模板。

在下面的示例java片段中,我们将从xml中将FAST消息模板导入到java HashMap中,以便在应用程序的其余部分中方便地访问。

Map<integer, messagetemplate=""> messageTemplates = new HashMap<integer, messagetemplate="">();
    public void readTemplates() {
        int templateId;
        for (int i =0; i < msgTemplatesCount; i++) {
            templateId = parseTemplateId();
            messageTemplates.put(templateId, new MessageTemplate(parseTemplateFields()));
        }
    }
</integer,></integer,>

第二个解码步骤是识别模板ID。下面的代码剪辑接收一条FAST消息,提取该消息PMap,解码模板ID,获取消息模板,并使用模板解码消息内容:

    int templateId;
    Message outputMessage;
    int offset;
    public Object decodeMessage(byte[] fastMessage) {
        offset = 0;
        // Get a presence map of the whole message
        presenceMap = new PresenceMap();
        offset = presenceMap.init(data, offset);
        
        // 注意此部分代码
        if (presenceMap.hasNextPrecenceBit()) {
            templateId = dataTypeDecoder.processUnInt32Decoding(fastMessage, offset);
        }
        messageTemplate = templateParser.getTemplate(templateId);
        outputMessage = new Message();
        messageTemplate.decode(fastMessage, offset, presenceMap, outputMessage);
        ...
    }

在FAST中,模板ID被编码为无符号整数,因此我们使用此数据类型的规则解码它并获得匹配的模板。然后,我们使用该模板和其他编码数据,并按照模板中的定义生成原始消息。

让我们解码3.1节中FAST消息0xC0 0xA9的templateID。

  1. 正如我们在上面定义的那样,我们的PMap看起来像: 1100 0000。第一个位是停止位,第二个位告诉我们消息中有下一个FAST条目。
    根据消息模板定义,它是templateID。所以我们取第二个字节并将其解码为templateID。
  2. 第二个字节是0xA9,它的位表示为1010 1001
    由于该字段类型是“Unsigned Integer”,并且根据此数据类型规则,我们可以通过删除第一个位获得其值:0101001
  3. 我们将这些位转换为十进制格式并接收41。我们的templateID是41

原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值