@@ -26,6 +26,62 @@ namespace o2 {
2626
2727namespace 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 */
48125template <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 >
53131class PageParser {
54132public:
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