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