-
Notifications
You must be signed in to change notification settings - Fork 490
Expand file tree
/
Copy pathParser.h
More file actions
403 lines (362 loc) · 13.7 KB
/
Parser.h
File metadata and controls
403 lines (362 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.
#ifndef ALGORITHM_PARSER_H
#define ALGORITHM_PARSER_H
/// @file Parser.h
/// @author Matthias Richter
/// @since 2017-09-20
/// @brief Utilities for parsing of data sequences
#include <functional>
#include <vector>
namespace o2
{
namespace algorithm
{
/// helper function returning size of type with a specialization for
/// void returning 0
template <typename T>
struct typesize {
static const size_t size = sizeof(T);
};
// specialization for void
template <>
struct typesize<void> {
static const size_t size = 0;
};
/**
* @class ForwardParser
* Parser for a sequence of frames with header, trailer and variable payload.
* The size is expected to be part of the header.
*
* Trailer type can be void, which is also the default template parameter. That
* allows to define a frame consisting of only header and data.
*
* Usage:
* <pre>
* using SomeParser = ForwardParser<SomeHeaderType, SomeTrailerType>;
* SomeParser parser;
* std::vector<typename SomeParser::FrameInfo> frames;
* parser.parse(ptr, size,
* [] (const typename SomeParser::HeaderType& h) {
* // check the header
* return true;
* },
* [] (const typename SomeParser::TrailerType& t) {
* // check the trailer
* return true;
* },
* [] (const typename SomeParser::HeaderType& h) {
* // get the size of the frame including payload
* // and header and trailer size, e.g. payload size
* // from a header member
* return h.payloadSize + SomeParser::totalOffset;
* },
* [&frames] (typename SomeParser::FrameInfo& info) {
* frames.emplace_back(info);
* return true;
* }
* )
*
* // a reduced version without trailer check callback
* using SomeParser = ForwardParser<SomeHeaderType>;
* SomeParser parser;
* std::vector<typename SomeParser::FrameInfo> frames;
* parser.parse(ptr, size,
* [] (const typename SomeParser::HeaderType& h) {
* // check the header
* return true;
* },
* [] (const typename SomeParser::HeaderType& h) {
* // get the size of the frame including payload
* // and header and trailer size, e.g. payload size
* // from a header member
* return h.payloadSize + SomeParser::totalOffset;
* },
* [&frames] (typename SomeParser::FrameInfo& info) {
* frames.emplace_back(info);
* return true;
* }
* )
* </pre>
*/
template <typename HeaderT,
typename TrailerT = void>
class ForwardParser
{
public:
using HeaderType = HeaderT;
using TrailerType = TrailerT;
using PayloadType = unsigned char;
/// @struct FrameInfo
/// a compound of header, data, and trailer
struct FrameInfo {
using PtrT = const PayloadType*;
const HeaderType* header = nullptr;
const TrailerType* trailer = nullptr;
PtrT payload = nullptr;
size_t length = 0;
};
/// the length offset due to header
static const size_t headOffset = typesize<HeaderType>::size;
/// the length offset due to trailer
static const size_t tailOffset = typesize<TrailerType>::size;
/// total length offset due to header and trailer
static const size_t totalOffset = headOffset + tailOffset;
/// alias for callback checking the header, return true if the object
/// is a valid header
using CheckHeaderFct = std::function<bool(const HeaderType&)>;
/// alias for the argument type to be used in the CheckTrailer function
/// have to forward to a valid type in case of void TrailerType in order
/// to allow passing by reference
using CheckTrailerFctArgumentT = typename std::conditional<
!std::is_void<TrailerType>::value, TrailerType, int>::type;
/// alias for callback checking the trailer, takes reference to trailer
/// object if TrailerType is a valid type, no argument otherwise
template <typename U>
using CheckTrailerFct = typename std::conditional<
!std::is_void<U>::value,
std::function<bool(const CheckTrailerFctArgumentT&)>,
std::function<bool()>>::type;
/// alias for callback to get the complete frame size including header,
/// trailer and the data
using GetFrameSizeFct = std::function<size_t(const HeaderType&)>;
/// function callback to insert/handle one frame into, sequentially called
/// for all frames if the whole block has a valid format
using InsertFct = std::function<bool(FrameInfo&)>;
/// Parse buffer of size bufferSize, requires callbacks to check header
/// trailer, the frame size, and insert callback to handle a FrameInfo
/// object.
template <typename InputType>
int parse(const InputType* buffer, size_t bufferSize,
CheckHeaderFct checkHeader,
CheckTrailerFct<TrailerType> checkTrailer,
GetFrameSizeFct getFrameSize,
InsertFct insert)
{
static_assert(sizeof(InputType) == 1,
"ForwardParser currently only supports byte type buffer");
if (buffer == nullptr || bufferSize == 0) {
return 0;
}
size_t position = 0;
std::vector<FrameInfo> frames;
do {
FrameInfo entry;
// check the header
if (sizeof(HeaderType) + position > bufferSize) {
break;
}
entry.header = reinterpret_cast<const HeaderType*>(buffer + position);
if (!checkHeader(*entry.header)) {
break;
}
// extract frame size from header, this is expected to be the
// total frome size including header, payload and optional trailer
auto frameSize = getFrameSize(*entry.header);
if (frameSize + position > bufferSize) {
break;
}
// payload starts right after the header
entry.payload = reinterpret_cast<typename FrameInfo::PtrT>(entry.header + 1);
entry.length = frameSize - totalOffset;
// optionally extract and check trailer
if (tailOffset > 0) {
entry.trailer = nullptr;
} else {
auto trailerStart = buffer + position + frameSize - tailOffset;
entry.trailer = reinterpret_cast<const TrailerType*>(trailerStart);
if (!CheckTrailer(entry, checkTrailer)) {
break;
}
}
// store the extracted frame info and continue with remaining buffer
frames.emplace_back(entry);
position += frameSize;
} while (position < bufferSize);
if (position == bufferSize) {
// frames found and format consistent, insert entries to target
// Note: the complete block must be consistent
for (auto entry : frames) {
if (!insert(entry)) {
break;
}
}
return frames.size();
} else if (frames.size() == 0) {
// no frames found at all, the buffer does not contain any
return 0;
}
// format error detected
// TODO: decide about error policy
return -1;
}
/// Parse buffer of size bufferSize, specialization skipping the trailer
/// check, e.g. when its type is void, or when the integrity of the trailer
/// is not relevant. Requires callbacks to check header, frame size, and
/// insert callback to handle a FrameInfo object.
template <typename InputType, typename U = TrailerType>
typename std::enable_if<std::is_void<U>::value, int>::type
parse(const InputType* buffer, size_t bufferSize,
CheckHeaderFct checkHeader,
GetFrameSizeFct getFrameSize,
InsertFct insert)
{
auto checkTrailer = []() { return true; };
return parse(buffer, bufferSize, checkHeader, checkTrailer, getFrameSize, insert);
}
private:
/// internal function to check the trailer, distinguishes void and non-void
/// trailer type.
template <typename U = TrailerType>
typename std::enable_if<!std::is_void<U>::value, bool>::type
CheckTrailer(const FrameInfo& entry, CheckTrailerFct<TrailerType>& checkTrailer) const
{
return checkTrailer(*entry.trailer);
}
template <typename U = TrailerType>
typename std::enable_if<std::is_void<U>::value, bool>::type
CheckTrailer(const FrameInfo&, CheckTrailerFct<TrailerType>&) const
{
return true;
}
};
/**
* @class ReverseParser
* Parser for a sequence of frames with header, trailer and variable payload.
* The size is expected to be part of the trailer, the parsing is thus in
* reverse direction. Also the insert callback is called with the entries
* starting form the end of the buffer.
* TODO: an easy extension can be to reverse the order of the inserts, meaning
* that the entries are read from the beginning.
*
* Usage:
* <pre>
* using SomeParser = ReverseParser<SomeHeaderType, SomeTrailerType>;
* SomeParser parser;
* std::vector<typename SomeParser::FrameInfo> frames;
* parser.parse(ptr, size,
* [] (const typename SomeParser::HeaderType& h) {
* // check the header
* return true;
* },
* [] (const typename SomeParser::TrailerType& t) {
* // check the trailer
* return true;
* },
* [] (const typename SomeParser::TrailerType& t) {
* // get the size of the frame including payload
* // and header and trailer size, e.g. payload size
* // from a trailer member
* return t.payloadSize + SomeParser::totalOffset;
* },
* [&frames] (typename SomeParser::FrameInfo& info) {
* frames.emplace_back(info);
* return true;
* }
* )
* </pre>
*/
template <typename HeaderT, typename TrailerT>
class ReverseParser
{
public:
using HeaderType = HeaderT;
using TrailerType = TrailerT;
using PayloadType = unsigned char;
/// @struct FrameInfo a compound of header, data, and trailer
struct FrameInfo {
using PtrT = const PayloadType*;
const HeaderType* header = nullptr;
const TrailerType* trailer = nullptr;
PtrT payload = nullptr;
size_t length = 0;
};
/// the length offset due to header
static const size_t headOffset = typesize<HeaderType>::size;
/// the length offset due to trailer
static const size_t tailOffset = typesize<TrailerType>::size;
/// total length offset due to header and trailer
static const size_t totalOffset = headOffset + tailOffset;
/// alias for callback checking the header, return true if the object
/// is a valid header
using CheckHeaderFct = std::function<bool(const HeaderType&)>;
/// alias for callback checking the trailer
using CheckTrailerFct = std::function<bool(const TrailerType&)>;
/// alias for callback to get the complete frame size including header,
/// trailer and the data
using GetFrameSizeFct = std::function<size_t(const TrailerType&)>;
/// function callback to insert/handle one frame into, sequentially called
/// for all frames if the whole block has a valid format
using InsertFct = std::function<bool(const FrameInfo&)>;
/// Parse buffer of size bufferSize, requires callbacks to check header
/// trailer, the frame size, and insert callback to handle a FrameInfo
/// object.
template <typename InputType>
int parse(const InputType* buffer, size_t bufferSize,
CheckHeaderFct checkHeader,
CheckTrailerFct checkTrailer,
GetFrameSizeFct getFrameSize,
InsertFct insert)
{
static_assert(sizeof(InputType) == 1,
"ReverseParser currently only supports byte type buffer");
if (buffer == nullptr || bufferSize == 0) {
return 0;
}
auto position = bufferSize;
std::vector<FrameInfo> frames;
do {
FrameInfo entry;
// start from end, extract and check trailer
if (sizeof(TrailerType) > position) {
break;
}
entry.trailer = reinterpret_cast<const TrailerType*>(buffer + position - sizeof(TrailerType));
if (!checkTrailer(*entry.trailer)) {
break;
}
// get the total frame size
auto frameSize = getFrameSize(*entry.trailer);
if (frameSize > position) {
break;
}
// extract and check header
auto headerStart = buffer + position - frameSize;
entry.header = reinterpret_cast<const HeaderType*>(headerStart);
if (!checkHeader(*entry.header)) {
break;
}
// payload immediately after header
entry.payload = reinterpret_cast<typename FrameInfo::PtrT>(entry.header + 1);
entry.length = frameSize - sizeof(HeaderType) - sizeof(TrailerType);
frames.emplace_back(entry);
position -= frameSize;
} while (position > 0);
if (position == 0) {
// frames found and format consistent, the complete block must be consistent
for (auto entry : frames) {
if (!insert(entry)) {
break;
}
}
return frames.size();
} else if (frames.size() == 0) {
// no frames found at all, the buffer does not contain any
return 0;
}
// format error detected
// TODO: decide about error policy
return -1;
}
};
} // namespace algorithm
} // namespace o2
#endif // ALGORITHM_PARSER_H