• Version 4.4.3
Show / Hide Table of Contents

IMessage interface

IMessage interface represents a generic model of a SBE message; to manipulate such a message, you need to know the message Template ID and its structure.

Tag-based API

Message fields are accessed via the standard tag-based API. The id attributes in the message schema describe tag numbers.

For example:

   <ns2:message name="Negotiate500" id="500" description="Negotiate" blockLength="76" semanticType="Negotiate">
       <field name="CustomerFlow" id="39000" type="ClientFlowType" description="Constant value representing type of flow from customer to CME" semanticType="String"/>
       <field name="HMACVersion" id="39003" type="HMACVersion" description="Constant value representing CME HMAC version" semanticType="String"/>
       <field name="HMACSignature" id="39005" type="String32Req" description="Contains the HMAC signature." offset="0" semanticType="String"/>
       <field name="AccessKeyID" id="39004" type="String20Req" description="Contains the AccessKeyID assigned to this session on this port." offset="32" semanticType="String"/>
       <field name="UUID" id="39001" type="uInt64" description="Session Identifier defined as type long (uInt64); recommended to use timestamp as number of microseconds since epoch (Jan 1, 1970)" offset="52" semanticType="int"/>
       <field name="RequestTimestamp" id="39002" type="uInt64" description="Time of request; recommended to use timestamp as number of nanoseconds since epoch (Jan 1, 1970)" offset="60" semanticType="int"/>
       <field name="Session" id="39006" type="String3Req" description="Session ID" offset="68" semanticType="String"/>
       <field name="Firm" id="39007" type="String5Req" description="Firm ID" offset="71" semanticType="String"/>
       <data name="Credentials" id="39008" type="DATA" description="Not used and will be set to 0" semanticType="data"/>
   </ns2:message>

Named constants

The "Code Generator" tool can generate named constants for all field tags.

For example:

dotnet OnixS.SimpleBinaryEncoding.CodeGenerator.dll ilinkbinary.xml CmeILinkHeader Trading --source true
public static class Tag
{
    public const int AccessKeyID = 39004;
    public const int AffectedOrderID = 535;
    public const int AggressorIndicator = 1057;
    ...
    public const int CustomerFlow = 39000;
    ...
    public const int HMACVersion = 39003;
    ...

Template Library

An SBE Schema represents an XML-based description of encoding and decoding rules per the SBE specification. The SBE Codec using this information to decode or encode SBE messages.

To use an SBE encoder or decoder, first, create the TemplateLibrary instance using the SBE Schema file content:

var lib = TemplateLibrary.Parse(File.ReadAllText(@"../../../../templates/SbeMessageTemplates.xml"));

Generating Encoder and Decoder

The encoder and decoder can be generated after constructing the Template Library:

var generator = new Generator();
var assembly = generator.GenerateAssembly<CmeILinkHeader>(lib, "OnixS.SimpleBinaryEncoding.Sample");
var decoder = (IDecoder)Activator.CreateInstance(assembly.GetType("OnixS.SimpleBinaryEncoding.Sample.Decoder", true));
var encoder = (IEncoder)Activator.CreateInstance(assembly.GetType("OnixS.SimpleBinaryEncoding.Sample.Encoder", true));

Decoding

The IDecoder object creates an SBE message from the given byte chunk.

IMessage Get<FIELD-TYPE> methods can be used to read field values:

