COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
Bitfield.h
1//
2// Copyright 2015 - 2024 (C). Alex Robenko. All rights reserved.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
8#pragma once
9
10#include <type_traits>
11#include <limits>
12
13#include "comms/util/Tuple.h"
14#include "comms/util/SizeToType.h"
15#include "comms/util/access.h"
17#include "comms/field/details/FieldOpHelpers.h"
18#include "comms/field/details/MembersVersionDependency.h"
19#include "comms/field/tag.h"
20#include "comms/Assert.h"
21#include "comms/ErrorStatus.h"
22
24#include "CommonFuncs.h"
25
26namespace comms
27{
28
29namespace field
30{
31
32namespace basic
33{
34
35namespace details
36{
37
38template <typename TSerializedType>
39class BitfieldReadHelper
40{
41public:
42 BitfieldReadHelper(TSerializedType val, ErrorStatus& es)
43 : value_(val),
44 es_(es)
45 {
46 }
47
48 template <std::size_t TIdx, typename TFieldParam>
49 void operator()(TFieldParam&& field)
50 {
51 if (es_ != comms::ErrorStatus::Success) {
52 return;
53 }
54
55 using FieldType = typename std::decay<decltype(field)>::type;
56 static const auto FieldBitLength =
57 comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
58 static const auto Mask =
59 (static_cast<TSerializedType>(1) << FieldBitLength) - 1;
60
61 auto fieldSerValue =
62 static_cast<TSerializedType>((value_ >> pos_) & Mask);
63
64 pos_ += FieldBitLength;
65
66 static_assert(FieldType::minLength() == FieldType::maxLength(),
67 "Bitfield doesn't support members with variable length");
68
69 static const std::size_t MaxLength = FieldType::maxLength();
70 std::uint8_t buf[MaxLength];
71 auto* writeIter = &buf[0];
72 using FieldEndian = typename FieldType::Endian;
73 comms::util::writeData<MaxLength>(fieldSerValue, writeIter, FieldEndian());
74
75 const auto* readIter = &buf[0];
76 es_ = field.read(readIter, MaxLength);
77 }
78
79private:
80 TSerializedType value_ = TSerializedType();
81 ErrorStatus& es_;
82 std::size_t pos_ = 0U;
83};
84
85template <typename TSerializedType>
86class BitfieldReadNoStatusHelper
87{
88public:
89 BitfieldReadNoStatusHelper(TSerializedType val)
90 : value_(val) {}
91
92 template <std::size_t TIdx, typename TFieldParam>
93 void operator()(TFieldParam&& field)
94 {
95 using FieldType = typename std::decay<decltype(field)>::type;
96 static const auto FieldBitLength =
97 comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
98 static const auto Mask =
99 (static_cast<TSerializedType>(1) << FieldBitLength) - 1;
100
101 auto fieldSerValue =
102 static_cast<TSerializedType>((value_ >> pos_) & Mask);
103 pos_ += FieldBitLength;
104
105 static_assert(FieldType::minLength() == FieldType::maxLength(),
106 "Bitfield doesn't support members with variable length");
107
108 static const std::size_t MaxLength = FieldType::maxLength();
109 std::uint8_t buf[MaxLength];
110 auto* writeIter = &buf[0];
111 using FieldEndian = typename FieldType::Endian;
112 comms::util::writeData<MaxLength>(fieldSerValue, writeIter, FieldEndian());
113
114 const auto* readIter = &buf[0];
115 field.readNoStatus(readIter);
116 }
117
118private:
119 TSerializedType value_ = TSerializedType();
120 std::size_t pos_ = 0U;
121};
122
123template <typename TSerializedType>
124class BitfieldWriteHelper
125{
126public:
127 BitfieldWriteHelper(TSerializedType& val, ErrorStatus& es)
128 : value_(val),
129 es_(es) {}
130
131 template <std::size_t TIdx, typename TFieldParam>
132 void operator()(TFieldParam&& field)
133 {
134 if (es_ != comms::ErrorStatus::Success) {
135 return;
136 }
137
138 using FieldType = typename std::decay<decltype(field)>::type;
139
140 static_assert(FieldType::minLength() == FieldType::maxLength(),
141 "Bitfield supports fixed length members only.");
142
143 static const std::size_t MaxLength = FieldType::maxLength();
144 std::uint8_t buf[MaxLength];
145 auto* writeIter = &buf[0];
146 es_ = field.write(writeIter, MaxLength);
147 if (es_ != comms::ErrorStatus::Success) {
148 return;
149 }
150
151 using FieldEndian = typename FieldType::Endian;
152 const auto* readIter = &buf[0];
153 auto fieldSerValue = comms::util::readData<TSerializedType, MaxLength>(readIter, FieldEndian());
154
155 static const auto FieldBitLength =
156 comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
157 static const auto Mask =
158 (static_cast<TSerializedType>(1) << FieldBitLength) - 1;
159
160 static const auto ClearMask = static_cast<TSerializedType>(~(Mask << pos_));
161
162 auto valueMask =
163 static_cast<TSerializedType>(
164 (static_cast<TSerializedType>(fieldSerValue) & Mask) << pos_);
165
166 value_ &= ClearMask;
167 value_ |= valueMask;
168 pos_ += FieldBitLength;
169 }
170
171
172private:
173 TSerializedType& value_;
174 ErrorStatus& es_;
175 std::size_t pos_ = 0U;
176};
177
178template <typename TSerializedType>
179class BitfieldWriteNoStatusHelper
180{
181public:
182 BitfieldWriteNoStatusHelper(TSerializedType& val)
183 : value_(val) {}
184
185 template <std::size_t TIdx, typename TFieldParam>
186 void operator()(TFieldParam&& field)
187 {
188
189 using FieldType = typename std::decay<decltype(field)>::type;
190
191 static_assert(FieldType::minLength() == FieldType::maxLength(),
192 "Bitfield supports fixed length members only.");
193
194 static const std::size_t MaxLength = FieldType::maxLength();
195 std::uint8_t buf[MaxLength];
196 auto* writeIter = &buf[0];
197 field.writeNoStatus(writeIter);
198
199 using FieldEndian = typename FieldType::Endian;
200 const auto* readIter = &buf[0];
201 auto fieldSerValue = comms::util::readData<TSerializedType, MaxLength>(readIter, FieldEndian());
202
203 static const auto FieldBitLength =
204 comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
205
206 static const auto Mask =
207 (static_cast<TSerializedType>(1) << FieldBitLength) - 1;
208
209 static const auto ClearMask = static_cast<TSerializedType>(~(Mask << pos_));
210
211 auto valueMask =
212 static_cast<TSerializedType>(
213 (static_cast<TSerializedType>(fieldSerValue) & Mask) << pos_);
214
215 value_ &= ClearMask;
216 value_ |= valueMask;
217 pos_ += FieldBitLength;
218 }
219
220private:
221 TSerializedType& value_;
222 std::size_t pos_ = 0U;
223};
224
225template <comms::field::details::MembersVersionDependency TVersionDependency, typename... TMembers>
226struct BitfieldVersionDependencyDetectHelper;
227
228template <typename... TMembers>
229struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_NotSpecified, TMembers...>
230{
231 static constexpr bool Value = CommonFuncs::IsAnyFieldVersionDependentBoolType<TMembers...>::value;
232};
233
234template <typename... TMembers>
235struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_Independent, TMembers...>
236{
237 static constexpr bool Value = false;
238};
239
240template <typename... TMembers>
241struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_Dependent, TMembers...>
242{
243 static constexpr bool Value = true;
244};
245
246} // namespace details
247
248template <typename TFieldBase, comms::field::details::MembersVersionDependency TVersionDependency, typename TMembers>
249class Bitfield;
250
251template <typename TFieldBase, comms::field::details::MembersVersionDependency TVersionDependency, typename... TMembers>
252class Bitfield<TFieldBase, TVersionDependency, std::tuple<TMembers...> > : public TFieldBase
253{
254 using BaseImpl = TFieldBase;
255 using Members = std::tuple<TMembers...>;
256
257 static_assert(
258 1U < std::tuple_size<Members>::value,
259 "Number of members is expected to be at least 2.");
260
261
262 static const std::size_t TotalBits = CommonFuncs::FieldSumTotalBitLengthIntType<TMembers...>::value;
263 static_assert(
264 (TotalBits % std::numeric_limits<std::uint8_t>::digits) == 0,
265 "Wrong number of total bits");
266
267 static const std::size_t Length = TotalBits / std::numeric_limits<std::uint8_t>::digits;
268 static_assert(0U < Length, "Serialised length is expected to be greater than 0");
269 using SerializedType = typename comms::util::SizeToType<Length, false>::Type;
270
271 using FixedIntValueField =
273 TFieldBase,
274 SerializedType,
276 >;
277
278
279 using SimpleIntValueField =
281 TFieldBase,
282 SerializedType
283 >;
284
285 using IntValueField =
287 ((Length & (Length - 1)) == 0)
288 >::template Type<
289 SimpleIntValueField,
290 FixedIntValueField
291 >;
292
293public:
294 using Endian = typename BaseImpl::Endian;
295 using VersionType = typename BaseImpl::VersionType;
296 using ValueType = Members;
297 using CommsTag = comms::field::tag::Bitfield;
298
299 Bitfield() = default;
300 explicit Bitfield(const ValueType& val)
301 : members_(val)
302 {
303 }
304
305 explicit Bitfield(ValueType&& val)
306 : members_(std::move(val))
307 {
308 }
309
310 const ValueType& value() const
311 {
312 return members_;
313 }
314
315 ValueType& value()
316 {
317 return members_;
318 }
319
320 const ValueType& getValue() const
321 {
322 return value();
323 }
324
325 template <typename T>
326 void setValue(T&& val)
327 {
328 value() = std::forward<T>(val);
329 }
330
331 static constexpr std::size_t length()
332 {
333 return Length;
334 }
335
336 static constexpr std::size_t minLength()
337 {
338 return length();
339 }
340
341 static constexpr std::size_t maxLength()
342 {
343 return length();
344 }
345
346 template <typename TIter>
347 ErrorStatus read(TIter& iter, std::size_t size)
348 {
349 if (size < length()) {
351 }
352
353 auto serValue = BaseImpl::template readData<SerializedType, Length>(iter);
356 members_, details::BitfieldReadHelper<SerializedType>(serValue, es));
357 return es;
358 }
359
360 static constexpr bool hasReadNoStatus()
361 {
362 return CommonFuncs::AllFieldsHaveReadNoStatusBoolType<TMembers...>::value;
363 }
364
365 template <typename TIter>
366 void readNoStatus(TIter& iter)
367 {
368 auto serValue = BaseImpl::template readData<SerializedType, Length>(iter);
370 members_, details::BitfieldReadNoStatusHelper<SerializedType>(serValue));
371 }
372
373 bool canWrite() const
374 {
375 return
377 value(), true, comms::field::details::FieldCanWriteCheckHelper<>());
378 }
379
380 template <typename TIter>
381 ErrorStatus write(TIter& iter, std::size_t size) const
382 {
383 if (size < length()) {
385 }
386
387 SerializedType serValue = 0;
390 members_, details::BitfieldWriteHelper<SerializedType>(serValue, es));
391 if (es == ErrorStatus::Success) {
392 comms::util::writeData<Length>(serValue, iter, Endian());
393 }
394 return es;
395 }
396
397 static constexpr bool hasWriteNoStatus()
398 {
399 return CommonFuncs::AllFieldsHaveWriteNoStatusBoolType<TMembers...>::value;
400 }
401
402 template <typename TIter>
403 void writeNoStatus(TIter& iter) const
404 {
405 SerializedType serValue = 0;
407 members_, details::BitfieldWriteNoStatusHelper<SerializedType>(serValue));
408 comms::util::writeData<Length>(serValue, iter, Endian());
409 }
410
411 constexpr bool valid() const
412 {
413 return comms::util::tupleAccumulate(value(), true, comms::field::details::FieldValidCheckHelper<>());
414 }
415
416 bool refresh()
417 {
419 members_, false, comms::field::details::FieldRefreshHelper<>());
420 }
421
422 template <std::size_t TIdx>
423 static constexpr std::size_t memberBitLength()
424 {
425 static_assert(
426 TIdx < std::tuple_size<ValueType>::value,
427 "Index exceeds number of fields");
428
429 using FieldType = typename std::tuple_element<TIdx, ValueType>::type;
430 return comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
431 }
432
433 static constexpr bool isVersionDependent()
434 {
435 return details::BitfieldVersionDependencyDetectHelper<TVersionDependency, TMembers...>::Value;
436 }
437
438 static constexpr bool hasNonDefaultRefresh()
439 {
440 return CommonFuncs::AnyFieldHasNonDefaultRefreshBoolType<TMembers...>::value;
441 }
442
443 bool setVersion(VersionType version)
444 {
445 return CommonFuncs::setVersionForMembers(value(), version);
446 }
447
448protected:
449 using BaseImpl::readData;
450 using BaseImpl::writeData;
451
452private:
453 ValueType members_;
454};
455
456} // namespace basic
457
458} // namespace field
459
460} // namespace comms
461
462
This file contains classes required for generic custom assertion functionality.
This file contain definition of error statuses used by comms module.
Contains definition of comms::field::IntValue.
Contains various tuple type manipulation classes and functions.
Contains functions for raw data access / (de)serialization.
Field that represent integral value.
Definition IntValue.h:72
Contains definition of various tag classes.
comms::option::def::VersionType< T > VersionType
Same as comms::option::def::VersionType.
Definition options.h:1797
comms::option::def::FieldType< TMsg > FieldType
Same as comms::option::def::FieldType.
Definition options.h:1463
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1438
void tupleForEachWithTemplateParamIdx(TTuple &&tuple, TFunc &&func)
Invoke provided functor for every element in the tuple while providing information about element inde...
Definition Tuple.h:520
constexpr TValue tupleAccumulate(TTuple &&tuple, const TValue &value, TFunc &&func)
Performs "accumulate" algorithm on every element of the tuple.
Definition Tuple.h:586
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:17
@ Success
Used to indicate successful outcome of the operation.
constexpr unsigned version()
Version of the COMMS library as single numeric value.
Definition version.h:64
STL namespace.
Option used to specify number of bytes that is used for field serialisation.
Definition options.h:280
Replacement to std::conditional.
Definition type_traits.h:28
Replacement to some types from standard type_traits.