Skip to content

Xetra fast enum definition #157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ jobs:
run: curl -L https://archives.boost.io/release/1.72.0/source/boost_1_72_0.tar.gz | tar zx
shell: bash
- name: configure
run: cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DXETRA_FAST_SPECIFICATION=ON
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON
- name: configure xetra
run: cmake -B ${{github.workspace}}/build-xetra -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DXETRA_FAST_SPECIFICATION=ON
- name: build
run: cmake --build build --parallel 2
run: cmake --build ${{github.workspace}}/build --parallel 2
- name: build xetra
run: cmake --build ${{github.workspace}}/build-xetra --parallel 2
- name: test
run: cd build && ctest -VV
run: cd ${{github.workspace}}/build && ctest -VV
- name: test xetra
run: cd ${{github.workspace}}/build-xetra && ctest -VV
142 changes: 142 additions & 0 deletions src/mfast/xml_parser/field_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include <boost/tokenizer.hpp>
#include "mfast/field_instructions.h"

#ifdef XETRA_FAST_SPECIFICATION
#include <boost/optional.hpp>
#endif

using namespace tinyxml2;

namespace mfast {
Expand Down Expand Up @@ -520,6 +524,31 @@ void field_builder::add_template(const char *, template_instruction *inst) {
<< referenced_by_info(parent_->name()));
}