  • Get(int)
  • GetBoolean(int)
  • GetByte(int)
  • GetBytes(int)
  • GetChar(int)
  • GetComposite(int)
  • GetComposite<T>(int)
  • GetDecimal(int)
  • GetEnum<T>(int)
  • GetGroup(int)
  • GetInteger(int)
  • GetLong(int)
  • GetMaturityMonthYear(int)
  • GetShort(int)
  • GetSignedByte(int)
  • GetString(int)
  • GetTimestamp(int)
  • GetUnsignedInteger(int)
  • GetUnsignedLong(int)
  • GetUnsignedShort(int)
  • GetVariableLengthField(int)

For example:

var negotiate = decoder.Wrap(negotiateMemorySegment);

Console.WriteLine(@$"
                    hmacSignature={negotiate.GetString(Tag.HMACSignature)},
                    accessKeyID={negotiate.GetString(Tag.AccessKeyID)},
                    UUID={negotiate.GetUnsignedLong(Tag.UUID)},
                    requestTimestamp={negotiate.GetUnsignedLong(Tag.RequestTimestamp)},
                    session={negotiate.GetString(Tag.Session)},
                    firm={negotiate.GetString(Tag.Firm)}
                  ");

Reading optional fields

To read optional message fields, use the corresponding TryGet<FIELD-TYPE> method:

  • TryGetByte(int)
  • TryGetChar(int)
  • TryGetDecimal(int)
  • TryGetInteger(int)
  • TryGetLong(int)
  • TryGetShort(int)
  • TryGetSignedByte(int)
  • TryGetUnsignedInteger(int)
  • TryGetUnsignedLong(int)
  • TryGetUnsignedShort(int)

These methods return nullable values. If the message field is absent, the null value is returned.

For example:

var quoteCancelAck = decoder.Wrap(quoteCancelAckSegment);

Assert.Null(quoteCancelAck.TryGetChar(Tag.UnsolicitedCancelType));
Assert.Null(quoteCancelAck.TryGetUnsignedShort(Tag.QuoteRejectReason));
Assert.Null(quoteCancelAck.TryGetByte(Tag.TotNoQuoteEntries));

To check the field presence, use the Contains(int) method.

Encoding

The IEncoder object creates a new message that wraps the given buffer. Because an SBE message can have a variable length, a customer code is responsible for calculating the required size and creating a corresponding buffer.

The current message length is accessible via the MessageLength property.

IMessageHeader setters are used to set field values.

For example:

int NegotiateMessageTemplateId = 500;
MemoryPointer buffer = new(new byte[65536]);
var negotiate = encoder.Wrap(NegotiateMessageTemplateId, buffer)
    .SetString(Tag.HMACSignature, "HMAC")
    .SetString(Tag.AccessKeyID, "AccessKey")
    .SetUnsignedInteger(Tag.UUID, 1)
    .SetUnsignedLong(Tag.RequestTimestamp, (ulong)(DateTime.UtcNow.Ticks - new DateTime(1970, 1, 1).Ticks) * 1000000 / TimeSpan.TicksPerMillisecond)
    .SetString(Tag.Session, "ABC")
    .SetString(Tag.Firm, "ABCDE");

IMessage message = (IMessage)negotiate;
Console.WriteLine($"SBE encoded memory region: {Encoding.Default.GetString(message.Buffer[..message.MessageLength])}");

Writing optional fields

The IEncoder object sets optional fields to null values when it creates a new message.

To explicitly set the field value to the null value, use the SetToNull(int) method.

Repeating groups

Repeating groups are represented by the IGroup interface.

The repeating group API implements the Iterator pattern to reduce the GC load. There are no objects created for group entries. Group entry fields can be accessed via getters and setters in the same manner as for IMessage.

To advance the repeating group to the next entry, use the MoveNext() method.

Example:

int MassQuoteAck545MessageTemplateID = 545;
MemoryPointer buffer = new(new byte[65536]);
var quoteAck = encoder.Wrap(MassQuoteAck545MessageTemplateID, buffer);

// Create a repeating group with two entries.
IGroup group = quoteAck.SetGroup(Tag.NoQuoteEntries, 2);
group.Reset();

group.MoveNext();
group.SetUnsignedInteger(Tag.QuoteEntryID, 1);
group.SetInteger(Tag.SecurityID, 12212);
group.SetUnsignedShort(Tag.QuoteSetID, 452);

group.MoveNext();
group.SetUnsignedInteger(Tag.QuoteEntryID, 2);
group.SetInteger(Tag.SecurityID, 141234424);
group.SetUnsignedShort(Tag.QuoteSetID, 32339);
// Decode a repeating group.
var quoteAck = decoder.Wrap(quoteAckMemorySegment);

IGroup group = quoteAck.GetGroup(Tag.NoQuoteEntries);
group.Reset();

while (group.MoveNext())
{
    Console.WriteLine($@"
        QuoteEntryID={group.GetUnsignedInteger(Tag.QuoteEntryID)}
        SecurityID={group.GetInteger(Tag.SecurityID)}
        QuoteSetID={group.GetUnsignedShort(Tag.QuoteSetID)}
    ");
}
Note

Each GetGroup(int) and SetGroup(int, int) call returns the same repeating group object.

GC Free interface

Use WrapPreCreatedMessage(int, MemoryPointer) and WrapPreCreatedMessage(MemoryPointer) methods to prevent IMessage instance allocation. For the same message Template ID, these methods return the same IMessage instance stored inside IEncoder or IDecoder instances.

This approach allows to encode/decode SBE messages without memory allocation on the critical path.

Note

These methods should be used carefully because each subsequent call for the same message Template ID returns the same IMessage instance.

OnixS.SimpleBinaryEncoding.IMessage negotiate1 = decoder.WrapPreCreatedMessage(buffer1);
OnixS.SimpleBinaryEncoding.IMessage negotiate2 = decoder.WrapPreCreatedMessage(buffer2);
Assert.True(ReferenceEquals(negotiate1, negotiate2));

In the example above, negotiate1 and negotiate2 are references to the same IMessage instance, which refers the buffer2 memory segment.

See also

  • IMessage Sample
In this article
Back to top Copyright © Onix Solutions.
Generated by DocFX