OnixS C++ FIX Engine  4.3.0
API Documentation
Decoding Data using FAST Event-based decoder

This section describes the usage of the OnixS::FIX::FAST::EventBasedDecoder class together with OnixS::FIX::FAST::IDecodeListener interface.

General considerations

FAST decoder could be used in a callback mode, which called 'event based' one. Within this mode, decoder does not create FIX message by itself, but instead of this, it notifies a user about different stages of the decoding process. These notification are sent using special interface (OnixS::FIX::FAST::IDecodeListener), which should be passed to the decoder as parameter of OnixS::FIX::FAST::EventBasedDecoder::decode method.

See the table below to consult about decoding process stages and callbacks.

Decoder positionListener callback
Message decode beginning

Decoder just got template identifier from input FAST stream and appropriate FIX message type identifier was detected. Both identifiers are passed to the callback through appropriate parameters.
OnixS::FIX::FAST::IDecodeListener::onBeginMessage
Simple
field


Field value decoded

Decoder just decoded a simple field; it means that appropriate input data was read, and required FAST operator was applied to.

The "simple" term means a field of one of the following FAST types:
  • <int32>
  • <uInt32>
  • <int64>
  • <uInt64>
  • <string>
  • <byteVector>
  • <decimal> There are no separate calls, generated for <exponent> and <mantissa> parts.
The order of a field decoding corresponds exactly to order of these fields in the FAST template.

Note: When decoding results in NULL value, there is no callback performed.
OnixS::FIX::FAST::IDecodeListener::onValue

(depends on the actual field type)
Sequence
Sequence beginning

Decoder just desired that input data contains non-empty sequence (length of the sequence is not zero). No any entries were decoded yet. Note, that there are no separate call for <length> field of sequence: length of the sequence is passed to the callback through appropriate parameter.

See FAST specification about <sequence> instruction for details about sequence tag detection scheme.
OnixS::FIX::FAST::IDecodeListener::onBeginSequence
Sequence entry
(steps repeated for each one entry of the sequence)
Entry beginning

Decoder just desired index of the current entry. This index is passed through appropriate parameter of the callback.
OnixS::FIX::FAST::IDecodeListener::onBeginSequenceEntry
Entry body

The sequence entry could be of any type allowed by FAST specification: a simple field, embedded sequence or a group. Therefore, the decoder will generate callback(s) here in accordance to the template specification.
Depend on a type of the entry.
Entry ended

Decoder just finished decoding of the entry.
OnixS::FIX::FAST::IDecodeListener::onEndSequenceEntry
Sequence ended

Decoder finished sequence decoding.
OnixS::FIX::FAST::IDecodeListener::onEndSequence
GroupGroup beginning

Decoder just desired that input data contains non-empty group. See FAST specification about <group> instruction.
OnixS::FIX::FAST::IDecodeListener::onBeginGroup
Group entryThe group entry could be of any type allowed by FAST specification: a simple field, embedded sequence or another group. Therefore, decoder will generate callback(s) here in accordance to the template specification.Depend on a type of the entry.
Group ended

Decoder just finished the group decoding.
OnixS::FIX::FAST::IDecodeListener::onEndGroup
Message decode ended

Message decoding was successfully completed.
OnixS::FIX::FAST::IDecodeListener::onEndMessage

Error handling

Whenever decoder meets data or another error occurs, it just throws C++ exception of the OnixS::FIX::Exception type. Decoding process becomes broken, and no more callback will be performed.

Sample of the listener implementation

