Skip to content

Commit 7910438

Browse files
Merge pull request #157 from martinfantini/xetra_fast_enum
Xetra fast enum definition
2 parents b4f4376 + 8b04943 commit 7910438

File tree

6 files changed

+271
-3
lines changed

6 files changed

+271
-3
lines changed

.github/workflows/main.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ jobs:
1616
run: curl -L https://archives.boost.io/release/1.72.0/source/boost_1_72_0.tar.gz | tar zx
1717
shell: bash
1818
- name: configure
19-
run: cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DXETRA_FAST_SPECIFICATION=ON
19+
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON
20+
- name: configure xetra
21+
run: cmake -B ${{github.workspace}}/build-xetra -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DXETRA_FAST_SPECIFICATION=ON
2022
- name: build
21-
run: cmake --build build --parallel 2
23+
run: cmake --build ${{github.workspace}}/build --parallel 2
24+
- name: build xetra
25+
run: cmake --build ${{github.workspace}}/build-xetra --parallel 2
2226
- name: test
23-
run: cd build && ctest -VV
27+
run: cd ${{github.workspace}}/build && ctest -VV
28+
- name: test xetra
29+
run: cd ${{github.workspace}}/build-xetra && ctest -VV

src/mfast/xml_parser/field_builder.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include <boost/tokenizer.hpp>
1010
#include "mfast/field_instructions.h"
1111

12+
#ifdef XETRA_FAST_SPECIFICATION
13+
#include <boost/optional.hpp>
14+
#endif
15+
1216
using namespace tinyxml2;
1317

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

