COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
VarLength.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/cast.h"
14#include "comms/details/tag.h"
15#include "comms/ErrorStatus.h"
16#include "comms/traits.h"
17#include "comms/util/SizeToType.h"
18#include "comms/util/access.h"
20
21#include <algorithm>
22#include <cstddef>
23#include <cstdint>
24#include <limits>
25#include <utility>
26
27namespace comms
28{
29
30namespace field
31{
32
33namespace adapter
34{
35
36template <std::size_t TMinLen, std::size_t TMaxLen, typename TBase>
37class VarLength : public TBase
38{
39 using BaseImpl = TBase;
40 using BaseSerialisedType = typename BaseImpl::SerialisedType;
41
42public:
43
44 using ValueType = typename BaseImpl::ValueType;
45
46 static_assert(1U <= TMinLen, "Minimal length must be at least 1");
47 static_assert(TMinLen < TMaxLen, "Maximal length must be greater than minimal");
48 static_assert(TMaxLen <= sizeof(std::uint64_t), "Currently variable length greater than 8 bytes is not supported");
49
50 using SerialisedType =
51 typename comms::util::SizeToType<TMaxLen, std::is_signed<BaseSerialisedType>::value>::Type;
52
53 using Endian = typename BaseImpl::Endian;
54
55 VarLength() = default;
56
57 explicit VarLength(const ValueType& val)
58 : BaseImpl(val)
59 {
60 }
61
62 explicit VarLength(ValueType&& val)
63 : BaseImpl(std::move(val))
64 {
65 }
66
67 VarLength(const VarLength&) = default;
68 VarLength(VarLength&&) = default;
69 VarLength& operator=(const VarLength&) = default;
70 VarLength& operator=(VarLength&&) = default;
71
72 std::size_t length() const
73 {
74 return lengthInternal(HasSignTag());
75 }
76
77 static constexpr std::size_t minLength()
78 {
79 return MinLength;
80 }
81
82 static constexpr std::size_t maxLength()
83 {
84 return MaxLength;
85 }
86
87 static constexpr SerialisedType toSerialised(ValueType val)
88 {
89 return static_cast<SerialisedType>(BaseImpl::toSerialised(val));
90 }
91
92 static constexpr ValueType fromSerialised(SerialisedType val)
93 {
94 return BaseImpl::fromSerialised(static_cast<BaseSerialisedType>(val));
95 }
96
97 template <typename TIter>
98 comms::ErrorStatus read(TIter& iter, std::size_t size)
99 {
100 UnsignedSerialisedType val = 0;
101 std::size_t bytesCount = 0;
102 while (true) {
103 if (size == 0) {
105 }
106
107 COMMS_ASSERT(bytesCount < MaxLength);
108 auto byte = comms::util::readData<std::uint8_t>(iter, Endian());
109 auto byteValue = static_cast<std::uint8_t>(byte & VarLengthValueBitsMask);
110 addByteToSerialisedValue(
111 byteValue, bytesCount, val, typename BaseImpl::Endian());
112
113 ++bytesCount;
114
115 if ((byte & VarLengthContinueBit) == 0) {
116 break;
117 }
118
119 if (MaxLength <= bytesCount) {
121 }
122
123 --size;
124 }
125
126 if (bytesCount < minLength()) {
128 }
129
130 auto adjustedValue = signExtUnsignedSerialised(val, bytesCount, HasSignTag());
131 BaseImpl::setValue(BaseImpl::fromSerialised(static_cast<BaseSerialisedType>(adjustedValue)));
133 }
134
135 static constexpr bool hasReadNoStatus()
136 {
137 return false;
138 }
139
140 template <typename TIter>
141 void readNoStatus(TIter& iter) = delete;
142
143 bool canWrite() const
144 {
145 if (!BaseImpl::canWrite()) {
146 return false;
147 }
148
149 return length() <= TMaxLen;
150 }
151
152 template <typename TIter>
153 comms::ErrorStatus write(TIter& iter, std::size_t size) const
154 {
155 if (!canWrite()) {
157 }
158
159 if (size < length()) {
161 }
162
163 writeNoStatusInternal(toSerialised(BaseImpl::getValue()), iter, HasSignTag(), Endian());
165 }
166
167 static constexpr bool hasWriteNoStatus()
168 {
169 return false;
170 }
171
172 template <typename TIter>
173 void writeNoStatus(TIter& iter) const = delete;
174
175 bool valid() const
176 {
177 return BaseImpl::valid() && canWrite();
178 }
179
180private:
181 template <typename... TParams>
182 using UnsignedTag = comms::details::tag::Tag1<>;
183
184 template <typename... TParams>
185 using SignedTag = comms::details::tag::Tag2<>;
186
187 using HasSignTag =
188 typename comms::util::LazyShallowConditional<
189 std::is_signed<SerialisedType>::value
190 >::template Type<
191 SignedTag,
192 UnsignedTag
193 >;
194
195 using UnsignedSerialisedType = typename std::make_unsigned<SerialisedType>::type;
196
197 template <typename... TParams>
198 std::size_t lengthInternal(UnsignedTag<TParams...>) const
199 {
200 auto serValue =
201 static_cast<UnsignedSerialisedType>(toSerialised(BaseImpl::getValue()));
202 std::size_t len = 0U;
203 while (0 < serValue) {
204 serValue = static_cast<decltype(serValue)>(serValue >> VarLengthShift);
205 ++len;
206 }
207
208 return std::max(std::size_t(minLength()), len);
209 }
210
211 template <typename... TParams>
212 std::size_t lengthInternal(SignedTag<TParams...>) const
213 {
214 auto serValue = toSerialised(BaseImpl::getValue());
215 if (0 <= serValue) {
216 // positive
217 return lengthSignedPositiveInternal();
218 }
219
220 return lengthSignedNegativeInternal();
221 }
222
223 std::size_t lengthSignedNegativeInternal() const
224 {
225 auto serValue = toSerialised(BaseImpl::getValue());
226 std::size_t len = 0U;
227 std::uint8_t lastByte = 0U;
228 while (serValue != static_cast<decltype(serValue)>(-1)) {
229 auto unsignedSerValue = static_cast<UnsignedSerialisedType>(serValue);
230 lastByte = static_cast<decltype(lastByte)>(unsignedSerValue & VarLengthValueBitsMask);
231 unsignedSerValue =
232 static_cast<decltype(unsignedSerValue)>(unsignedSerValue >> VarLengthShift);
233 ++len;
234
235 unsignedSerValue |= SignExtMask;
236 serValue = static_cast<decltype(serValue)>(unsignedSerValue);
237 }
238
239 if ((lastByte & 0x40) == 0U) {
240 // Sign hasn't been captured, add one more byte
241 ++len;
242 }
243
244 //COMMS_ASSERT(len <= maxLength());
245 //return std::max(std::size_t(minLength()), std::min(len, maxLength()));
246 return std::max(std::size_t(minLength()), len);
247 }
248
249 std::size_t lengthSignedPositiveInternal() const
250 {
251 auto serValue = toSerialised(BaseImpl::getValue());
252 std::size_t len = 0U;
253 std::uint8_t lastByte = 0U;
254 while (serValue != static_cast<decltype(serValue)>(0)) {
255 auto unsignedSerValue = static_cast<UnsignedSerialisedType>(serValue);
256 lastByte = static_cast<decltype(lastByte)>(unsignedSerValue & VarLengthValueBitsMask);
257 unsignedSerValue = static_cast<decltype(unsignedSerValue)>(unsignedSerValue >> VarLengthShift);
258 ++len;
259
260 serValue = static_cast<decltype(serValue)>(unsignedSerValue);
261 }
262
263 if ((lastByte & 0x40) != 0U) {
264 // Last data byte is 1, not be mistaken sign
265 ++len;
266 }
267
268 //COMMS_ASSERT(len <= maxLength());
269 //return std::max(std::size_t(minLength()), std::min(len, maxLength()));
270 return std::max(std::size_t(minLength()), len);
271 }
272
273 template <typename TIter, typename... TParams>
274 static void writeNoStatusInternal(
275 SerialisedType val,
276 TIter& iter,
277 UnsignedTag<TParams...>,
279 {
280 auto unsignedVal =
281 static_cast<UnsignedSerialisedType>(val);
282 UnsignedSerialisedType unsignedValToWrite = 0U;
283 std::size_t bytesCount = 0;
284
285 auto isLastByte =
286 [&unsignedVal, &bytesCount]() -> bool
287 {
288 return
289 ((unsignedVal == 0) && (MinLength <= bytesCount)) ||
290 (MaxLength <= bytesCount);
291 };
292
293 while (!isLastByte()) {
294 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
295 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
296 ++bytesCount;
297
298 if (!isLastByte()) {
299 byte |= VarLengthContinueBit;
300 }
301
302 comms::cast_assign(unsignedValToWrite) =
303 unsignedValToWrite |
304 static_cast<decltype(unsignedValToWrite)>(
305 static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
306 }
307
308 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
309 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
310 }
311
312 template <typename TIter, typename... TParams>
313 static void writeNoStatusInternal(
314 SerialisedType val,
315 TIter& iter,
316 UnsignedTag<TParams...>,
318 {
319 auto unsignedVal =
320 static_cast<UnsignedSerialisedType>(val);
321 UnsignedSerialisedType unsignedValToWrite = 0U;
322 std::size_t bytesCount = 0;
323
324 auto isLastByte =
325 [&unsignedVal, &bytesCount]() -> bool
326 {
327 return
328 ((unsignedVal == 0) && (MinLength <= bytesCount)) ||
329 (MaxLength <= bytesCount);
330 };
331
332 while (!isLastByte()) {
333 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
334 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
335
336 if (0 < bytesCount) {
337 byte |= VarLengthContinueBit;
338 }
339
340 comms::cast_assign(unsignedValToWrite) =
341 unsignedValToWrite |
342 static_cast<decltype(unsignedValToWrite)>(
343 static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
344
345 ++bytesCount;
346 }
347
348 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
349 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
350 }
351
352 template <typename TIter, typename TEndian, typename... TParams>
353 static void writeNoStatusInternal(
354 SerialisedType val,
355 TIter& iter,
356 SignedTag<TParams...>,
357 TEndian endian)
358 {
359 if (static_cast<SerialisedType>(0) <= val) {
360 return writePositiveNoStatusInternal(val, iter, endian);
361 }
362
363 return writeNegativeNoStatusInternal(val, iter, endian);
364 }
365
366 template <typename TIter>
367 static void writeNegativeNoStatusInternal(
368 SerialisedType val,
369 TIter& iter,
371 {
372 UnsignedSerialisedType unsignedValToWrite = 0U;
373 std::size_t bytesCount = 0;
374
375 auto isLastByte =
376 [&val, &bytesCount]() -> bool
377 {
378 return
379 ((val == static_cast<SerialisedType>(-1)) && (MinLength <= bytesCount)) ||
380 (MaxLength <= bytesCount);
381 };
382
383 while (!isLastByte()) {
384 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
385 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
386 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
387 ++bytesCount;
388 unsignedVal |= SignExtMask;
389 val = static_cast<decltype(val)>(unsignedVal);
390
391 if (!isLastByte()) {
392 byte |= VarLengthContinueBit;
393 }
394 else if (((byte & 0x40) == 0U) && (bytesCount < MaxLength)) {
395 // Sign is not captured
396 byte |= VarLengthContinueBit;
397 unsignedValToWrite |=
398 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
399
400 ++bytesCount;
401 byte = VarLengthValueBitsMask;
402 }
403
404 unsignedValToWrite |=
405 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
406 }
407
408 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
409 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
410 }
411
412 template <typename TIter>
413 static void writePositiveNoStatusInternal(
414 SerialisedType val,
415 TIter& iter,
417 {
418 UnsignedSerialisedType unsignedValToWrite = 0U;
419 std::size_t bytesCount = 0;
420
421 auto isLastByte =
422 [&val, &bytesCount]() -> bool
423 {
424 return
425 ((val == static_cast<SerialisedType>(0)) && (MinLength <= bytesCount)) ||
426 (MaxLength <= bytesCount);
427 };
428
429 while (!isLastByte()) {
430 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
431 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
432 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
433 ++bytesCount;
434 val = static_cast<decltype(val)>(unsignedVal);
435
436 if (!isLastByte()) {
437 byte |= VarLengthContinueBit;
438 }
439 else if (((byte & 0x40) != 0U) && (bytesCount < MaxLength)) {
440 // data MSB is 1, may be confused with sign
441 byte |= VarLengthContinueBit;
442 unsignedValToWrite |=
443 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
444
445 ++bytesCount;
446 byte = 0U;
447 }
448
449 unsignedValToWrite |=
450 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
451 }
452
453 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
454 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
455 }
456
457 template <typename TIter>
458 static void writeNegativeNoStatusInternal(
459 SerialisedType val,
460 TIter& iter,
462 {
463 UnsignedSerialisedType unsignedValToWrite = 0U;
464 std::size_t bytesCount = 0;
465
466 auto isLastByte =
467 [&val, &bytesCount]() -> bool
468 {
469 return
470 ((val == static_cast<SerialisedType>(-1)) && (MinLength <= bytesCount)) ||
471 (MaxLength <= bytesCount);
472 };
473
474 while (!isLastByte()) {
475 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
476 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
477 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
478 unsignedVal |= SignExtMask;
479 val = static_cast<decltype(val)>(unsignedVal);
480
481 if (0U < bytesCount) {
482 byte |= VarLengthContinueBit;
483 }
484
485 unsignedValToWrite |=
486 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
487 ++bytesCount;
488
489 if (isLastByte() && ((byte & 0x40) == 0U) && (bytesCount < MaxLength)) {
490 // Sign is not captured
491 byte = 0xff;
492 unsignedValToWrite |=
493 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
494 ++bytesCount;
495 }
496 }
497
498 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
499 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
500 }
501
502 template <typename TIter>
503 static void writePositiveNoStatusInternal(
504 SerialisedType val,
505 TIter& iter,
507 {
508 UnsignedSerialisedType unsignedValToWrite = 0U;
509 std::size_t bytesCount = 0;
510
511 auto isLastByte =
512 [&val, &bytesCount]() -> bool
513 {
514 return
515 ((val == static_cast<SerialisedType>(0)) && (MinLength <= bytesCount)) ||
516 (MaxLength <= bytesCount);
517 };
518
519 while (!isLastByte()) {
520 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
521 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
522 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
523 val = static_cast<decltype(val)>(unsignedVal);
524
525 if (0U < bytesCount) {
526 byte |= VarLengthContinueBit;
527 }
528 unsignedValToWrite |=
529 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
530 ++bytesCount;
531
532 if (isLastByte() && ((byte & 0x40) != 0U) && (bytesCount < MaxLength)) {
533 // MSB data bit may be confusted with sign, add one more byte
534 byte = VarLengthContinueBit;
535
536 unsignedValToWrite |=
537 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
538 ++bytesCount;
539 break;
540 }
541 }
542
543 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
544 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
545 }
546
547 template <typename... TParams>
548 static constexpr SerialisedType signExtUnsignedSerialised(
549 UnsignedSerialisedType val,
550 std::size_t,
551 UnsignedTag<TParams...>)
552 {
553 return static_cast<SerialisedType>(val);
554 }
555
556 template <typename... TParams>
557 static SerialisedType signExtUnsignedSerialised(
558 UnsignedSerialisedType val,
559 std::size_t bytesCount,
560 SignedTag<TParams...>)
561 {
562 UnsignedSerialisedType signBitMask =
563 static_cast<UnsignedSerialisedType>(1U) << ((bytesCount * BitsInByte) - (bytesCount + 1));
564
565 if ((val & signBitMask) == 0U) {
566 return static_cast<SerialisedType>(val);
567 }
568
569 UnsignedSerialisedType signExtMask =
570 static_cast<UnsignedSerialisedType>(~(signBitMask - 1));
571
572 val |= signExtMask;
573 return static_cast<SerialisedType>(val);
574 }
575
576 static void addByteToSerialisedValue(
577 std::uint8_t byte,
578 std::size_t byteCount,
579 UnsignedSerialisedType& val,
581 {
582 static_cast<void>(byteCount);
583 COMMS_ASSERT((byte & VarLengthContinueBit) == 0);
584 val = static_cast<UnsignedSerialisedType>(val << VarLengthShift);
585 val = static_cast<UnsignedSerialisedType>(val | byte);
586 }
587
588 static void addByteToSerialisedValue(
589 std::uint8_t byte,
590 std::size_t byteCount,
591 UnsignedSerialisedType& val,
593 {
594 COMMS_ASSERT((byte & VarLengthContinueBit) == 0);
595 auto shift =
596 byteCount * VarLengthShift;
597 val = static_cast<UnsignedSerialisedType>((static_cast<UnsignedSerialisedType>(byte) << shift) | val);
598 }
599
600 static const std::size_t MinLength = TMinLen;
601 static const std::size_t MaxLength = TMaxLen;
602 static const std::size_t VarLengthShift = 7;
603 static const std::uint8_t VarLengthValueBitsMask =
604 (static_cast<std::uint8_t>(1U) << VarLengthShift) - 1;
605 static const std::uint8_t VarLengthContinueBit =
606 static_cast<std::uint8_t>(~(VarLengthValueBitsMask));
607 static const std::size_t BitsInByte =
608 std::numeric_limits<std::uint8_t>::digits;
609 static const std::size_t SerLengthInBits =
610 BitsInByte * sizeof(SerialisedType);
611 static const auto SignExtMask =
612 static_cast<UnsignedSerialisedType>(
613 std::numeric_limits<UnsignedSerialisedType>::max() << (SerLengthInBits - VarLengthShift));
614
615 static_assert(0 < MinLength, "MinLength is expected to be greater than 0");
616 static_assert(MinLength <= MaxLength,
617 "MinLength is expected to be no greater than MaxLength");
618};
619
620} // namespace adapter
621
622} // namespace field
623
624} // namespace comms
625
This file contains classes required for generic custom assertion functionality.
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
This file contain definition of error statuses used by comms module.
Contains functions for raw data access / (de)serialization.
Contains definition of various casts.
comms::option::def::VarLength< TMin, TMax > VarLength
Same as comms::option::def::VarLength.
Definition options.h:1547
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1473
util::traits::endian::Little Little
Empty class used in traits to indicate Little Endian.
Definition traits.h:33
util::traits::endian::Big Big
Empty class used in traits to indicate Big Endian.
Definition traits.h:30
void writeData(T value, TIter &iter, const traits::endian::Big &endian)
Same as writeBig<T, TIter>()
Definition access.h:707
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.
@ InvalidMsgData
Used to indicate that a message has invalid data.
details::ValueAssignWrapper< T > cast_assign(T &value)
Helper function to assign value with static_cast to appropriate type.
Definition cast.h:30
STL namespace.
Empty class used in traits to indicate Big Endian.
Definition access.h:44
Empty class used in traits to indicate Little Endian.
Definition access.h:47
This file contains all the classes necessary to properly define message traits.
Replacement to some types from standard type_traits.