Skip to content

Commit 49f6fcb

Browse files
Support void trailer type, i.e. frame consisting of only header and data
The trailer type as second template argument of the ForwardParser is now optional, 'parse' function can drop trailer check function in that case. Adding further test cases for format error and non-consistent format.
1 parent b646a7d commit 49f6fcb

File tree

4 files changed

+232
-13
lines changed

4 files changed

+232
-13
lines changed

Algorithm/include/Algorithm/Parser.h

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,36 @@ class ForwardParser {
8686
static const size_t tailOffset = typesize<TrailerType>::size;
8787
static const size_t totalOffset = headOffset + tailOffset;
8888

89+
// alias for callback checking the header, return true if the object
90+
// is a valid header
8991
using CheckHeaderFct = std::function<bool(const HeaderType&)>;
90-
using CheckTrailerFct = std::function<bool(const TrailerType&)>;
92+
93+
// alias for the argument type to be used in the CheckTrailer function
94+
// have to forward to a valid type in case of void TrailerType in order
95+
// to allow passing by reference
96+
using CheckTrailerFctArgumentT = typename std::conditional<
97+
!std::is_void<TrailerType>::value, TrailerType, int>::type;
98+
99+
// alias for callback checking the trailer, takes reference to trailer
100+
// object if TrailerType is a valid type, no argument otherwise
101+
template <typename U>
102+
using CheckTrailerFct = typename std::conditional<
103+
!std::is_void<U>::value,
104+
std::function<bool(const CheckTrailerFctArgumentT&)>,
105+
std::function<bool()>>::type;
106+
107+
// alias for callback to get the complete frame size including header,
108+
// trailer and the data
91109
using GetFrameSizeFct = std::function<size_t(const HeaderType& )>;
110+
111+
// function callback to insert/handle one frame into, sequentially called
112+
// for all frames if the whole block has a valid format
92113
using InsertFct = std::function<bool(FrameInfo&)>;
93114

94115
template<typename InputType>
95116
int parse(const InputType* buffer, size_t bufferSize,
96117
CheckHeaderFct checkHeader,
97-
CheckTrailerFct checkTrailer,
118+
CheckTrailerFct<TrailerType> checkTrailer,
98119
GetFrameSizeFct getFrameSize,
99120
InsertFct insert) {
100121
static_assert(sizeof(InputType) == 1,
@@ -126,7 +147,7 @@ class ForwardParser {
126147
} else {
127148
auto trailerStart = buffer + position + frameSize - tailOffset;
128149
entry.trailer = reinterpret_cast<const TrailerType*>(trailerStart);
129-
if (!checkTrailer(*entry.trailer)) break;
150+
if (!CheckTrailer(entry, checkTrailer)) break;
130151
}
131152

132153
// store the extracted frame info and continue with remaining buffer
@@ -150,6 +171,29 @@ class ForwardParser {
150171
// TODO: decide about error policy
151172
return -1;
152173
}
174+
175+
template<typename InputType, typename U = TrailerType>
176+
typename std::enable_if<std::is_void<U>::value, int>::type
177+
parse(const InputType* buffer, size_t bufferSize,
178+
CheckHeaderFct checkHeader,
179+
GetFrameSizeFct getFrameSize,
180+
InsertFct insert) {
181+
auto checkTrailer = [] () {return true;};
182+
return parse(buffer, bufferSize, checkHeader, checkTrailer, getFrameSize, insert);
183+
}
184+
185+
private:
186+
template <typename U = TrailerType>
187+
typename std::enable_if<!std::is_void<U>::value, bool>::type
188+
CheckTrailer(const FrameInfo& entry, CheckTrailerFct<TrailerType>& checkTrailer) const {
189+
return checkTrailer(*entry.trailer);
190+
}
191+
192+
template <typename U = TrailerType>
193+
typename std::enable_if<std::is_void<U>::value, bool>::type
194+
CheckTrailer(const FrameInfo&, CheckTrailerFct<TrailerType>&) const {
195+
return true;
196+
}
153197
};
154198

155199
/**
@@ -198,9 +242,11 @@ class ReverseParser {
198242
PtrT payload = nullptr;
199243
size_t length = 0;
200244
};
201-
// the length offset due to header and trailer
245+
/// the length offset due to header
202246
static const size_t headOffset = typesize<HeaderType>::size;
247+
/// the length offset due to trailer
203248
static const size_t tailOffset = typesize<TrailerType>::size;
249+
/// total length offset due to header and trailer
204250
static const size_t totalOffset = headOffset + tailOffset;
205251

206252
using CheckHeaderFct = std::function<bool(const HeaderType&)>;

Algorithm/test/StaticSequenceAllocator.h

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,38 @@ namespace algorithm {
2121
* and a trailer
2222
*/
2323
template <typename HeaderT
24-
, typename TrailerT>
24+
, typename TrailerT = void>
2525
struct Composite {
2626
using HeaderType = HeaderT;
2727
using TrailerType = TrailerT;
2828
size_t compositeLength = 0;
29+
size_t trailerLength = 0;
2930
size_t dataLength = 0;
3031

31-
template<size_t N>
32-
constexpr Composite(const HeaderType& h, const char (&d)[N], const TrailerType& t)
32+
template<size_t N,
33+
typename U = TrailerType>
34+
constexpr Composite(const HeaderType h, const char (&d)[N],
35+
typename std::conditional<!std::is_void<U>::value, const TrailerType, int>::type t,
36+
typename std::enable_if<!std::is_void<U>::value>::type* = nullptr)
3337
: header(h)
3438
, data(d)
3539
, trailer(t)
3640
{
3741
dataLength = N;
38-
compositeLength = sizeof(HeaderType) + dataLength + sizeof(TrailerType);
42+
trailerLength = sizeof(TrailerType);
43+
compositeLength = sizeof(HeaderType) + dataLength + trailerLength;
44+
}
45+
46+
template<size_t N,
47+
typename U = TrailerType>
48+
constexpr Composite(const HeaderType& h, const char (&d)[N],
49+
typename std::enable_if<std::is_void<U>::value>::type* = nullptr)
50+
: header(h)
51+
, data(d)
52+
{
53+
dataLength = N;
54+
trailerLength = 0;
55+
compositeLength = sizeof(HeaderType) + dataLength + trailerLength;
3956
}
4057

4158
constexpr size_t getLength() const noexcept {
@@ -54,14 +71,16 @@ struct Composite {
5471
length += sizeof(HeaderType);
5572
memcpy(buffer + length, data, dataLength);
5673
length += dataLength;
57-
memcpy(buffer + length, &trailer, sizeof(TrailerType));
58-
length += sizeof(TrailerType);
74+
if (trailerLength > 0) {
75+
memcpy(buffer + length, &trailer, trailerLength);
76+
length += trailerLength;
77+
}
5978
return length;
6079
}
6180

62-
const HeaderType& header;
81+
const HeaderType header;
6382
const char* data = nullptr;
64-
const TrailerType& trailer;
83+
typename std::conditional<!std::is_void<TrailerType>::value, const TrailerType, int>::type trailer;
6584
};
6685

6786
/// recursively calculate the length of the sequence

Algorithm/test/parser.cxx

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ BOOST_AUTO_TEST_CASE(test_forwardparser_header_and_trailer)
6868
return trailer.identifier == 0xaaffee00;
6969
};
7070
auto getFrameSize = [] (const typename ParserT::HeaderType& header) {
71+
// frame size includes total offset from header and trailer
7172
return header.payloadSize + ParserT::totalOffset;
7273
};
7374

@@ -93,7 +94,126 @@ BOOST_AUTO_TEST_CASE(test_forwardparser_header_and_trailer)
9394
BOOST_CHECK(memcmp(frames[2].payload, "dummydata", frames[2].length) == 0);
9495
}
9596

96-
// TODO: make a test of a composite with header and payload
97+
BOOST_AUTO_TEST_CASE(test_forwardparser_header_and_void_trailer)
98+
{
99+
using FrameT = o2::algorithm::Composite<Header>;
100+
// note: the length of the data is set in the header word
101+
using TestFrame = o2::algorithm::StaticSequenceAllocator;
102+
TestFrame tf(FrameT(16, "lotsofsillydata"),
103+
FrameT(5, "test"),
104+
FrameT(10, "dummydata")
105+
);
106+
107+
using ParserT = o2::algorithm::ForwardParser<typename FrameT::HeaderType,
108+
typename FrameT::TrailerType>;
109+
110+
auto checkHeader = [] (const typename FrameT::HeaderType& header) {
111+
return header.identifier == 0xdeadbeef;
112+
};
113+
114+
auto getFrameSize = [] (const typename ParserT::HeaderType& header) {
115+
// frame size includes total offset from header and trailer
116+
return header.payloadSize + ParserT::totalOffset;
117+
};
118+
119+
std::vector<typename ParserT::FrameInfo> frames;
120+
auto insert = [&frames] (typename ParserT::FrameInfo& info) {
121+
frames.emplace_back(info);
122+
return true;
123+
};
124+
125+
ParserT parser;
126+
auto result = parser.parse(tf.buffer.get(), tf.size(),
127+
checkHeader,
128+
getFrameSize,
129+
insert
130+
);
131+
132+
BOOST_REQUIRE(result == 3);
133+
BOOST_REQUIRE(frames.size() == 3);
134+
135+
BOOST_CHECK(memcmp(frames[0].payload, "lotsofsillydata", frames[0].length) == 0);
136+
BOOST_CHECK(memcmp(frames[1].payload, "test", frames[1].length) == 0);
137+
BOOST_CHECK(memcmp(frames[2].payload, "dummydata", frames[2].length) == 0);
138+
}
139+
140+
BOOST_AUTO_TEST_CASE(test_forwardparser_no_frames)
141+
{
142+
using FrameT = o2::algorithm::Composite<Header>;
143+
// note: the length of the data is set in the header word
144+
using TestFrame = o2::algorithm::StaticSequenceAllocator;
145+
TestFrame tf(FrameT(16, "lotsofsillydata"),
146+
FrameT(5, "test"),
147+
FrameT(10, "dummydata")
148+
);
149+
150+
using ParserT = o2::algorithm::ForwardParser<typename FrameT::HeaderType,
151+
typename FrameT::TrailerType>;
152+
153+
auto checkHeader = [] (const typename FrameT::HeaderType& header) {
154+
// simply indicate invalid header to read no frames
155+
return false;
156+
};
157+
158+
auto getFrameSize = [] (const typename ParserT::HeaderType& header) {
159+
// frame size includes total offset from header and trailer
160+
return header.payloadSize + ParserT::totalOffset;
161+
};
162+
163+
std::vector<typename ParserT::FrameInfo> frames;
164+
auto insert = [&frames] (typename ParserT::FrameInfo& info) {
165+
frames.emplace_back(info);
166+
return true;
167+
};
168+
169+
ParserT parser;
170+
auto result = parser.parse(tf.buffer.get(), tf.size(),
171+
checkHeader,
172+
getFrameSize,
173+
insert
174+
);
175+
176+
// check that there are really no frames found
177+
BOOST_REQUIRE(result == 0);
178+
}
179+
180+
BOOST_AUTO_TEST_CASE(test_forwardparser_format_error)
181+
{
182+
using FrameT = o2::algorithm::Composite<Header>;
183+
// note: the length of the data is set in the header word
184+
using TestFrame = o2::algorithm::StaticSequenceAllocator;
185+
TestFrame tf(FrameT(16, "lotsofsillydata"),
186+
FrameT(4, "test"), // <- note wrong size
187+
FrameT(10, "dummydata")
188+
);
189+
190+
using ParserT = o2::algorithm::ForwardParser<typename FrameT::HeaderType,
191+
typename FrameT::TrailerType>;
192+
193+
auto checkHeader = [] (const typename FrameT::HeaderType& header) {
194+
return header.identifier == 0xdeadbeef;
195+
};
196+
197+
auto getFrameSize = [] (const typename ParserT::HeaderType& header) {
198+
// frame size includes total offset from header and trailer
199+
return header.payloadSize + ParserT::totalOffset;
200+
};
201+
202+
std::vector<typename ParserT::FrameInfo> frames;
203+
auto insert = [&frames] (typename ParserT::FrameInfo& info) {
204+
frames.emplace_back(info);
205+
return true;
206+
};
207+
208+
ParserT parser;
209+
auto result = parser.parse(tf.buffer.get(), tf.size(),
210+
checkHeader,
211+
getFrameSize,
212+
insert
213+
);
214+
215+
BOOST_REQUIRE(result == -1);
216+
}
97217

98218
BOOST_AUTO_TEST_CASE(test_reverseparser)
99219
{

Algorithm/test/tableview.cxx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,37 @@ BOOST_AUTO_TEST_CASE(test_tableview_reverse)
120120
BOOST_CHECK(rowidx == requiredNofRowsInColumn[colidx]);
121121
}
122122
}
123+
124+
BOOST_AUTO_TEST_CASE(test_tableview_formaterror)
125+
{
126+
using FrameT = o2::algorithm::Composite<HeartbeatHeader, HeartbeatTrailer>;
127+
using TestFrame = o2::algorithm::StaticSequenceAllocator;
128+
// note: the length of the data is set in the trailer word
129+
// specifying wrong length in the second entry, no frames should be added
130+
TestFrame tf1(FrameT({0x1100000000000000}, "heartbeatdata", {0x510000000000000e}),
131+
FrameT({0x1100000000000001}, "test", {0x5100000000000004}),
132+
FrameT({0x1100000000000003}, "dummydata", {0x510000000000000a})
133+
);
134+
135+
// the payload length is set in the trailer, so we need a reverse parser
136+
using ParserT = o2::algorithm::ReverseParser<typename FrameT::HeaderType,
137+
typename FrameT::TrailerType>;
138+
139+
// define the view type for DataHeader as row descriptor,
140+
// HeartbeatHeader as column descriptor and the reverse parser
141+
using ViewType = o2::algorithm::TableView<o2::header::DataHeader,
142+
o2::header::HeartbeatHeader,
143+
ParserT>;
144+
ViewType heartbeatview;
145+
146+
o2::Header::DataHeader dh;
147+
dh.dataDescription = o2::Header::DataDescription("FIRSTSLOT");
148+
dh.dataOrigin = o2::Header::DataOrigin("TST");
149+
dh.subSpecification = 0;
150+
dh.payloadSize = 0;
151+
152+
heartbeatview.addRow(dh, tf1.buffer.get(), tf1.size());
153+
154+
BOOST_CHECK(heartbeatview.getNRows() == 0);
155+
BOOST_CHECK(heartbeatview.getNColumns() == 0);
156+
}

0 commit comments

Comments
 (0)