#if defined(XETRA_FAST_SPECIFICATION)
bool parse_enum_value(const char **enum_element_names,
const uint64_t *enum_element_values,
uint64_t num_elements, const char *value_name,
uint64_t &result) {
boost::optional<std::uint64_t> value_int;
try {
value_int = std::stoul(value_name);
} catch (...) {}
for (uint64_t i = 0; i < num_elements; ++i) {
// FAST 1.2 does not cleary specify what a default enum value refers to,
// search for a match in either name or value/deduce_value
if (std::strcmp(enum_element_names[i], value_name) == 0 ||
(value_int.has_value() && enum_element_values[i] == *value_int)) {
if (enum_element_values)
result = enum_element_values[i];
else
result = i;
return true;
}
}

return false;
}
#else
bool parse_enum_value(const char **enum_element_names,
const uint64_t *enum_element_values,
uint64_t num_elements, const char *value_name,
Expand All @@ -537,6 +566,7 @@ bool parse_enum_value(const char **enum_element_names,

return false;
}
#endif

bool parse_enum_value(const enum_field_instruction *inst,
const char *value_name, uint64_t &result) {
Expand All @@ -547,6 +577,117 @@ bool parse_enum_value(const enum_field_instruction *inst,
struct tag_value;
typedef boost::error_info<tag_value, std::string> value_info;


#if defined(XETRA_FAST_SPECIFICATION)
void field_builder::visit(const enum_field_instruction *inst, void *) {

const XMLElement *element = &this->element_;
if (!field_op::find_field_op_element(*element))
element = content_element_;
field_op fop(inst, element, alloc());

const char **enum_element_names = inst->elements();
uint64_t num_elements = inst->num_elements();
const uint64_t *enum_element_values = inst->element_values();

const char *init_value_str = nullptr;
if (!fop.initial_value_.is_defined()) {
// if the defined flag is false, the content value is parsed string from
// XML
init_value_str = fop.initial_value_.get<const char *>();
}

if (enum_element_names == nullptr) {

std::deque<const char *> names;
std::deque<uint64_t> values;

const XMLElement *xml_element =
content_element_->FirstChildElement("element");
for (; xml_element != nullptr;
xml_element = xml_element->NextSiblingElement("element")) {
// Use fancier identifier if available (Eurex style)
const char *name_attr = xml_element->Attribute("id");
// Otherwise revert to the specified name attribute
if (name_attr == nullptr)
name_attr = xml_element->Attribute("name");
if (name_attr != nullptr) {
if (init_value_str && std::strcmp(name_attr, init_value_str) == 0) {
fop.initial_value_.set<uint64_t>(names.size());
}
names.push_back(string_dup(name_attr, alloc()));

const char *value_str = xml_element->Attribute("value");
if (value_str) {
uint64_t v = boost::lexical_cast<uint64_t>(value_str);
if (values.empty() || v > values.back()) {
values.push_back(v);
}
} else {
// FAST 1.2 specification does not require a value attribute
if (values.empty())
values.push_back(0);
else
values.push_back(values.back() + 1);
}
}
}

if (values.size() != names.size()) {
throw std::runtime_error("Invalid value specification for enum elements");
}

num_elements = names.size();
enum_element_names = static_cast<const char **>(
alloc().allocate(names.size() * sizeof(const char *)));
std::copy(names.begin(), names.end(), enum_element_names);

if (values.size()) {
uint64_t *values_array = static_cast<uint64_t *>(
alloc().allocate(values.size() * sizeof(uint64_t)));
std::copy(values.begin(), values.end(), values_array);
enum_element_values = values_array;
}
} else if (init_value_str) {
// In this case, the element names are already defined, but we haven't
// decide what the specified
// initial value is.

uint64_t init_value;
if (parse_enum_value(enum_element_names, enum_element_values, num_elements,
init_value_str, init_value)) {
fop.initial_value_ =
value_storage(0); // reset the storage to defined value
fop.initial_value_.set<uint64_t>(init_value);
} else {
BOOST_THROW_EXCEPTION(
fast_static_error("Unrecognized enum initial value : ")
<< value_info(init_value_str));
}
}

if (!fop.initial_value_.is_defined()) {
if (fop.initial_value_.get<const char *>() != nullptr) {
std::string msg = "Invalid initial value for enum : ";
throw std::runtime_error(msg + init_value_str);
} else {
// at this point if initial_value_ is still undefined, we should reset it
// to zero
fop.initial_value_.set<uint64_t>(0);
}
}

auto instruction = new (alloc()) enum_field_instruction(
fop.op_, get_presence(inst), get_id(inst), get_name(alloc()),
get_ns(inst, alloc()), fop.context_,
int_value_storage<uint64_t>(fop.initial_value_), enum_element_names,
enum_element_values, num_elements,
inst->elements_ == nullptr ? nullptr : inst, inst->cpp_ns(),
parse_tag(inst));

parent_->add_instruction(instruction);
}
#else
void field_builder::visit(const enum_field_instruction *inst, void *) {

const XMLElement *element = &this->element_;
Expand Down Expand Up @@ -647,6 +788,7 @@ void field_builder::visit(const enum_field_instruction *inst, void *) {

parent_->add_instruction(instruction);
}
#endif

instruction_tag field_builder::parse_tag(const field_instruction *inst) {
uint64_t value = inst->tag().to_uint64();
Expand Down
4 changes: 4 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ FASTTYPEGEN_TARGET(simple_types16 simple16.xml)

if(XETRA_FAST_SPECIFICATION)
FASTTYPEGEN_TARGET(simple_types17 simple17.xml)
FASTTYPEGEN_TARGET(simple_types18 simple18.xml)
endif(XETRA_FAST_SPECIFICATION)

FASTTYPEGEN_TARGET(test_types1 test1.xml test2.xml)
Expand Down Expand Up @@ -94,8 +95,11 @@ set(test_sources
if(XETRA_FAST_SPECIFICATION)
set(test_sources ${test_sources}
${FASTTYPEGEN_simple_types17_OUTPUTS}
${FASTTYPEGEN_simple_types18_OUTPUTS}
timestamp_encoder_decoder_v2.cpp
timestamp_encoder_decoder.cpp
xetra_enum_encoder_decoder_v2.cpp
xetra_enum_encoder_decoder.cpp
)
endif(XETRA_FAST_SPECIFICATION)

Expand Down
30 changes: 30 additions & 0 deletions tests/simple18.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates
xmlns="http://www.fixprotocol.org/ns/fast/td/1.2"
xmlns:scp="http://www.fixprotocol.org/ns/fast/scp/1.2"
templateNs="https://www.deutsche-boerse.com/rta">
<define name="TypeMDStatisticStatus">
<enum>
<element name="1" id="Active"/>
<element name="2" id="Inactive"/>
</enum>
</define>
<define name="TypeUnit">
<enum>
<element name="0" id="Seconds"/>
<element name="3" id="MilliSeconds"/>
<element name="10" id="Minutes"/>
<element name="12" id="Days"/>
</enum>
</define>
<!-- Define templates-->
<template name="Test_1" id="1">
<string name="MsgType" id="35"><constant value="DP"/></string>
<field name="MDStatisticStatus" id="2477"><type name="TypeMDStatisticStatus"><default value="0"/></type></field>
<field name="MDStatisticFrequencyUnit" id="2461" presence="optional"><type name="TypeUnit"/></field>
</template>
<template name="Test_2" id="2">
<string name="MsgType" id="36"><constant value="DQ"/></string>
<field name="MDStatisticIntervalUnit" id="2467"><type name="TypeUnit"><default value="3"/></type></field>
</template>
</templates>
43 changes: 43 additions & 0 deletions tests/xetra_enum_encoder_decoder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "catch.hpp"
#include <mfast.h>

#include "fast_test_coding_case.hpp"
#include "byte_stream.h"

#include "simple18.h"

using namespace test::coding;

TEST_CASE("xetra enum test encoder/decoder","[xetra_enum_encoder_decoder]")
{
SECTION("No optional enum / Active")
{
fast_test_coding_case<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Active();
REQUIRE(test_case.encoding(test_1.cref(),"\xc0\x81\x80",true));
REQUIRE(test_case.decoding("\xc0\x81\x80",test_1.cref(),true));
}

SECTION("No optional enum / Inactive")
{
fast_test_coding_case<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Inactive();
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x80",true));
REQUIRE(test_case.decoding("\xe0\x81\x81\x80",test_1.cref(),true));
}

SECTION("Optional enum")
{
fast_test_coding_case<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Inactive();
test_1_mref.set_MDStatisticFrequencyUnit().as_Minutes();
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x83",true));
REQUIRE(test_case.decoding("\xe0\x81\x81\x83",test_1.cref(),true));
}
}
43 changes: 43 additions & 0 deletions tests/xetra_enum_encoder_decoder_v2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "catch.hpp"
#include <mfast.h>

#include "fast_test_coding_case_v2.hpp"
#include "byte_stream.h"

#include "simple18.h"

using namespace test::coding;

TEST_CASE("xetra enum test encoder_V2/decoder_v2","[xetra_enum_encoder_v2_decoder_v2]")
{
SECTION("No optional enum / Active")
{
fast_test_coding_case_v2<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Active();
REQUIRE(test_case.encoding(test_1.cref(),"\xc0\x81\x80",true));
REQUIRE(test_case.decoding("\xc0\x81\x80",test_1.cref(),true));
}

SECTION("No optional enum / Inactive")
{
fast_test_coding_case_v2<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Inactive();
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x80",true));
REQUIRE(test_case.decoding("\xe0\x81\x81\x80",test_1.cref(),true));
}

SECTION("Optional enum")
{
fast_test_coding_case_v2<simple18::templates_description> test_case;
simple18::Test_1 test_1;
simple18::Test_1_mref test_1_mref = test_1.mref();
test_1_mref.set_MDStatisticStatus().as_Inactive();
test_1_mref.set_MDStatisticFrequencyUnit().as_Minutes();
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x83",true));
REQUIRE(test_case.decoding("\xe0\x81\x81\x83",test_1.cref(),true));
}
}