Skip to content

Commit 3ea8a19

Browse files
Adding BitstreamReader utility
some minor compiler warnings fixed
1 parent bb7ea09 commit 3ea8a19

5 files changed

Lines changed: 411 additions & 2 deletions

File tree

Algorithm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(TEST_SRCS
4545
test/pageparser.cxx
4646
test/test_mpl_tools.cxx
4747
test/test_RangeTokenizer.cxx
48+
test/test_BitstreamReader.cxx
4849
)
4950

5051
O2_GENERATE_TESTS(
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
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 BITSTREAMREADER_H
12+
#define BITSTREAMREADER_H
13+
14+
/// @file BitstreamReader.h
15+
/// @author Matthias Richter
16+
/// @since 2019-06-05
17+
/// @brief Utility class to provide bitstream access to an underlying resource
18+
19+
#include <type_traits>
20+
#include <bitset>
21+
22+
namespace o2
23+
{
24+
namespace algorithm
25+
{
26+
27+
/// @class BitStreamReader
28+
/// @brief Utility class to provide bitstream access to an underlying resource
29+
///
30+
/// Allows to access bits of variable length, supports integral types and also
31+
/// bitsets as target type. At the moment, the access is in direction MSB -> LSB.
32+
///
33+
/// BitstreamReader<uint8_t> reader(start, end);
34+
/// while (reader.good() && not reader.eof()) {
35+
/// // get an 8 bit value from the stream, moves the position
36+
/// uint8_t ivalue;
37+
/// reader.get(ivalue);
38+
///
39+
/// // get a 13 bit bitset without moving the position
40+
/// std::bitset<13> value;
41+
/// reader.peek(value, value.size());
42+
/// // e.g. use 7 bits of the data
43+
/// value >>= value.size() - 7;
44+
/// // move position by the specific number of bits
45+
/// reader.seek(7);
46+
/// }
47+
template <typename BufferType>
48+
class BitstreamReader
49+
{
50+
public:
51+
using self_type = BitstreamReader<BufferType>;
52+
// for the moment we simply use pointers, but with some traits this can be extended to
53+
// containers
54+
using value_type = BufferType;
55+
using iterator = const value_type*;
56+
static constexpr size_t value_size = sizeof(value_type) * 8;
57+
BitstreamReader() = delete;
58+
BitstreamReader(iterator start, iterator end)
59+
: mStart(start), mEnd(end), mCurrent(mStart), mBitPosition(value_size)
60+
{
61+
}
62+
~BitstreamReader() = default;
63+
64+
/// Check reader's state
65+
/// @return true if not in error state
66+
bool good() const
67+
{
68+
return mBitPosition > 0;
69+
}
70+
71+
/// Indicates end of data
72+
/// @return true if end of resource is reached
73+
bool eof() const
74+
{
75+
return mCurrent == mEnd && mBitPosition > 0;
76+
}
77+
78+
/// Reset the reader, start over at beginning
79+
void reset()
80+
{
81+
mCurrent = mStart;
82+
mBitPosition = value_size;
83+
}
84+
85+
/// Get the next N bits without moving the read position
86+
/// if bitlength is smaller than the size of data type, result is aligned to LSB
87+
/// TODO: this also works nicely for bitsets, but then the bitlength has to be specified
88+
/// as template parameter, want to do a specific overload, but needs more work to catch
89+
/// all cases.
90+
/// @param v target variable passed by reference
91+
/// @return number of poked bits
92+
template <typename T, size_t N = sizeof(T) * 8>
93+
size_t peek(T& v)
94+
{
95+
static_assert(N <= sizeof(T) * 8);
96+
return peek<T, false>(v, N);
97+
}
98+
99+
/// Get the next n bits without moving the read position
100+
/// if bitlength is smaller than the size of data type, result is aligned to LSB
101+
/// @param v target variable passed by reference
102+
/// @param bitlength number of bits to read
103+
/// @return number of poked bits
104+
template <typename T>
105+
size_t peek(T& v, size_t bitlength)
106+
{
107+
return peek<T, true>(v, bitlength);
108+
}
109+
110+
/// Move read position
111+
/// @param bitlength move count in number of bits
112+
void seek(size_t bitlength)
113+
{
114+
while (good() && bitlength > 0 && mCurrent != mEnd) {
115+
if (bitlength >= mBitPosition) {
116+
bitlength -= mBitPosition;
117+
mBitPosition = 0;
118+
} else {
119+
mBitPosition -= bitlength;
120+
bitlength = 0;
121+
}
122+
if (mBitPosition == 0) {
123+
mCurrent++;
124+
mBitPosition = value_size;
125+
}
126+
}
127+
128+
if (bitlength > 0) {
129+
mBitPosition = 0;
130+
}
131+
}
132+
133+
/// Get the next n bits and move the read position
134+
template <typename T, size_t N = sizeof(T) * 8>
135+
T get()
136+
{
137+
T result;
138+
peek<T, N>(result);
139+
seek(N);
140+
return result;
141+
}
142+
143+
/// Get the next n and move the read position
144+
template <typename T>
145+
T get(size_t bitlength = sizeof(T) * 8)
146+
{
147+
T result;
148+
peek<T>(result, bitlength);
149+
seek(bitlength);
150+
return result;
151+
}
152+
153+
/// @class Bits
154+
/// @brief Helper class to get value of specified type which holds the number used bits
155+
///
156+
/// The class holds both the extracted value access via peek method and the number of used
157+
/// bits. The reader will be incremented when the object is destroyed.
158+
/// The number of bits can be adjusted by using markUsed method
159+
template <typename FieldType, size_t N = sizeof(FieldType) * 8, typename ParentType = self_type>
160+
class Bits
161+
{
162+
public:
163+
using field_type = FieldType;
164+
static_assert(N <= sizeof(FieldType) * 8);
165+
Bits()
166+
: mParent(nullptr), mData(0), mLength(0)
167+
{
168+
}
169+
Bits(ParentType* parent, FieldType&& data)
170+
: mParent(parent), mData(std::move(data)), mLength(N)
171+
{
172+
}
173+
Bits(Bits&& other)
174+
: mParent(other.mParent), mData(std::move(other.mData)), mLength(other.mLength)
175+
{
176+
other.mParent = nullptr;
177+
other.mLength = 0;
178+
}
179+
180+
~Bits()
181+
{
182+
if (mParent) {
183+
mParent->seek(mLength);
184+
}
185+
}
186+
187+
auto& operator=(Bits<FieldType, N, ParentType>&& other)
188+
{
189+
mParent = other.mParent;
190+
mData = std::move(other.mData);
191+
mLength = other.mLength;
192+
other.mParent = nullptr;
193+
other.mLength = 0;
194+
195+
return *this;
196+
}
197+
198+
FieldType& operator*()
199+
{
200+
return mData;
201+
}
202+
203+
void markUsed(size_t length)
204+
{
205+
mLength = length;
206+
}
207+
208+
private:
209+
ParentType* mParent;
210+
FieldType mData;
211+
size_t mLength;
212+
};
213+
214+
/// Read an integral value from the stream
215+
template <typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
216+
self_type& operator>>(T& target)
217+
{
218+
target = get<T>();
219+
return *this;
220+
}
221+
222+
/// Read a bitstream value from the stream
223+
template <size_t N>
224+
self_type& operator>>(std::bitset<N>& target)
225+
{
226+
target = get<std::bitset<N>, N>();
227+
return *this;
228+
}
229+
230+
/// Read a Bits object from the stream
231+
template <typename T>
232+
self_type& operator>>(Bits<T>& target)
233+
{
234+
T bitfield;
235+
peek<T>(bitfield);
236+
target = std::move(Bits<T>(this, std::move(bitfield)));
237+
return *this;
238+
}
239+
240+
private:
241+
/// The internal peek method
242+
template <typename T, bool RuntimeCheck>
243+
size_t peek(T& result, size_t bitlength)
244+
{
245+
if constexpr (RuntimeCheck) {
246+
// the runtime check is disabled if bitlength is derived at compile time
247+
if (bitlength > sizeof(T) * 8) {
248+
throw std::length_error(std::string("requested bit length ") + std::to_string(bitlength) + " does not fit size of result data type " + std::to_string(sizeof(T) * 8));
249+
}
250+
}
251+
result = 0;
252+
size_t bitsToWrite = bitlength;
253+
auto current = mCurrent;
254+
auto bitsAvailable = mBitPosition;
255+
while (bitsToWrite > 0 && current != mEnd) {
256+
// extract available bits
257+
value_type mask = ~value_type(0) >> (value_size - bitsAvailable);
258+
if (bitsToWrite >= bitsAvailable) {
259+
T value = (*current & mask) << (bitsToWrite - bitsAvailable);
260+
result |= value;
261+
bitsToWrite -= bitsAvailable;
262+
bitsAvailable = 0;
263+
} else {
264+
value_type value = (*current & mask) >> (bitsAvailable - bitsToWrite);
265+
result |= value;
266+
bitsAvailable -= bitsToWrite;
267+
bitsToWrite = 0;
268+
}
269+
if (bitsAvailable == 0) {
270+
current++;
271+
bitsAvailable = value_size;
272+
}
273+
}
274+
275+
return bitlength - bitsToWrite;
276+
}
277+
278+
/// start of resource
279+
iterator mStart;
280+
/// end of resource
281+
iterator mEnd;
282+
/// current position in resource
283+
iterator mCurrent;
284+
/// bit position in current element
285+
size_t mBitPosition;
286+
};
287+
} // namespace algorithm
288+
} // namespace o2
289+
#endif

Algorithm/include/Algorithm/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class ForwardParser {
157157
"ForwardParser currently only supports byte type buffer"
158158
);
159159
if (buffer == nullptr || bufferSize == 0) return 0;
160-
auto position = 0;
160+
size_t position = 0;
161161
std::vector<FrameInfo> frames;
162162
do {
163163
FrameInfo entry;

Algorithm/include/Algorithm/RangeTokenizer.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ struct RangeTokenizer {
6666
std::string token;
6767
std::vector<T> res;
6868
while (std::getline(stream, token, ',')) {
69-
T value;
7069
if (std::is_integral<T>::value && token.find('-') != token.npos) {
7170
// extract range
7271
if constexpr (std::is_integral<T>::value) { // c++17 compile time

0 commit comments

Comments
 (0)