Skip to content

Commit 9234790

Browse files
matthiasrichterktf
authored andcommitted
Added support for grouped elements
Further bugfixes and enhancements: - bugfix in the generation of the raw buffer, the element at the page border had been written incorrectly. - correctly treating eof condition the eof condition of the iterator was so far only working if there have not been any extra padding bytes at the end of the buffer - unit testing with page sizes 128, 100, and 89 Note: write functionality not yet implemented for grouped elements * @Class PageParser * Parser for a set of data objects in consecutive memory pages. * * All memory pages have a fixed size and start with a page header. * Depending on the page size and size of the data object, some * objects can be split at the page boundary and have the page header * embedded. * * The class iterator can be used to iterate over the data objects * transparently. * * In addition data elements can be grouped. In that case a group * header comes immediately after the first page header. The header * has to store the number of elements which follow, a getter function * has to be provided to retrieve the number from the header. * * In the most simple case, the group header consists of just one * element of arbitrary integral type, the parser implements a default * getter function to retrieve that number. The parser can be invoked * by simply specifying an integral type as GroupT template parameter. * * Multiple blocks of grouped data elements can be in the group, a block * can wrap over page boundery. A new block of grouped elements can * however only start in a new page right after the page header. * * Usage: ungrouped elements * RawParser<PageHeaderType, N, ElementType> RawParser; * RawParser parser(ptr, size); * for (auto element : parser) { * // do something with element * } * * Usage: grouped elements * RawParser<PageHeaderType, N, ElementType, int> RawParser; * RawParser parser(ptr, size); * for (auto element : parser) { * // do something with element * }
1 parent f4a1445 commit 9234790

2 files changed

Lines changed: 265 additions & 19 deletions

File tree

Algorithm/include/Algorithm/PageParser.h

Lines changed: 172 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,62 @@ namespace o2 {
2626

2727
namespace algorithm {
2828

29+
namespace pageparser {
30+
// a function to extract the number of elements from the group type
31+
// this is the version for all but integral types
32+
template<typename T>
33+
typename std::enable_if<std::is_void<T>::value, size_t>::type
34+
extractNElements(T* v) { return 0;}
35+
36+
// the specialization for integral types
37+
template<typename T>
38+
typename std::enable_if<std::is_integral<T>::value, T>::type
39+
extractNElements(T* v) {return *v;}
40+
41+
template<typename GroupT>
42+
using DefaultGetNElementsFctT = size_t(*)(const GroupT *);
43+
44+
// the default function to extract the number of elements in a group
45+
// where the group header is a single integral type holding number of
46+
// elements
47+
auto defaultGetNElementsFct = [] (const auto* groupdata)
48+
{
49+
using ReturnType = size_t;
50+
using T = typename std::remove_pointer<decltype(groupdata)>::type;
51+
// this default function is only for integral types
52+
static_assert(std::is_integral<T>::value || std::is_void<T>::value,
53+
"A function for extracting the number of elements from the "
54+
"group header must be specified for non-trivial types");
55+
// the default function for trivial integral types means there
56+
// is exactly one number holding the size
57+
return static_cast<ReturnType>(extractNElements(groupdata));
58+
};
59+
60+
template<typename T>
61+
T* alloc() {return new T;}
62+
63+
template<>
64+
void* alloc<void>() {return nullptr;}
65+
66+
template<typename T>
67+
void free(T* ptr) {if (ptr) delete ptr;}
68+
69+
template<>
70+
void free<void>(void*) {}
71+
72+
template<typename T>
73+
size_t sizeofGroupHeader() {return sizeof(T);}
74+
75+
template<>
76+
size_t sizeofGroupHeader<void>() {return 0;}
77+
78+
template<typename T>
79+
void set(T* h, size_t v) { *h = v;}
80+
81+
template<>
82+
void set<void>(void*, size_t) {}
83+
}
84+
2985
/**
3086
* @class PageParser
3187
* Parser for a set of data objects in consecutive memory pages.
@@ -38,25 +94,47 @@ namespace algorithm {
3894
* The class iterator can be used to iterate over the data objects
3995
* transparently.
4096
*
41-
* Usage:
97+
* In addition data elements can be grouped. In that case a group
98+
* header comes immediately after the first page header. The header
99+
* has to store the number of elements which follow, a getter function
100+
* has to be provided to retrieve the number from the header.
101+
*
102+
* In the most simple case, the group header consists of just one
103+
* element of arbitrary integral type, the parser implements a default
104+
* getter function to retrieve that number. The parser can be invoked
105+
* by simply specifying an integral type as GroupT template parameter.
106+
*
107+
* Multiple blocks of grouped data elements can be in the group, a block
108+
* can wrap over page boundery. A new block of grouped elements can
109+
* however only start in a new page right after the page header.
110+
*
111+
* Usage: ungrouped elements
42112
* RawParser<PageHeaderType, N, ElementType> RawParser;
43113
* RawParser parser(ptr, size);
44114
* for (auto element : parser) {
45115
* // do something with element
46116
* }
117+
*
118+
* Usage: grouped elements
119+
* RawParser<PageHeaderType, N, ElementType, int> RawParser;
120+
* RawParser parser(ptr, size);
121+
* for (auto element : parser) {
122+
* // do something with element
123+
* }
47124
*/
48125
template<typename PageHeaderT,
49126
size_t PageSize,
50127
typename ElementT,
51-
typename GroupT = int // later extension to groups of elements
128+
typename GroupT = void,
129+
typename GetNElementsFctT = pageparser::DefaultGetNElementsFctT<GroupT>
52130
>
53131
class PageParser {
54132
public:
55133
using PageHeaderType = PageHeaderT;
56134
using BufferType = unsigned char;
57135
using value_type = ElementT;
58136
using GroupType = GroupT;
59-
using GetNElements = std::function<size_t(const GroupType&)>;
137+
using GetNElements = GetNElementsFctT;
60138
static const size_t page_size = PageSize;
61139

62140
// at the moment an object can only be split among two pages
@@ -67,16 +145,18 @@ class PageParser {
67145
using TargetInPageBuffer = std::true_type;
68146
using SourceInPageBuffer = std::false_type;
69147

148+
70149
PageParser() = delete;
71150
template<typename T>
72151
PageParser(T* buffer, size_t size,
73-
GetNElements getNElementsFct = [] (const GroupType&) {return 0;}
152+
GetNElements getNElementsFct = pageparser::defaultGetNElementsFct
74153
)
75154
: mBuffer(nullptr)
76155
, mBufferIsConst(std::is_const<T>::value)
77156
, mSize(size)
78157
, mGetNElementsFct(getNElementsFct)
79158
, mNPages(size>0?((size-1)/page_size)+1:0)
159+
, mGroupHeader(pageparser::alloc<GroupType>())
80160
{
81161
static_assert(sizeof(T) == sizeof(BufferType),
82162
"buffer required to be byte-type");
@@ -85,7 +165,9 @@ class PageParser {
85165
// that iterator write works only for non-const buffers
86166
mBuffer = const_cast<BufferType*>(buffer);
87167
}
88-
~PageParser() = default;
168+
~PageParser() {
169+
pageparser::free(mGroupHeader);
170+
}
89171

90172
template<typename T>
91173
using IteratorBase = std::iterator<std::forward_iterator_tag, T>;
@@ -107,7 +189,12 @@ class PageParser {
107189
: mParent(parent)
108190
{
109191
mPosition = position;
110-
mNextPosition = mParent->getElement(mPosition, mElement);
192+
size_t argument = mPosition;
193+
if (!mParent->getElement(argument, mElement)) {
194+
// eof, both mPosition and mNextPosition point to buffer end
195+
mPosition = argument;
196+
}
197+
mNextPosition = argument;
111198
backup();
112199
}
113200
~Iterator()
@@ -119,7 +206,12 @@ class PageParser {
119206
SelfType& operator++() {
120207
sync();
121208
mPosition = mNextPosition;
122-
mNextPosition = mParent->getElement(mPosition, mElement);
209+
size_t argument = mPosition;
210+
if (!mParent->getElement(argument, mElement)) {
211+
// eof, both mPosition and mNextPosition point to buffer end
212+
mPosition = argument;
213+
}
214+
mNextPosition = argument;
123215
backup();
124216
return *this;
125217
}
@@ -140,6 +232,10 @@ class PageParser {
140232
return mPosition != rh.mPosition;
141233
}
142234

235+
const GroupType* getGroupHeader() const {
236+
return mParent->getGroupHeader();
237+
}
238+
143239
private:
144240
// sync method for non-const iterator
145241
template< typename U = void >
@@ -173,6 +269,8 @@ class PageParser {
173269

174270
/// set an object at position
175271
size_t setElement(size_t position, const value_type& element) const {
272+
// write functionality not yet implemented for grouped elements
273+
assert(std::is_void<GroupType>::value);
176274
// check if we are at the end
177275
if (position >= mSize) {
178276
assert(position == mSize);
@@ -190,23 +288,78 @@ class PageParser {
190288
return position + copy<TargetInPageBuffer>(source, target, page_size - (position % page_size));
191289
}
192290

291+
template<typename T>
292+
size_t readGroupHeader(size_t position, T * groupHeader) const {
293+
assert((position % page_size) == sizeof(PageHeaderType));
294+
if (std::is_void<T>::value) return 0;
295+
296+
memcpy(groupHeader, mBuffer + position, pageparser::sizeofGroupHeader<T>());
297+
return mGetNElementsFct(groupHeader);
298+
}
299+
193300
/// retrieve an object at position
194-
size_t getElement(size_t position, value_type& element) const {
301+
bool getElement(size_t& position, value_type& element) const {
195302
// check if we are at the end
196303
if (position >= mSize) {
197304
assert(position == mSize);
198-
return mSize;
305+
position = mSize;
306+
return false;
307+
}
308+
309+
// handle group if defined
310+
if (!std::is_void<GroupType>::value) {
311+
if (mNGroupElements == 0) {
312+
// new group has to be read from the buffer
313+
do {
314+
if ((position % page_size) == 0) {
315+
position += sizeof(PageHeaderType);
316+
}
317+
if ((position % page_size) != sizeof(PageHeaderType)) {
318+
// forward to the next page
319+
position += page_size - (position % page_size) + sizeof(PageHeaderType);
320+
if (position > mSize) {
321+
//this is probably a valid condition as the group header can just
322+
//indicate zero clusters
323+
//throw std::runtime_error("");
324+
position = mSize;
325+
return false;
326+
}
327+
}
328+
const_cast<PageParser*>(this)->mNGroupElements = readGroupHeader(position, mGroupHeader);
329+
position += pageparser::sizeofGroupHeader<GroupType>();
330+
} while (mNGroupElements == 0);
331+
332+
size_t nPages = 0;
333+
size_t required = pageparser::sizeofGroupHeader<GroupType>() + mNGroupElements * sizeof(value_type);
334+
do {
335+
// the block of elements can go beyond the current page, find out
336+
// how many additional pages are required
337+
required += sizeof(PageHeaderType);
338+
++nPages;
339+
} while (required > nPages * page_size);
340+
required -= sizeof(PageHeaderType) + pageparser::sizeofGroupHeader<GroupType>();
341+
if (position + required > mSize) {
342+
throw std::runtime_error("format error: the number of group elements "
343+
"does not fit into the remaining buffer");
344+
}
345+
}
346+
// now we will read one element
347+
const_cast<PageParser*>(this)->mNGroupElements -= 1;;
199348
}
200349

201350
// check if there is space for one element
202351
if (position + sizeof(value_type) > mSize) {
203-
// format error, probably throw exception
204-
return mSize;
352+
// FIXME: not sure if this is considered an error condition if
353+
// no groups are used, i.e. the buffer should have the correct size
354+
// and no extra space after the last element
355+
position = mSize;
356+
return false;
205357
}
206358

207359
auto source = mBuffer + position;
208360
auto target = reinterpret_cast<BufferType*>(&element);
209-
return position + copy<SourceInPageBuffer>(source, target, page_size - (position % page_size));
361+
position += copy<SourceInPageBuffer>(source, target, page_size - (position % page_size));
362+
return true;
210363
}
211364

212365
// copy data, depending on compile time switch, either source or target
@@ -248,6 +401,10 @@ class PageParser {
248401
return position;
249402
}
250403

404+
const GroupType getGroupHeader() const {
405+
return mGroupHeader;
406+
}
407+
251408
using iterator = Iterator<value_type>;
252409
using const_iterator = Iterator<const value_type>;
253410

@@ -277,8 +434,10 @@ class PageParser {
277434
BufferType* mBuffer = nullptr;
278435
bool mBufferIsConst = false;
279436
size_t mSize = 0;
280-
GetNElements mGetNElementsFct;
437+
GetNElements mGetNElementsFct = nullptr;
281438
size_t mNPages = 0;
439+
GroupType* mGroupHeader = nullptr;
440+
size_t mNGroupElements = 0;
282441
};
283442

284443
}

0 commit comments

Comments
 (0)