Skip to content

Commit b274b3c

Browse files
fix for constant length sequences
This fixes error which occurs when parsing Eurex messages with mFAST. Eurex schema uses constant length sequences in several places, e.g. in QuoteRequest message: ``` <sequence name="SequenceGroup"> <length name="NoElements" id="111"> <constant value="1"/> </length> <int64 name="Id" id="112"> <copy/> </int64> </sequence> ``` Unfortunately mFAST fails to properly handle that schema. The problem boils down to code decoding sequence instruction: ``` template <typename T> inline void fast_decoder_base::decode_field(const T &ext_ref, sequence_type_tag) { value_storage storage; auto length = ext_ref.set_length(storage); this->visit(length); ``` Here value_storage is not initialized with initial (constant) value. Later, in ``` template <typename T, typename TypeCategory> void fast_decoder_base::decode_field(const T &ext_ref, constant_operator_tag, TypeCategory) { ``` the value for constant field is not copied (and that's what was changed in original patch) and sequence length is considered to be 0 leaving all of sequence payload bytes unparsed.
1 parent b01b6ee commit b274b3c

File tree

4 files changed

+50
-1
lines changed

4 files changed

+50
-1
lines changed

src/mfast/ext_ref.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,12 @@ class ext_mref<sequence_mref, LengthExtRef, ElementExtRef> {
310310
cref_type get() const { return base_; }
311311
is_optional_type optional() const { return is_optional_type(); }
312312
length_type set_length(value_storage &storage) const {
313+
auto* length_inst = base_.instruction()->length_instruction();
313314
field_mref_base length_mref(nullptr, &storage,
314-
base_.instruction()->length_instruction());
315+
length_inst);
316+
// a temporary storage is used for sequence length field
317+
// and we must initialize it properly to support constant operators
318+
length_inst->construct_value(storage, nullptr);
315319
return length_type(length_mref);
316320
}
317321

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ FASTTYPEGEN_TARGET(simple_types6 simple6.xml)
1818
FASTTYPEGEN_TARGET(simple_types7 simple7.xml)
1919
FASTTYPEGEN_TARGET(simple_types8 simple8.xml)
2020
FASTTYPEGEN_TARGET(simple_types9 simple9.xml)
21+
FASTTYPEGEN_TARGET(simple_types10 simple10.xml)
2122

2223

2324
FASTTYPEGEN_TARGET(test_types1 test1.xml test2.xml)
@@ -51,6 +52,7 @@ add_executable (mfast_test
5152
${FASTTYPEGEN_simple_types7_OUTPUTS}
5253
${FASTTYPEGEN_simple_types8_OUTPUTS}
5354
${FASTTYPEGEN_simple_types9_OUTPUTS}
55+
${FASTTYPEGEN_simple_types10_OUTPUTS}
5456
fast_type_gen_test.cpp
5557
dictionary_builder_test.cpp
5658
json_test.cpp

tests/simple10.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version=" 1.0 "?>
2+
<templates xmlns="http://www.fixprotocol.org/ns/template-definition"
3+
templateNs="http://www.fixprotocol.org/ns/templates/sample" ns="http://www.fixprotocol.org/ns/fix">
4+
<template name="Test" id="1">
5+
<sequence name="sequence1">
6+
<length id="110">
7+
<constant value="1"/>
8+
</length>
9+
<int64 name="field1" id="111">
10+
<copy/>
11+
</int64>
12+
</sequence>
13+
</template>
14+
</templates>

tests/simple_coder_test.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "simple7.h"
3535
#include "simple8.h"
3636
#include "simple9.h"
37+
#include "simple10.h"
3738

3839
#include "byte_stream.h"
3940
#include "debug_allocator.h"
@@ -294,3 +295,31 @@ TEST_CASE("test fast coder v2 for a simple template with absent optional fields
294295
REQUIRE(test_case.decoding("\xA0\x81\x83\x80\x84\x82\x80", msg_ref));
295296
}
296297

298+
299+
TEST_CASE("test fast decoder for a simple template with constant len in a sequence", "[constant_sequence_length_test]")
300+
{
301+
fast_coding_test_case<simple10::templates_description> test_case;
302+
303+
debug_allocator alloc;
304+
simple10::Test msg(&alloc);
305+
simple10::Test_mref msg_ref = msg.mref();
306+
307+
simple10::Test_mref::sequence1_mref seq(msg_ref.set_sequence1());
308+
seq.resize(1);
309+
seq[0].set_field1().as(10);
310+
311+
// The value of a constant field is never transferred.
312+
// ...
313+
// A field will not occupy any bit in the presence map if it is mandatory and has the constant operator.
314+
// ...
315+
// The default, copy, and increment operators have the following presence map and NULL utilization:
316+
// - Mandatory integer, decimal, string and byte vector fields – one bit. If set, the value appears in the stream.
317+
REQUIRE(test_case.encoding(msg_ref,
318+
// pmap | elem1 pmap | f1 |
319+
// 80 C0 8A
320+
"\x80\xC0\x8A"
321+
));
322+
323+
REQUIRE(test_case.decoding("\x80\xC0\x8A", msg_ref, true));
324+
}
325+

0 commit comments

Comments
 (0)