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