527+
#if defined(XETRA_FAST_SPECIFICATION)
528+
bool parse_enum_value(const char **enum_element_names,
529+
const uint64_t *enum_element_values,
530+
uint64_t num_elements, const char *value_name,
531+
uint64_t &result) {
532+
boost::optional<std::uint64_t> value_int;
533+
try {
534+
value_int = std::stoul(value_name);
535+
} catch (...) {}
536+
for (uint64_t i = 0; i < num_elements; ++i) {
537+
// FAST 1.2 does not cleary specify what a default enum value refers to,
538+
// search for a match in either name or value/deduce_value
539+
if (std::strcmp(enum_element_names[i], value_name) == 0 ||
540+
(value_int.has_value() && enum_element_values[i] == *value_int)) {
541+
if (enum_element_values)
542+
result = enum_element_values[i];
543+
else
544+
result = i;
545+
return true;
546+
}
547+
}
548+
549+
return false;
550+
}
551+
#else
523552
bool parse_enum_value(const char **enum_element_names,
524553
const uint64_t *enum_element_values,
525554
uint64_t num_elements, const char *value_name,
@@ -537,6 +566,7 @@ bool parse_enum_value(const char **enum_element_names,
537566

538567
return false;
539568
}
569+
#endif
540570

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

580+
581+
#if defined(XETRA_FAST_SPECIFICATION)
582+
void field_builder::visit(const enum_field_instruction *inst, void *) {
583+
584+
const XMLElement *element = &this->element_;
585+
if (!field_op::find_field_op_element(*element))
586+
element = content_element_;
587+
field_op fop(inst, element, alloc());
588+
589+
const char **enum_element_names = inst->elements();
590+
uint64_t num_elements = inst->num_elements();
591+
const uint64_t *enum_element_values = inst->element_values();
592+
593+
const char *init_value_str = nullptr;
594+
if (!fop.initial_value_.is_defined()) {
595+
// if the defined flag is false, the content value is parsed string from
596+
// XML
597+
init_value_str = fop.initial_value_.get<const char *>();
598+
}
599+
600+
if (enum_element_names == nullptr) {
601+
602+
std::deque<const char *> names;
603+
std::deque<uint64_t> values;
604+
605+
const XMLElement *xml_element =
606+
content_element_->FirstChildElement("element");
607+
for (; xml_element != nullptr;
608+
xml_element = xml_element->NextSiblingElement("element")) {
609+
// Use fancier identifier if available (Eurex style)
610+
const char *name_attr = xml_element->Attribute("id");
611+
// Otherwise revert to the specified name attribute
612+
if (name_attr == nullptr)
613+
name_attr = xml_element->Attribute("name");
614+
if (name_attr != nullptr) {
615+
if (init_value_str && std::strcmp(name_attr, init_value_str) == 0) {
616+
fop.initial_value_.set<uint64_t>(names.size());
617+
}
618+
names.push_back(string_dup(name_attr, alloc()));
619+
620+
const char *value_str = xml_element->Attribute("value");
621+
if (value_str) {
622+
uint64_t v = boost::lexical_cast<uint64_t>(value_str);
623+
if (values.empty() || v > values.back()) {
624+
values.push_back(v);
625+
}
626+
} else {
627+
// FAST 1.2 specification does not require a value attribute
628+
if (values.empty())
629+
values.push_back(0);
630+
else
631+
values.push_back(values.back() + 1);
632+
}
633+
}
634+
}
635+
636+
if (values.size() != names.size()) {
637+
throw std::runtime_error("Invalid value specification for enum elements");
638+
}
639+
640+
num_elements = names.size();
641+
enum_element_names = static_cast<const char **>(
642+
alloc().allocate(names.size() * sizeof(const char *)));
643+
std::copy(names.begin(), names.end(), enum_element_names);
644+
645+
if (values.size()) {
646+
uint64_t *values_array = static_cast<uint64_t *>(
647+
alloc().allocate(values.size() * sizeof(uint64_t)));
648+
std::copy(values.begin(), values.end(), values_array);
649+
enum_element_values = values_array;
650+
}
651+
} else if (init_value_str) {
652+
// In this case, the element names are already defined, but we haven't
653+
// decide what the specified
654+
// initial value is.
655+
656+
uint64_t init_value;
657+
if (parse_enum_value(enum_element_names, enum_element_values, num_elements,
658+
init_value_str, init_value)) {
659+
fop.initial_value_ =
660+
value_storage(0); // reset the storage to defined value
661+
fop.initial_value_.set<uint64_t>(init_value);
662+
} else {
663+
BOOST_THROW_EXCEPTION(
664+
fast_static_error("Unrecognized enum initial value : ")
665+
<< value_info(init_value_str));
666+
}
667+
}
668+
669+
if (!fop.initial_value_.is_defined()) {
670+
if (fop.initial_value_.get<const char *>() != nullptr) {
671+
std::string msg = "Invalid initial value for enum : ";
672+
throw std::runtime_error(msg + init_value_str);
673+
} else {
674+
// at this point if initial_value_ is still undefined, we should reset it
675+
// to zero
676+
fop.initial_value_.set<uint64_t>(0);
677+
}
678+
}
679+
680+
auto instruction = new (alloc()) enum_field_instruction(
681+
fop.op_, get_presence(inst), get_id(inst), get_name(alloc()),
682+
get_ns(inst, alloc()), fop.context_,
683+
int_value_storage<uint64_t>(fop.initial_value_), enum_element_names,
684+
enum_element_values, num_elements,
685+
inst->elements_ == nullptr ? nullptr : inst, inst->cpp_ns(),
686+
parse_tag(inst));
687+
688+
parent_->add_instruction(instruction);
689+
}
690+
#else
550691
void field_builder::visit(const enum_field_instruction *inst, void *) {
551692

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

648789
parent_->add_instruction(instruction);
649790
}
791+
#endif
650792

651793
instruction_tag field_builder::parse_tag(const field_instruction *inst) {
652794
uint64_t value = inst->tag().to_uint64();

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ FASTTYPEGEN_TARGET(simple_types16 simple16.xml)
2828

2929
if(XETRA_FAST_SPECIFICATION)
3030
FASTTYPEGEN_TARGET(simple_types17 simple17.xml)
31+
FASTTYPEGEN_TARGET(simple_types18 simple18.xml)
3132
endif(XETRA_FAST_SPECIFICATION)
3233

3334
FASTTYPEGEN_TARGET(test_types1 test1.xml test2.xml)
@@ -94,8 +95,11 @@ set(test_sources
9495
if(XETRA_FAST_SPECIFICATION)
9596
set(test_sources ${test_sources}
9697
${FASTTYPEGEN_simple_types17_OUTPUTS}
98+
${FASTTYPEGEN_simple_types18_OUTPUTS}
9799
timestamp_encoder_decoder_v2.cpp
98100
timestamp_encoder_decoder.cpp
101+
xetra_enum_encoder_decoder_v2.cpp
102+
xetra_enum_encoder_decoder.cpp
99103
)
100104
endif(XETRA_FAST_SPECIFICATION)
101105

tests/simple18.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates
3+
xmlns="http://www.fixprotocol.org/ns/fast/td/1.2"
4+
xmlns:scp="http://www.fixprotocol.org/ns/fast/scp/1.2"
5+
templateNs="https://www.deutsche-boerse.com/rta">
6+
<define name="TypeMDStatisticStatus">
7+
<enum>
8+
<element name="1" id="Active"/>
9+
<element name="2" id="Inactive"/>
10+
</enum>
11+
</define>
12+
<define name="TypeUnit">
13+
<enum>
14+
<element name="0" id="Seconds"/>
15+
<element name="3" id="MilliSeconds"/>
16+
<element name="10" id="Minutes"/>
17+
<element name="12" id="Days"/>
18+
</enum>
19+
</define>
20+
<!-- Define templates-->
21+
<template name="Test_1" id="1">
22+
<string name="MsgType" id="35"><constant value="DP"/></string>
23+
<field name="MDStatisticStatus" id="2477"><type name="TypeMDStatisticStatus"><default value="0"/></type></field>
24+
<field name="MDStatisticFrequencyUnit" id="2461" presence="optional"><type name="TypeUnit"/></field>
25+
</template>
26+
<template name="Test_2" id="2">
27+
<string name="MsgType" id="36"><constant value="DQ"/></string>
28+
<field name="MDStatisticIntervalUnit" id="2467"><type name="TypeUnit"><default value="3"/></type></field>
29+
</template>
30+
</templates>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "catch.hpp"
2+
#include <mfast.h>
3+
4+
#include "fast_test_coding_case.hpp"
5+
#include "byte_stream.h"
6+
7+
#include "simple18.h"
8+
9+
using namespace test::coding;
10+
11+
TEST_CASE("xetra enum test encoder/decoder","[xetra_enum_encoder_decoder]")
12+
{
13+
SECTION("No optional enum / Active")
14+
{
15+
fast_test_coding_case<simple18::templates_description> test_case;
16+
simple18::Test_1 test_1;
17+
simple18::Test_1_mref test_1_mref = test_1.mref();
18+
test_1_mref.set_MDStatisticStatus().as_Active();
19+
REQUIRE(test_case.encoding(test_1.cref(),"\xc0\x81\x80",true));
20+
REQUIRE(test_case.decoding("\xc0\x81\x80",test_1.cref(),true));
21+
}
22+
23+
SECTION("No optional enum / Inactive")
24+
{
25+
fast_test_coding_case<simple18::templates_description> test_case;
26+
simple18::Test_1 test_1;
27+
simple18::Test_1_mref test_1_mref = test_1.mref();
28+
test_1_mref.set_MDStatisticStatus().as_Inactive();
29+
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x80",true));
30+
REQUIRE(test_case.decoding("\xe0\x81\x81\x80",test_1.cref(),true));
31+
}
32+
33+
SECTION("Optional enum")
34+
{
35+
fast_test_coding_case<simple18::templates_description> test_case;
36+
simple18::Test_1 test_1;
37+
simple18::Test_1_mref test_1_mref = test_1.mref();
38+
test_1_mref.set_MDStatisticStatus().as_Inactive();
39+
test_1_mref.set_MDStatisticFrequencyUnit().as_Minutes();
40+
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x83",true));
41+
REQUIRE(test_case.decoding("\xe0\x81\x81\x83",test_1.cref(),true));
42+
}
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "catch.hpp"
2+
#include <mfast.h>
3+
4+
#include "fast_test_coding_case_v2.hpp"
5+
#include "byte_stream.h"
6+
7+
#include "simple18.h"
8+
9+
using namespace test::coding;
10+
11+
TEST_CASE("xetra enum test encoder_V2/decoder_v2","[xetra_enum_encoder_v2_decoder_v2]")
12+
{
13+
SECTION("No optional enum / Active")
14+
{
15+
fast_test_coding_case_v2<simple18::templates_description> test_case;
16+
simple18::Test_1 test_1;
17+
simple18::Test_1_mref test_1_mref = test_1.mref();
18+
test_1_mref.set_MDStatisticStatus().as_Active();
19+
REQUIRE(test_case.encoding(test_1.cref(),"\xc0\x81\x80",true));
20+
REQUIRE(test_case.decoding("\xc0\x81\x80",test_1.cref(),true));
21+
}
22+
23+
SECTION("No optional enum / Inactive")
24+
{
25+
fast_test_coding_case_v2<simple18::templates_description> test_case;
26+
simple18::Test_1 test_1;
27+
simple18::Test_1_mref test_1_mref = test_1.mref();
28+
test_1_mref.set_MDStatisticStatus().as_Inactive();
29+
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x80",true));
30+
REQUIRE(test_case.decoding("\xe0\x81\x81\x80",test_1.cref(),true));
31+
}
32+
33+
SECTION("Optional enum")
34+
{
35+
fast_test_coding_case_v2<simple18::templates_description> test_case;
36+
simple18::Test_1 test_1;
37+
simple18::Test_1_mref test_1_mref = test_1.mref();
38+
test_1_mref.set_MDStatisticStatus().as_Inactive();
39+
test_1_mref.set_MDStatisticFrequencyUnit().as_Minutes();
40+
REQUIRE(test_case.encoding(test_1.cref(),"\xe0\x81\x81\x83",true));
41+
REQUIRE(test_case.decoding("\xe0\x81\x81\x83",test_1.cref(),true));
42+
}
43+
}

0 commit comments

Comments
 (0)