FIX FAST 教程 (3)解码字段

3.3 解码各个消息字段

FAST消息模板由字段对象组成。每个实体都应该分别编码。

3.3.1. 术语

为了在接下来的章节中介绍信息,我们将考虑以下表示消息模板结构的概念:

  • 模板。包含一个或多个模板单元(template unit)。
  • 模板单元。模板字段(template field)或序列(sequence)。
  • 模板字段。定义数据类型、状态属性和操作符。
  • 序列。是一个计数,后跟可重复的字段组(field group)。
  • 字段组(Field Group)。是一组模板单元,它们可能有自己的PMap。
3.3.2. 模板单元

根据FAST规范,模板定义了以下内部实体:

  • 模板字段
  • 模板序列

在下面的java界面中,每一个都用TemplateUnit表示:

    public interface TemplateUnit {
    public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, 
                        OutputMessage message);
	
    public void reset();
	
    }

reset()方法的目的是将Template Unit对象返回到初始(预解码)状态。

消息模板本身由一组字段组成,也由TemplateUnit表示。

    public class MessageTemplate implements TemplateUnit {
        private TemplateUnit[] templateFields;
        public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, 
                            OutputMessage message) {
            for (TemplateUnit templateField : templateFields) {
                offset = templateField.decode(buffer, offset, presenceMap, message);
            }
            return 0;
        }
        
        public void reset() {
            for (TemplateUnit templateField : templateFields) {
                templateField.reset();
            }
        }
    }

在我们继续之前,我们要介绍几个FAST消息模板概念:

3.3.2.1. 必选和可选字段

在FAST消息模板中,模板单元(即字段或序列)可以具有可选属性。如果设置,则字段或序列是可选的,否则是必选的。
这个属性有以下含义:

  • 如果模板单元是必需的,那么它必须出现在结果解码的消息(输出流)中。
  • 如果某个字段是可选的,则该字段可能不会出现在最终解码的消息中(在解码的消息中,特殊的编码NULL表示转换为无值)。

必选字段必须出现在已解码的消息中。下面的代码示例演示如何在遍历每个模板字段时测试此约束。

    offset = dataTypeDecoder.decode(encodedData, offset, templateField);
    Object decodedValue = getFieldValue();
    if (decodedValue == null) {
        if (isMandatory) {
            throw new RuntimeException("Mandatory field decoded value can not be NULL");
        } else {
        // do nothing
        }
        ...
    }
    ...
3.3.2.2. 前值,初始值,基值

一些模板指令(或称“操作符 - operator”)依赖于前一个值(前一条消息中的字段值,以下简称前值)。这是运算符的内部属性。
对于解码使用依赖于前值的运算符的字段,此前值用于产生当前消息的现值。
前值可以处于以下三种状态之一: 未定义(undefined)、(empty)和已指定的(assigned)。

  • 当处理开始时,所有值都处于未定义(undefined)状态。
  • 已指定的(assigned)状态表示前值存在并有值。
  • (empty)状态是一种特殊的已指定的(assigned)状态,它表示前值空缺。在这种情况下,前值是null表示的值。

在某些时刻,模板字段的前值的状态需要重置(reset)为未定义的状态。这些时刻由用于传输数据的协议定义:

  • 有包边界的协议(UDP,带分隔符的TCP流)考虑自动重置状态
  • 对于没有包边界的协议,复位通常只发生在连接开始时。

然而,重要的是重置必须在编码器和解码器中按照与流内容的顺序相同的顺序执行。
下面的示例显示在接收每个FAST消息后执行的重置操作。

    public Object decodeMessage(byte[] fastMessage) {
        ...    
        messageTemplate.decode(fastMessage, offset, presenceMap, outputMessage);
        messageTemplate.reset();		// 这一行
        return outputMessage;
    }

重置状态意味着将所有字段设置为初始预处理状态(未定义状态)。

字段的未定义状态意味着以前的值不存在。在这种情况下,在第一次迭代的解码过程中使用初始值代替前值。(初始值在Template中由operator元素的value属性指定。)
如果模板没有定义初始值(如果它具有“null”值表示),则可以使用基值代替初始值。每个字段根据数据类型属性定义了自己适当的基值

3.3.3. 模板字段

模板字段(Template Field)定义了以下主要解码动作:

  • 定义字段是否出现在流中(输入FAST消息)
  • 通过使用停止位定义解码的提取字节创建字段值
  • 定义针对已解码字段值的计算,以实现最终输出字段值。

当我们在代码中实现这个概念时,Template Field有一组TemplateUnit方法,根据它的字段操作符(FieldOperator)属性进行解码。接下来将描述字段操作符。

3.3.4. 字段操作符(FieldOperator)

可以将字段操作符分配给模板字段。一个简单的例子是“copy”操作符,如果此消息中没有值,则需要直接使用前值。这对于经常重复的日期来说是很常见的。

但是有许多操作符可以定义字段的编码和解码方式。这些都列在下面。

3.3.4.1. 字段存在表(PMap)需求

除了Presence Map之外,还有一个参数可以定义流中的Presence字段值。下面是它的描述:

在某些情况下,Presence Map可能不包含某些模板字段的位。在字段属性(操作符类型和强制 - Operator type and Mandatory)的关系中,我们定义了与该字段相关的PMap位的存在。
如果该字段值(不取决于该值是否为空)必须始终出现在输入编码消息中,则该字段不需要PMap中的存在位。 在这种情况下,该字段的PMap位总是在PMap中缺失,我们总是从输入消息解码字段值。在其他情况下,我们根据PMap位值(’ 1 ‘或’ 0 ')的关系从输入消息解码字段值。

下表根据字段的Operator类型和Mandatory属性显示了PMap位的存在。

表 3.1

OperationMandatoryOptional
NoOperatorNoNo
ConstantNoYes
CopyYesYes
DefaultYesYes
DeltaNoNo
IncrementYesYes
TailYesYes

每个模板字段实体对象都应该实现以下方法来定义PMap中存在位值的需求:

public boolean isPMapBitExist();

下面是使用这种方法的一个例子:

    public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
        if (isPMapBitExist()) {
            boolean presenceBit = presenceMap.hasNextPresenceBit();
            ...

从上表可以看出,我们应该只在字段操作符为Constant的情况下使用此方法。在其他情况下,我们不需要检查存在位值的要求。这个功能应该直接实现。

3.3.4.2. 可空(Nullable)字段

如前所述,模板字段有一个“可选”属性,这意味着该字段在输出消息中是可选的。为了指示输出消息中不存在某个字段,将在消息中放入一个特殊的NULL值。
字段在某些情况下是可空的,并可能影响编码字段的正常解码。例如,可选整数字段如果为正,则偏移1,并为表示NULL的保留值0腾出空间。对于强制的整数字段则不是这样。

确定字段可空性的规则如下表3.2所示。

表3.2

OperationMandatoryOptional
NoOperatorNoYes
ConstantNoNo
CopyNoYes
DefaultNoYes
DeltaNoYes
IncrementNoYes
TailNoYes

在代码中,您希望每个模板字段对象实现一个确定字段可空性的方法。

    public boolean isFieldNullable();

在FAST库中,我们有两个解码器,它们从流中执行字段值提取:

  • NullableDataTypeDecoder用于可为空的字段(可以返回null作为字段值)。
  • 非空字段的NonNullableDataTypeDecoder(不能返回null作为字段值)。

在为模板字段对象分配解码器之前,我们检查字段的可空性以选择正确的一个:


    dataTypeDecoder = isFieldNullable() ? NullableDataTypeDecoder.getDefaultInstance() 
                                        : NonNullableDataTypeDecoder.getDefaultInstance();
3.3.4.3. 无操作符(NoOperator)

没有定义运算符的字段只执行已编码的值提取,而不对提取的值执行其他操作。
下面的图表说明了这个解码过程:

请添加图片描述

下面是一个实现上述算法的java示例:

    public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
        offset = dataTypeDecoder.decode(encodedData, offset, templateField);
        Object decodedValue = getFieldValue();
        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() {
        // Since NoOp fields do not use a dictionary entry there is nothing to reset.
    }
    
    public boolean isPMapBitExist() {
        // Without operator fields not require presence map bit. In any case it should be in encoded message
        return false;
    }
    
    public boolean isFieldNullable() {
        // If isMandatory false the NoOp field could has the 'null' representation of field value.
        // In such case it could not apear in the output FIX message
        return !isMandatory;
    }
3.3.4.4. 常量操作符(Constant Operator)

常量操作符指定字段值总是给定值。
此值从不在消息本身中传输,而是由编码器抑制并由解码器替换。
这方面的一个例子可能是8=FIX.4.4,在这里您永远不会通过网络发送FIX.4.4
如果可选,字段将使用PMap位。如果未设置,则该字段将不会出现在输出消息中。
使用常量运算符的模板字段解码算法:

请添加图片描述
以下是上述算法在java中的示例实现:

public int decode(byte[] encodedData, int offset, PresenceMap presenceMap, OutputMessage message) {
        if (isMandatory || (isPMapBitExist() && presenceMap.hasNextPresenceBit())) {
            message.addValue(tagId, initialValue);
        }
        return offset;
    }
    
    public void reset() {
        // Constant field never use the previous value
    }
    
    public boolean isPMapBitExist() {
        // If isMandatory true the Constant field do not require a presence map bit. 
        // Otherwise it is require the presence map bit.
        return !isMandatory;
    }
    
    public boolean isFieldNullable() {
        // Constant field is not nullable
        return false;
    }
3.3.4.5. 复制操作符

如果先前的值没有出现在编码的消息中(未设置PMap位),则此操作符指示解码器使用该值。
下面是这些字段的解码算法:

请添加图片描述
该算法的代码示例:

    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);
            } else {
                if (isMandatory) {
                    throw new RuntimeException("Mandatory field decoded value can not be NULL");
                } else {
                    setPreviousValue(null);
                }
            } 
        } else {
            // there is no undefinite status for the previous value
            Object prevValue = getPreviousValue();
            if (prevValue != null) {
                // add field into the message
                message.addValue(tagId, prevValue);
            } 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());
    }
    
    public boolean isPMapBitExist() {
        // Copy fields require presence map bit
        return true;
    }
        
    public boolean isFieldNullable() {
        // If isMandatory false the Copy field could has the null representation of field value and does not apear in the output FIX message
        return !isMandatory;
    }

原文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值