class MyDecodeListener : public OnixS::FIX::FAST::IDecodeListener
{
public:
MyDecodeListener()
: beginMessageCount_(0), endMessageCount_(0),
valueCount_(0),
beginSequenceCount_(0), endSequenceCount_(0),
beginSequenceEntryCount_(0), endSequenceEntryCount_(0),
beginGroupCount_(0), endGroupCount_(0)
{}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations of IDecodeListener methods
void onBeginMessage(unsigned /*templateId*/, const char * /*messageType*/, size_t /*messageTypeLength*/) ONIXS_FIXENGINE_OVERRIDE
{
++beginMessageCount_;
}
void onEndMessage() ONIXS_FIXENGINE_OVERRIDE
{
++endMessageCount_;
}
void onValue(int /*tag*/, int /*value*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(int /*tag*/, unsigned /*value*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(int /*tag*/, long long /*value*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(int /*tag*/, unsigned long long /*value*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(int /*tag*/, long long /*mantissa*/, int /*exponent*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(int /*tag*/, const char * /*value*/, size_t /*valueLength*/) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onBeginSequence(int /*tag*/, size_t /*itemCount*/, int /*lengthFieldTag*/) ONIXS_FIXENGINE_OVERRIDE { ++beginSequenceCount_; }
void onEndSequence() ONIXS_FIXENGINE_OVERRIDE { ++endSequenceCount_; }
void onBeginSequenceEntry(size_t /*index*/) ONIXS_FIXENGINE_OVERRIDE { ++beginSequenceEntryCount_; }
void onEndSequenceEntry() ONIXS_FIXENGINE_OVERRIDE { ++endSequenceEntryCount_; }
void onBeginGroup(int /*tag*/) ONIXS_FIXENGINE_OVERRIDE { ++beginGroupCount_; }
void onEndGroup() ONIXS_FIXENGINE_OVERRIDE { ++endGroupCount_; }
/////////////////////////////////////////////////////////////////////////////////////////////////
// Other methods
void printStatistics()
{
assert(beginMessageCount_ == endMessageCount_);
assert(beginSequenceCount_ == endSequenceCount_);
assert(beginSequenceEntryCount_ == endSequenceEntryCount_);
assert(beginGroupCount_ == endGroupCount_);
std::cout << "Messages: " << endMessageCount_ << std::endl
<< "Fields: " << valueCount_ << std::endl
<< "Sequences: " << endSequenceCount_ << std::endl
<< "Groups: " << endGroupCount_ << std::endl;
}
private:
unsigned beginMessageCount_;
unsigned endMessageCount_;
unsigned valueCount_;
unsigned beginSequenceCount_;
unsigned endSequenceCount_;
unsigned beginSequenceEntryCount_;
unsigned endSequenceEntryCount_;
unsigned beginGroupCount_;
unsigned endGroupCount_;
};

Usage of event based decoder

using namespace OnixS::FIX;
using namespace OnixS::FIX::FAST;
EngineSettings settings;
settings.listenPort(-1)
.dictionaryFile("dictionaries/FixDictionary_FDMM.xml");
Engine::init(settings);
std::ifstream templatesStream("templates/FastTemplates_FDMM.xml");
assert(templatesStream);
const std::string xmlFastTemplates((std::istreambuf_iterator<char>(templatesStream)), std::istreambuf_iterator<char>());
assert(!xmlFastTemplates.empty());
Dictionary dictionary("FDMM");
const bool decodeEachMessageIndependently = true;
FAST::EventBasedDecoder decoder(xmlFastTemplates, dictionary, decodeEachMessageIndependently, InputDataTraits::CouldContainPartialMessages);
std::ifstream dataStream("data/fastChunk_FDMM.bin", std::ios_base::in | std::ios_base::binary);
assert(dataStream);
const std::string binaryChunk((std::istreambuf_iterator<char>(dataStream)), std::istreambuf_iterator<char>());
assert(!binaryChunk.empty());
try
{
MyDecodeListener myListener;
size_t fastChunkLength = 0;
bool result = decoder.decode(binaryChunk.data(), binaryChunk.length(), &myListener, &fastChunkLength);
if (result)
{
std::cout << fastChunkLength << " bytes of " << binaryChunk.length() << " used." << std::endl;
myListener.printStatistics();
}
else
{
std::cout << "Nothing to decode" << std::endl;
}
}
catch (const OnixS::FIX::Exception &e)
{
std::cerr << e.what();
}