Skip to content

Commit 2e537f0

Browse files
matthiasrichtersawenzel
authored andcommitted
Adding parser for a set of data objects in consecutive memory pages (follow up AliceO2Group#585) (AliceO2Group#629)
* Adding parser for a set of data objects in consecutive memory pages Memory pages have a fixed size and start with a page header. The parser provides transparent iteration through data objects and handles page boundaries. * Adding write functionality for parser iterator - bidirectional copy function - runtype check for the buffer const'ness - supporting iterator and const_iterator - fixing the assignment operator problem on xcode, now internally using pointer instead of reference. Using reference was resulting in making and assigning a copy.
1 parent 8773588 commit 2e537f0

File tree

3 files changed

+453
-0
lines changed

3 files changed

+453
-0
lines changed

Algorithm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ set(TEST_SRCS
4141
test/headerstack.cxx
4242
test/parser.cxx
4343
test/tableview.cxx
44+
test/pageparser.cxx
4445
)
4546

4647
O2_GENERATE_TESTS(
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#ifndef ALGORITHM_PAGEPARSER_H
12+
#define ALGORITHM_PAGEPARSER_H
13+
14+
/// @file PageParser.h
15+
/// @author Matthias Richter
16+
/// @since 2017-09-27
17+
/// @brief Parser for a set of data objects in consecutive memory pages.
18+
19+
#include <functional>
20+
#include <vector>
21+
#include <cassert>
22+
#include <type_traits>
23+
#include <stdexcept>
24+
25+
namespace o2 {
26+
27+
namespace algorithm {
28+
29+
/**
30+
* @class PageParser
31+
* Parser for a set of data objects in consecutive memory pages.
32+
*
33+
* All memory pages have a fixed size and start with a page header.
34+
* Depending on the page size and size of the data object, some
35+
* objects can be split at the page boundary and have the page header
36+
* embedded.
37+
*
38+
* The class iterator can be used to iterate over the data objects
39+
* transparently.
40+
*
41+
* Usage:
42+
* RawParser<PageHeaderType, N, ElementType> RawParser;
43+
* RawParser parser(ptr, size);
44+
* for (auto element : parser) {
45+
* // do something with element
46+
* }
47+
*/
48+
template<typename PageHeaderT,
49+
size_t PageSize,
50+
typename ElementT,
51+
typename GroupT = int // later extension to groups of elements
52+
>
53+
class PageParser {
54+
public:
55+
using PageHeaderType = PageHeaderT;
56+
using BufferType = unsigned char;
57+
using value_type = ElementT;
58+
using GroupType = GroupT;
59+
using GetNElements = std::function<size_t(const GroupType&)>;
60+
static const size_t page_size = PageSize;
61+
62+
// at the moment an object can only be split among two pages
63+
static_assert(PageSize >= sizeof(PageHeaderType) + sizeof(value_type),
64+
"Page Header and at least one element have to fit into page");
65+
66+
// switches for the copy method, used to skip ill-formed expressions
67+
using TargetInPageBuffer = std::true_type;
68+
using SourceInPageBuffer = std::false_type;
69+
70+
PageParser() = delete;
71+
template<typename T>
72+
PageParser(T* buffer, size_t size,
73+
GetNElements getNElementsFct = [] (const GroupType&) {return 0;}
74+
)
75+
: mBuffer(nullptr)
76+
, mBufferIsConst(std::is_const<T>::value)
77+
, mSize(size)
78+
, mGetNElementsFct(getNElementsFct)
79+
, mNPages(size>0?((size-1)/page_size)+1:0)
80+
{
81+
static_assert(sizeof(T) == sizeof(BufferType),
82+
"buffer required to be byte-type");
83+
84+
// the buffer pointer is stored non-const, a runtime check ensures
85+
// that iterator write works only for non-const buffers
86+
mBuffer = const_cast<BufferType*>(buffer);
87+
}
88+
~PageParser() = default;
89+
90+
template<typename T>
91+
using IteratorBase = std::iterator<std::forward_iterator_tag, T>;
92+
93+
template<typename T>
94+
class Iterator : public IteratorBase<T> {
95+
public:
96+
using ParentType = PageParser;
97+
using SelfType = Iterator;
98+
using value_type = typename IteratorBase<T>::value_type;
99+
using reference = typename IteratorBase<T>::reference;
100+
using pointer = typename IteratorBase<T>::pointer;
101+
using ElementType = typename std::remove_const<value_type>::type;
102+
103+
104+
Iterator() = delete;
105+
106+
Iterator(ParentType const * parent, size_t position = 0)
107+
: mParent(parent)
108+
{
109+
mPosition = position;
110+
mNextPosition = mParent->getElement(mPosition, mElement);
111+
backup();
112+
}
113+
~Iterator()
114+
{
115+
sync();
116+
}
117+
118+
// prefix increment
119+
SelfType& operator++() {
120+
sync();
121+
mPosition = mNextPosition;
122+
mNextPosition = mParent->getElement(mPosition, mElement);
123+
backup();
124+
return *this;
125+
}
126+
// postfix increment
127+
SelfType operator++(int /*unused*/) {
128+
SelfType copy(*this); operator++(); return copy;
129+
}
130+
// return reference
131+
reference operator*() {
132+
return mElement;
133+
}
134+
// comparison
135+
bool operator==(const SelfType& rh) {
136+
return mPosition == rh.mPosition;
137+
}
138+
// comparison
139+
bool operator!=(const SelfType& rh) {
140+
return mPosition != rh.mPosition;
141+
}
142+
143+
private:
144+
// sync method for non-const iterator
145+
template< typename U = void >
146+
typename std::enable_if< !std::is_const<value_type>::value, U >::type sync() {
147+
if (std::memcmp(&mElement, &mBackup, sizeof(value_type)) != 0) {
148+
// mElement is changed, sync to buffer
149+
mParent->setElement(mPosition, mElement);
150+
}
151+
}
152+
153+
// overload for const_iterator, empty function body
154+
template< typename U = void >
155+
typename std::enable_if< std::is_const<value_type>::value, U >::type sync() {}
156+
157+
// backup for non-const iterator
158+
template< typename U = void >
159+
typename std::enable_if< !std::is_const<value_type>::value, U >::type backup() {
160+
mBackup = mElement;
161+
}
162+
163+
// overload for const_iterator, empty function body
164+
template< typename U = void >
165+
typename std::enable_if< std::is_const<value_type>::value, U >::type backup() {}
166+
167+
int mPosition;
168+
int mNextPosition;
169+
ParentType const * mParent;
170+
ElementType mElement;
171+
ElementType mBackup;
172+
};
173+
174+
/// set an object at position
175+
size_t setElement(size_t position, const value_type& element) const {
176+
// check if we are at the end
177+
if (position >= mSize) {
178+
assert(position == mSize);
179+
return mSize;
180+
}
181+
182+
// check if there is space for one element
183+
if (position + sizeof(value_type) > mSize) {
184+
// format error, probably throw exception
185+
return mSize;
186+
}
187+
188+
auto source = reinterpret_cast<const BufferType*>(&element);
189+
auto target = mBuffer + position;
190+
return position + copy<TargetInPageBuffer>(source, target, page_size - (position % page_size));
191+
}
192+
193+
/// retrieve an object at position
194+
size_t getElement(size_t position, value_type& element) const {
195+
// check if we are at the end
196+
if (position >= mSize) {
197+
assert(position == mSize);
198+
return mSize;
199+
}
200+
201+
// check if there is space for one element
202+
if (position + sizeof(value_type) > mSize) {
203+
// format error, probably throw exception
204+
return mSize;
205+
}
206+
207+
auto source = mBuffer + position;
208+
auto target = reinterpret_cast<BufferType*>(&element);
209+
return position + copy<SourceInPageBuffer>(source, target, page_size - (position % page_size));
210+
}
211+
212+
// copy data, depending on compile time switch, either source or target
213+
// pointer are treated as pointer in the raw page, i.e. can be additionally
214+
// incremented by the page header
215+
template<typename SwitchT>
216+
size_t copy(const BufferType* source, BufferType* target, size_t pageCapacity) const
217+
{
218+
size_t position = 0;
219+
auto copySize = sizeof(value_type);
220+
// choose which of the pointers needs additional PageHeader offsets
221+
auto pageOffsetTarget = SwitchT::value? &target : const_cast<BufferType**>(&source);
222+
if (pageCapacity == page_size) {
223+
// skip the page header at beginning of page
224+
position += sizeof(PageHeaderType);
225+
pageCapacity -= sizeof(PageHeaderType);
226+
*pageOffsetTarget += sizeof(PageHeaderType);
227+
}
228+
if (copySize > pageCapacity) {
229+
// object is split at the page boundary, copy the part
230+
// in the current page first
231+
copySize = pageCapacity;
232+
}
233+
if (copySize > 0) {
234+
memcpy(target, source, copySize);
235+
position += copySize;
236+
source += copySize;
237+
target += copySize;
238+
}
239+
copySize = sizeof(value_type) - copySize;
240+
if (copySize > 0) {
241+
// skip page header at beginning of new page and copy
242+
// remaining part of the element
243+
position += sizeof(PageHeaderType);
244+
*pageOffsetTarget += sizeof(PageHeaderType);
245+
memcpy(target, source, copySize);
246+
position += copySize;
247+
}
248+
return position;
249+
}
250+
251+
using iterator = Iterator<value_type>;
252+
using const_iterator = Iterator<const value_type>;
253+
254+
const_iterator begin() const {
255+
return const_iterator(this, 0);
256+
}
257+
258+
const_iterator end() const {
259+
return const_iterator(this, mSize);
260+
}
261+
262+
iterator begin() {
263+
if (mBufferIsConst) {
264+
// did not find a way to do this at compile time in the constructor,
265+
// probably one needs to make the buffer type a template parameter
266+
// to the class
267+
throw std::runtime_error("the underlying buffer is not writeable");
268+
}
269+
return iterator(this, 0);
270+
}
271+
272+
iterator end() {
273+
return iterator(this, mSize);
274+
}
275+
276+
private:
277+
BufferType* mBuffer = nullptr;
278+
bool mBufferIsConst = false;
279+
size_t mSize = 0;
280+
GetNElements mGetNElementsFct;
281+
size_t mNPages = 0;
282+
};
283+
284+
}
285+
}
286+
287+
#endif

0 commit comments

Comments
 (0)