This section describes the usage of the OnixS::FIX::SBE::EventBasedDecoder class together with OnixS::FIX::SBE::IDecodeListener interface.
General considerations
SBE 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::SBE::IDecodeListener), which should be passed to the decoder as parameter of OnixS::FIX::SBE::EventBasedDecoder::decodeSingleMessage method.
See the table below to consult about decoding process stages and callbacks.
Decoder position | Listener callback |
Message decode beginning
Decoder just got template identifier from input SBE stream and appropriate FIX message type identifier was detected. Both identifiers are passed to the callback through appropriate parameters.
| OnixS::FIX::SBE::IDecodeListener::onBeginMessage |
Simple
field
| Field value decoded
Decoder just decoded a simple field; it means that appropriate input data was read from the input data stream.
The "simple" term means a field of elementary or composite SBE type. The order of a field decoding corresponds exactly to order of these fields in the SBE template.
Note: When decoding results in NULL value, there is no callback performed. | OnixS::FIX::SBE::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 the length field: length of the sequence is passed to the callback through appropriate parameter.
See SBE specification about <group> instruction for details about sequence tag detection scheme.
| OnixS::FIX::SBE::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::SBE::IDecodeListener::onBeginSequenceEntry |
Entry body
The sequence entry could be of any type allowed by SBE specification: a simple field or embedded sequence. 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::SBE::IDecodeListener::onEndSequenceEntry |
Sequence ended
Decoder finished sequence decoding.
| OnixS::FIX::SBE::IDecodeListener::onEndSequence |
Message decode ended
Message decoding was successfully completed.
| OnixS::FIX::SBE::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
{
public:
MyDecodeListener()
: beginMessageCount_(0), endMessageCount_(0),
valueCount_(0),
beginSequenceCount_(0), endSequenceCount_(0),
beginSequenceEntryCount_(0), endSequenceEntryCount_(0)
{}
void onBeginMessage(
unsigned ,
const char * ,
size_t ) ONIXS_FIXENGINE_OVERRIDE
{
++beginMessageCount_;
}
{
++endMessageCount_;
}
void onValue(
int ,
char ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
short ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
unsigned short ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
int ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
unsigned ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
long long ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
unsigned long long ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
long long ,
int ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
float ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
double ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onValue(
int ,
const char * ,
size_t ) ONIXS_FIXENGINE_OVERRIDE { ++valueCount_; }
void onBeginSequence(
int ,
size_t ) ONIXS_FIXENGINE_OVERRIDE { ++beginSequenceCount_; }
void onEndSequence() ONIXS_FIXENGINE_OVERRIDE { ++endSequenceCount_; }
void printStatistics()
{
assert(beginMessageCount_ == endMessageCount_);
assert(beginSequenceCount_ == endSequenceCount_);
assert(beginSequenceEntryCount_ == endSequenceEntryCount_);
std::cout << "Messages: " << endMessageCount_ << std::endl
<< "Fields: " << valueCount_ << std::endl
<< "Sequences: " << endSequenceCount_ << std::endl;
}
private:
unsigned beginMessageCount_;
unsigned endMessageCount_;
unsigned valueCount_;
unsigned beginSequenceCount_;
unsigned endSequenceCount_;
unsigned beginSequenceEntryCount_;
unsigned endSequenceEntryCount_;
};
Usage of event based decoder
void sbeEventBasedDecoding()
{
EngineSettings settings;
Engine::init(settings);
std::ifstream templatesStream("templates/SbeTemplates.xml");
assert(templatesStream);
const std::string XmlSbeTemplates((std::istreambuf_iterator<char>(templatesStream)), std::istreambuf_iterator<char>());
assert(!XmlSbeTemplates.empty());
Dictionary dictionary("DICT1");
SBE::EventBasedDecoder decoder(XmlSbeTemplates);
std::ifstream dataStream("data/sbeChunk.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());
const int TemplateId = 10;
const int Version = schemaVersion();
const size_t RootBlockLength = 28;
try
{
MyDecodeListener myListener;
size_t sbeChunkLength = 0;
bool result = decoder.decodeSingleMessage(TemplateId, Version, RootBlockLength, reinterpret_cast<const unsigned char*>(BinaryChunk.data()), 0, BinaryChunk.length(), &myListener, &sbeChunkLength);
if (result)
{
std::cout << sbeChunkLength << " bytes of " << BinaryChunk.length() << " used." << std::endl;
myListener.printStatistics();
}
else
{
std::cout << "Nothing to decode" << std::endl;
}
}
{
std::cerr << e.what();
}
Engine::shutdown();
}