COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
VarLength.h
1//
2// Copyright 2015 - 2024 (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 <type_traits>
11#include <algorithm>
12#include <limits>
13
14#include "comms/Assert.h"
15#include "comms/util/SizeToType.h"
16#include "comms/util/access.h"
18#include "comms/ErrorStatus.h"
19#include "comms/details/tag.h"
20#include "comms/cast.h"
21
22namespace comms
23{
24
25namespace field
26{
27
28namespace adapter
29{
30
31template <std::size_t TMinLen, std::size_t TMaxLen, typename TBase>
32class VarLength : public TBase
33{
34 using BaseImpl = TBase;
35 using BaseSerialisedType = typename BaseImpl::SerialisedType;
36
37public:
38
39 using ValueType = typename BaseImpl::ValueType;
40
41 static_assert(1U <= TMinLen, "Minimal length must be at least 1");
42 static_assert(TMinLen < TMaxLen, "Maximal length must be greater than minimal");
43 static_assert(TMaxLen <= sizeof(std::uint64_t), "Currently variable length greater than 8 bytes is not supported");
44
45 using SerialisedType =
46 typename comms::util::SizeToType<TMaxLen, std::is_signed<BaseSerialisedType>::value>::Type;
47
48 using Endian = typename BaseImpl::Endian;
49
50 VarLength() = default;
51
52 explicit VarLength(const ValueType& val)
53 : BaseImpl(val)
54 {
55 }
56
57 explicit VarLength(ValueType&& val)
58 : BaseImpl(std::move(val))
59 {
60 }
61
62 VarLength(const VarLength&) = default;
63 VarLength(VarLength&&) = default;
64 VarLength& operator=(const VarLength&) = default;
65 VarLength& operator=(VarLength&&) = default;
66
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
270 template <typename TIter, typename... TParams>
271 static void writeNoStatusInternal(
272 SerialisedType val,
273 TIter& iter,
274 UnsignedTag<TParams...>,
276 {
277 auto unsignedVal =
278 static_cast<UnsignedSerialisedType>(val);
279 UnsignedSerialisedType unsignedValToWrite = 0U;
280 std::size_t bytesCount = 0;
281
282 auto isLastByte =
283 [&unsignedVal, &bytesCount]() -> bool
284 {
285 return
286 ((unsignedVal == 0) && (MinLength <= bytesCount)) ||
287 (MaxLength <= bytesCount);
288 };
289
290 while (!isLastByte()) {
291 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
292 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
293 ++bytesCount;
294
295 if (!isLastByte()) {
296 byte |= VarLengthContinueBit;
297 }
298
299 comms::cast_assign(unsignedValToWrite) =
300 unsignedValToWrite |
301 static_cast<decltype(unsignedValToWrite)>(
302 static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
303 }
304
305 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
306 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
307 }
308
309 template <typename TIter, typename... TParams>
310 static void writeNoStatusInternal(
311 SerialisedType val,
312 TIter& iter,
313 UnsignedTag<TParams...>,
315 {
316 auto unsignedVal =
317 static_cast<UnsignedSerialisedType>(val);
318 UnsignedSerialisedType unsignedValToWrite = 0U;
319 std::size_t bytesCount = 0;
320
321 auto isLastByte =
322 [&unsignedVal, &bytesCount]() -> bool
323 {
324 return
325 ((unsignedVal == 0) && (MinLength <= bytesCount)) ||
326 (MaxLength <= bytesCount);
327 };
328
329 while (!isLastByte()) {
330 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
331 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
332
333 if (0 < bytesCount) {
334 byte |= VarLengthContinueBit;
335 }
336
337 comms::cast_assign(unsignedValToWrite) =
338 unsignedValToWrite |
339 static_cast<decltype(unsignedValToWrite)>(
340 static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
341
342 ++bytesCount;
343 }
344
345 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
346 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
347 }
348
349 template <typename TIter, typename TEndian, typename... TParams>
350 static void writeNoStatusInternal(
351 SerialisedType val,
352 TIter& iter,
353 SignedTag<TParams...>,
354 TEndian endian)
355 {
356 if (static_cast<SerialisedType>(0) <= val) {
357 return writePositiveNoStatusInternal(val, iter, endian);
358 }
359
360 return writeNegativeNoStatusInternal(val, iter, endian);
361 }
362
363 template <typename TIter>
364 static void writeNegativeNoStatusInternal(
365 SerialisedType val,
366 TIter& iter,
368 {
369 UnsignedSerialisedType unsignedValToWrite = 0U;
370 std::size_t bytesCount = 0;
371
372 auto isLastByte =
373 [&val, &bytesCount]() -> bool
374 {
375 return
376 ((val == static_cast<SerialisedType>(-1)) && (MinLength <= bytesCount)) ||
377 (MaxLength <= bytesCount);
378 };
379
380 while (!isLastByte()) {
381 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
382 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
383 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
384 ++bytesCount;
385 unsignedVal |= SignExtMask;
386 val = static_cast<decltype(val)>(unsignedVal);
387
388 if (!isLastByte()) {
389 byte |= VarLengthContinueBit;
390 }
391 else if (((byte & 0x40) == 0U) && (bytesCount < MaxLength)) {
392 // Sign is not captured
393 byte |= VarLengthContinueBit;
394 unsignedValToWrite |=
395 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
396
397 ++bytesCount;
398 byte = VarLengthValueBitsMask;
399 }
400
401 unsignedValToWrite |=
402 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
403 }
404
405 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
406 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
407 }
408
409 template <typename TIter>
410 static void writePositiveNoStatusInternal(
411 SerialisedType val,
412 TIter& iter,
414 {
415 UnsignedSerialisedType unsignedValToWrite = 0U;
416 std::size_t bytesCount = 0;
417
418 auto isLastByte =
419 [&val, &bytesCount]() -> bool
420 {
421 return
422 ((val == static_cast<SerialisedType>(0)) && (MinLength <= bytesCount)) ||
423 (MaxLength <= bytesCount);
424 };
425
426 while (!isLastByte()) {
427 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
428 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
429 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
430 ++bytesCount;
431 val = static_cast<decltype(val)>(unsignedVal);
432
433 if (!isLastByte()) {
434 byte |= VarLengthContinueBit;
435 }
436 else if (((byte & 0x40) != 0U) && (bytesCount < MaxLength)) {
437 // data MSB is 1, may be confused with sign
438 byte |= VarLengthContinueBit;
439 unsignedValToWrite |=
440 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
441
442 ++bytesCount;
443 byte = 0U;
444 }
445
446 unsignedValToWrite |=
447 (static_cast<UnsignedSerialisedType>(byte) << ((bytesCount - 1) * BitsInByte));
448 }
449
450 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
451 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
452 }
453
454 template <typename TIter>
455 static void writeNegativeNoStatusInternal(
456 SerialisedType val,
457 TIter& iter,
459 {
460 UnsignedSerialisedType unsignedValToWrite = 0U;
461 std::size_t bytesCount = 0;
462
463 auto isLastByte =
464 [&val, &bytesCount]() -> bool
465 {
466 return
467 ((val == static_cast<SerialisedType>(-1)) && (MinLength <= bytesCount)) ||
468 (MaxLength <= bytesCount);
469 };
470
471 while (!isLastByte()) {
472 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
473 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
474 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
475 unsignedVal |= SignExtMask;
476 val = static_cast<decltype(val)>(unsignedVal);
477
478 if (0U < bytesCount) {
479 byte |= VarLengthContinueBit;
480 }
481
482 unsignedValToWrite |=
483 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
484 ++bytesCount;
485
486
487 if (isLastByte() && ((byte & 0x40) == 0U) && (bytesCount < MaxLength)) {
488 // Sign is not captured
489 byte = 0xff;
490 unsignedValToWrite |=
491 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
492 ++bytesCount;
493 }
494 }
495
496 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
497 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
498 }
499
500 template <typename TIter>
501 static void writePositiveNoStatusInternal(
502 SerialisedType val,
503 TIter& iter,
505 {
506 UnsignedSerialisedType unsignedValToWrite = 0U;
507 std::size_t bytesCount = 0;
508
509 auto isLastByte =
510 [&val, &bytesCount]() -> bool
511 {
512 return
513 ((val == static_cast<SerialisedType>(0)) && (MinLength <= bytesCount)) ||
514 (MaxLength <= bytesCount);
515 };
516
517 while (!isLastByte()) {
518 auto unsignedVal = static_cast<UnsignedSerialisedType>(val);
519 auto byte = static_cast<std::uint8_t>(unsignedVal & VarLengthValueBitsMask);
520 unsignedVal = static_cast<decltype(unsignedVal)>(unsignedVal >> VarLengthShift);
521 val = static_cast<decltype(val)>(unsignedVal);
522
523 if (0U < bytesCount) {
524 byte |= VarLengthContinueBit;
525 }
526 unsignedValToWrite |=
527 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
528 ++bytesCount;
529
530 if (isLastByte() && ((byte & 0x40) != 0U) && (bytesCount < MaxLength)) {
531 // MSB data bit may be confusted with sign, add one more byte
532 byte = VarLengthContinueBit;
533
534 unsignedValToWrite |=
535 (static_cast<UnsignedSerialisedType>(byte) << (bytesCount * BitsInByte));
536 ++bytesCount;
537 break;
538 }
539 }
540
541 auto len = std::max(minLength(), std::min(bytesCount, maxLength()));
542 comms::util::writeData(unsignedValToWrite, len, iter, Endian());
543 }
544
545 template <typename... TParams>
546 static constexpr SerialisedType signExtUnsignedSerialised(
547 UnsignedSerialisedType val,
548 std::size_t,
549 UnsignedTag<TParams...>)
550 {
551 return static_cast<SerialisedType>(val);
552 }
553
554 template <typename... TParams>
555 static SerialisedType signExtUnsignedSerialised(
556 UnsignedSerialisedType val,
557 std::size_t bytesCount,
558 SignedTag<TParams...>)
559 {
560 UnsignedSerialisedType signBitMask =
561 static_cast<UnsignedSerialisedType>(1U) << ((bytesCount * BitsInByte) - (bytesCount + 1));
562
563 if ((val & signBitMask) == 0U) {
564 return static_cast<SerialisedType>(val);
565 }
566
567 UnsignedSerialisedType signExtMask =
568 static_cast<UnsignedSerialisedType>(~(signBitMask - 1));
569
570 val |= signExtMask;
571 return static_cast<SerialisedType>(val);
572 }
573
574
575 static void addByteToSerialisedValue(
576 std::uint8_t byte,
577 std::size_t byteCount,
578 UnsignedSerialisedType& val,
580 {
581 static_cast<void>(byteCount);
582 COMMS_ASSERT((byte & VarLengthContinueBit) == 0);
583 val = static_cast<UnsignedSerialisedType>(val << VarLengthShift);
584 val = static_cast<UnsignedSerialisedType>(val | byte);
585 }
586
587 static void addByteToSerialisedValue(
588 std::uint8_t byte,
589 std::size_t byteCount,
590 UnsignedSerialisedType& val,
592 {
593 COMMS_ASSERT((byte & VarLengthContinueBit) == 0);
594 auto shift =
595 byteCount * VarLengthShift;
596 val = static_cast<UnsignedSerialisedType>((static_cast<UnsignedSerialisedType>(byte) << shift) | val);
597 }
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
626
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:1496
comms::option::def::Endian< TEndian > Endian
Same as comms::option::def::Endian.
Definition options.h:1438
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: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
Replacement to some types from standard type_traits.