<variant> Field

This field is basically a union of other fields. It can hold only one field at a time out of the provided list of supported fields. The <variant> field has all the common properties as well as extra properties and elements described below.

Member Fields

The <variant> field is there to support heterogeneous lists of fields. The classic example would be a list of key-value pairs, where numeric key defines what type of value follows. Similar to <bundle> field, member fields need to be listed as children XML elements of the <variant>.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <int name="Key" type="uint8" failOnInvalid="true" displayReadOnly="true" />

        <variant name="Property">
            <bundle name="Prop1">
                <int reuse="Key" defaultValue="0" validValue="0" />
                <int name="Value" type="uint32" />
            </bundle>
            <bundle name="Prop2">
                <int reuse="Key" defaultValue="1" validValue="1" />
                <string name="Value" length="16" />
            </bundle>
        </variant>

        <list name="PropertiesList" element="Property" />
    </fields>
</schema>

The example above defines every key-value pair as <bundle> field, where first field (key) reuses external definition of Key field and adds its default construction value as well as what value considered to be valid. NOTE, that every key member sets failOnInvalid property to true. The code generator is expected to generate code that attempts read operation of every defined member field in the order of their definition. Once the read operation is successful, the right member has been found and the read operation needs to terminate. The in-order read is high level logic, the code generator is allowed to introduce optimizations as long as the outcome of detecting the right member is the same.

If there is any other property defined as XML child of the <variant>, then all the members must be wrapped in <members> XML element for separation.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <variant name="Property">
            <displayName value="Proper Variant Name" />
            <members>
                <bundle name="Prop1">
                    ...
                </bundle>
                ...
            </members>
        </variant>
    </fields>
</schema>

Another quite popular example of is to have heterogeneous list of TLV (type / length / value) triplets.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <int name="Type" type="uint8" failOnInvalid="true" displayReadOnly="true" />

        <variant name="Property">
            <bundle name="Prop1">
                <int reuse="Type" defaultValue="0" validValue="0" />
                <int name="Length" type="uint16" semanticType="length" />
                <int name="Value" type="uint32" />
            </bundle>
            <bundle name="Prop2">
                <int reuse="Type" defaultValue="1" validValue="1" />
                <int name="Length" type="uint16" semanticType="length" />
                <string name="Value" />
            </bundle>
            ... 
        </variant>

        <list name="PropertiesList" element="Property" />
    </fields>
</schema>

Please note assigning semanticType property to be length for the Length field in every bundle. It specifies that the field contains remaining length of all the subsequent fields in the <bundle> and allows the code generator to produce correct code. The support for length value of semanticType has been introduced in v2 of this specification.

Quite often the developers wonder why there is a need to use remaining length information for the fields, length of which is constant and known and compile time. It allows introducing more fields in future versions of the protocol while preserving forward / backward compatibility of the protocol. For example:

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big" version="2">
    <fields>
        <int name="Type" type="uint8" failOnInvalid="true" displayReadOnly="true" />

        <variant name="Property">
            <bundle name="Prop1">
                <int reuse="Type" defaultValue="0" validValue="0" />
                <int name="Length" type="uint16" semanticType="length" />
                <int name="Value" type="uint32" />
                <int name="Value2" type="int16" sinceVersion="2"/>
            </bundle>
            ... 
        </variant>

        <list name="PropertiesList" element="Property" />
    </fields>
</schema>

The old version of the protocol code, that is not aware of extra field being added in the new version, will be able to skip over unknown data and read the next property from the correct location.

It also allows safe reception and handling of unexpected (or unknown) properties that could be introduced in the future versions of the protocol while still operating correctly.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <int name="Type" type="uint8" failOnInvalid="true" displayReadOnly="true" />

        <variant name="Property">
            ...
            <bundle name="UnknownProp">
                <int reuse="Type" failOnInvalid="false" />
                <int name="Length" type="uint16" semanticType="length" />
                <data name="Value" />
            </bundle>
        </variant>

        <list name="PropertiesList" element="Property" />
    </fields>
</schema>

NOTE, that in the example above the UnknownProp is defined to be the last member field of the <variant> field and has non-failing read of its Type (failOnInvalid property has been set to false).

Default Member

When <variant> field is constructed, it should not hold any field and when serialized, it mustn't produce any output. However, it is possible to specify default member to which the <variant> field should be initialized when constructed. To specify such member use defaultMember property.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <variant name="Property" defaultMember="Prop1">
            <bundle name="Prop1">
                ...
            </bundle>
            ...
        </variant>
    </fields>
</schema>

The defaultMember property may also specify index instead of the member name.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <variant name="Property" defaultMember="0">
            <bundle name="Prop1">
                ...
            </bundle>
            ...
        </variant>
    </fields>
</schema>

Negative number as value of defaultMember property will force the <variant> field not to have a default member.

Extra Display Property

By default GUI protocol analysis tools should display the index of the held member when displaying <variant> field in "read only" mode. However, for some occasions this information may be irrelevant. To hide display of index use displayIdxReadOnlyHidden property with boolean value.

<?xml version="1.0" encoding="UTF-8"?>
<schema name="MyProtocol" endian="big">
    <fields>
        <variant name="Property" displayIdxReadOnlyHidden="true">
            <bundle name="Prop1">
                ...
            </bundle>
            ...
        </variant>
    </fields>
</schema>

Use properties table for future references.

results matching ""

    No results matching ""