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