COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
Bitfield.h
1//
2// Copyright 2015 - 2025 (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 "comms/Assert.h"
11#include "comms/ErrorStatus.h"
12#include "comms/field/basic/CommonFuncs.h"
13#include "comms/field/details/FieldOpHelpers.h"
14#include "comms/field/details/MembersVersionDependency.h"
16#include "comms/field/tag.h"
17#include "comms/util/access.h"
18#include "comms/util/SizeToType.h"
19#include "comms/util/Tuple.h"
21
22#include <limits>
23#include <type_traits>
24#include <utility>
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 : m_value(val),
44 m_es(es)
45 {
46 }
47
48 template <std::size_t TIdx, typename TFieldParam>
49 void operator()(TFieldParam&& field)
50 {
51 if (m_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>((m_value >> m_pos) & Mask);
63
64 m_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 m_es = field.read(readIter, MaxLength);
77 }
78
79private:
80 TSerializedType m_value = TSerializedType();
81 ErrorStatus& m_es;
82 std::size_t m_pos = 0U;
83};
84
85template <typename TSerializedType>
86class BitfieldReadNoStatusHelper
87{
88public:
89 BitfieldReadNoStatusHelper(TSerializedType val)
90 : m_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>((m_value >> m_pos) & Mask);
103 m_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 m_value = TSerializedType();
120 std::size_t m_pos = 0U;
121};
122
123template <typename TSerializedType>
124class BitfieldWriteHelper
125{
126public:
127 BitfieldWriteHelper(TSerializedType& val, ErrorStatus& es)
128 : m_value(val),
129 m_es(es) {}
130
131 template <std::size_t TIdx, typename TFieldParam>
132 void operator()(TFieldParam&& field)
133 {
134 if (m_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 m_es = field.write(writeIter, MaxLength);
147 if (m_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 << m_pos));
161
162 auto valueMask =
163 static_cast<TSerializedType>(
164 (static_cast<TSerializedType>(fieldSerValue) & Mask) << m_pos);
165
166 m_value &= ClearMask;
167 m_value |= valueMask;
168 m_pos += FieldBitLength;
169 }
170
171private:
172 TSerializedType& m_value;
173 ErrorStatus& m_es;
174 std::size_t m_pos = 0U;
175};
176
177template <typename TSerializedType>
178class BitfieldWriteNoStatusHelper
179{
180public:
181 BitfieldWriteNoStatusHelper(TSerializedType& val)
182 : m_value(val) {}
183
184 template <std::size_t TIdx, typename TFieldParam>
185 void operator()(TFieldParam&& field)
186 {
187
188 using FieldType = typename std::decay<decltype(field)>::type;
189
190 static_assert(FieldType::minLength() == FieldType::maxLength(),
191 "Bitfield supports fixed length members only.");
192
193 static const std::size_t MaxLength = FieldType::maxLength();
194 std::uint8_t buf[MaxLength];
195 auto* writeIter = &buf[0];
196 field.writeNoStatus(writeIter);
197
198 using FieldEndian = typename FieldType::Endian;
199 const auto* readIter = &buf[0];
200 auto fieldSerValue = comms::util::readData<TSerializedType, MaxLength>(readIter, FieldEndian());
201
202 static const auto FieldBitLength =
203 comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
204
205 static const auto Mask =
206 (static_cast<TSerializedType>(1) << FieldBitLength) - 1;
207
208 static const auto ClearMask = static_cast<TSerializedType>(~(Mask << m_pos));
209
210 auto valueMask =
211 static_cast<TSerializedType>(
212 (static_cast<TSerializedType>(fieldSerValue) & Mask) << m_pos);
213
214 m_value &= ClearMask;
215 m_value |= valueMask;
216 m_pos += FieldBitLength;
217 }
218
219private:
220 TSerializedType& m_value;
221 std::size_t m_pos = 0U;
222};
223
224template <comms::field::details::MembersVersionDependency TVersionDependency, typename... TMembers>
225struct BitfieldVersionDependencyDetectHelper;
226
227template <typename... TMembers>
228struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_NotSpecified, TMembers...>
229{
230 static constexpr bool Value = CommonFuncs::IsAnyFieldVersionDependentBoolType<TMembers...>::value;
231};
232
233template <typename... TMembers>
234struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_Independent, TMembers...>
235{
236 static constexpr bool Value = false;
237};
238
239template <typename... TMembers>
240struct BitfieldVersionDependencyDetectHelper<comms::field::details::MembersVersionDependency_Dependent, TMembers...>
241{
242 static constexpr bool Value = true;
243};
244
245} // namespace details
246
247template <typename TFieldBase, comms::field::details::MembersVersionDependency TVersionDependency, typename TMembers>
248class Bitfield;
249
250template <typename TFieldBase, comms::field::details::MembersVersionDependency TVersionDependency, typename... TMembers>
251class Bitfield<TFieldBase, TVersionDependency, std::tuple<TMembers...> > : public TFieldBase
252{
253 using BaseImpl = TFieldBase;
254 using Members = std::tuple<TMembers...>;
255
256 static_assert(
257 1U < std::tuple_size<Members>::value,
258 "Number of members is expected to be at least 2.");
259
260 static const std::size_t TotalBits = CommonFuncs::FieldSumTotalBitLengthIntType<TMembers...>::value;
261 static_assert(
262 (TotalBits % std::numeric_limits<std::uint8_t>::digits) == 0,
263 "Wrong number of total bits");
264
265 static const std::size_t Length = TotalBits / std::numeric_limits<std::uint8_t>::digits;
266 static_assert(0U < Length, "Serialised length is expected to be greater than 0");
267 using SerializedType = typename comms::util::SizeToType<Length, false>::Type;
268
269 using FixedIntValueField =
271 TFieldBase,
272 SerializedType,
274 >;
275
276 using SimpleIntValueField =
278 TFieldBase,
279 SerializedType
280 >;
281
282 using IntValueField =
284 ((Length & (Length - 1)) == 0)
285 >::template Type<
286 SimpleIntValueField,
287 FixedIntValueField
288 >;
289
290public:
291 using Endian = typename BaseImpl::Endian;
292 using VersionType = typename BaseImpl::VersionType;
293 using ValueType = Members;
294 using CommsTag = comms::field::tag::Bitfield;
295
296 Bitfield() = default;
297 explicit Bitfield(const ValueType& val)
298 : m_members(val)
299 {
300 }
301
302 explicit Bitfield(ValueType&& val)
303 : m_members(std::move(val))
304 {
305 }
306
307 const ValueType& value() const
308 {
309 return m_members;
310 }
311
312 ValueType& value()
313 {
314 return m_members;
315 }
316
317 const ValueType& getValue() const
318 {
319 return value();
320 }
321
322 template <typename T>
323 void setValue(T&& val)
324 {
325 value() = std::forward<T>(val);
326 }
327
328 static constexpr std::size_t length()
329 {
330 return Length;
331 }
332
333 static constexpr std::size_t minLength()
334 {
335 return length();
336 }
337
338 static constexpr std::size_t maxLength()
339 {
340 return length();
341 }
342
343 template <typename TIter>
344 ErrorStatus read(TIter& iter, std::size_t size)
345 {
346 if (size < length()) {
348 }
349
350 auto serValue = BaseImpl::template readData<SerializedType, Length>(iter);
353 m_members, details::BitfieldReadHelper<SerializedType>(serValue, es));
354 return es;
355 }
356
357 static constexpr bool hasReadNoStatus()
358 {
359 return CommonFuncs::AllFieldsHaveReadNoStatusBoolType<TMembers...>::value;
360 }
361
362 template <typename TIter>
363 void readNoStatus(TIter& iter)
364 {
365 auto serValue = BaseImpl::template readData<SerializedType, Length>(iter);
367 m_members, details::BitfieldReadNoStatusHelper<SerializedType>(serValue));
368 }
369
370 bool canWrite() const
371 {
372 return
374 value(), true, comms::field::details::FieldCanWriteCheckHelper<>());
375 }
376
377 template <typename TIter>
378 ErrorStatus write(TIter& iter, std::size_t size) const
379 {
380 if (size < length()) {
382 }
383
384 SerializedType serValue = 0;
387 m_members, details::BitfieldWriteHelper<SerializedType>(serValue, es));
388 if (es == ErrorStatus::Success) {
389 comms::util::writeData<Length>(serValue, iter, Endian());
390 }
391 return es;
392 }
393
394 static constexpr bool hasWriteNoStatus()
395 {
396 return CommonFuncs::AllFieldsHaveWriteNoStatusBoolType<TMembers...>::value;
397 }
398
399 template <typename TIter>
400 void writeNoStatus(TIter& iter) const
401 {
402 SerializedType serValue = 0;
404 m_members, details::BitfieldWriteNoStatusHelper<SerializedType>(serValue));
405 comms::util::writeData<Length>(serValue, iter, Endian());
406 }
407
408 constexpr bool valid() const
409 {
410 return comms::util::tupleAccumulate(value(), true, comms::field::details::FieldValidCheckHelper<>());
411 }
412
413 bool refresh()
414 {
416 m_members, false, comms::field::details::FieldRefreshHelper<>());
417 }
418
419 template <std::size_t TIdx>
420 static constexpr std::size_t memberBitLength()
421 {
422 static_assert(
423 TIdx < std::tuple_size<ValueType>::value,
424 "Index exceeds number of fields");
425
426 using FieldType = typename std::tuple_element<TIdx, ValueType>::type;
427 return comms::util::FieldBitLengthIntType<>::template Type<FieldType>::value;
428 }
429
430 static constexpr bool isVersionDependent()
431 {
432 return details::BitfieldVersionDependencyDetectHelper<TVersionDependency, TMembers...>::Value;
433 }
434
435 static constexpr bool hasNonDefaultRefresh()
436 {
437 return CommonFuncs::AnyFieldHasNonDefaultRefreshBoolType<TMembers...>::value;
438 }
439
440 bool setVersion(VersionType version)
441 {
442 return CommonFuncs::setVersionForMembers(value(), version);
443 }
444
445protected:
446 using BaseImpl::readData;
447 using BaseImpl::writeData;
448
449private:
450 ValueType m_members;
451};
452
453} // namespace basic
454
455} // namespace field
456
457} // namespace comms
458
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:76
Contains definition of various tag classes.
comms::option::def::VersionType< T > VersionType
Same as comms::option::def::VersionType.
Definition options.h:1928
comms::option::def::FieldType< TMsg > FieldType
Same as comms::option::def::FieldType.
Definition options.h:1503
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1471
void tupleForEachWithTemplateParamIdx(TTuple &&tuple, TFunc &&func)
Invoke provided functor for every element in the tuple while providing information about element inde...
Definition Tuple.h:519
constexpr TValue tupleAccumulate(TTuple &&tuple, const TValue &value, TFunc &&func)
Performs "accumulate" algorithm on every element of the tuple.
Definition Tuple.h:584
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:278
Replacement to std::conditional.
Definition type_traits.h:29
Replacement to some types from standard type_traits.