COMMS
Template library intended to help with implementation of communication protocols.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ChecksumLayer.h
Go to the documentation of this file.
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
10
11#pragma once
12
13#include "comms/cast.h"
15#include "comms/details/tag.h"
17#include "comms/frame/details/ChecksumLayerBase.h"
18#include "comms/frame/details/ChecksumLayerOptionsParser.h"
20
21#include <iterator>
22#include <type_traits>
23
24COMMS_MSVC_WARNING_PUSH
25COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
26
27namespace comms
28{
29
30namespace frame
31{
62template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
63class ChecksumLayer : public comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>
64{
65 using BaseImpl = comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>;
66 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
67
68public:
70 using Field = typename BaseImpl::Field;
71
73 using ChecksumCalc = TCalc;
74
78 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
79
81 ChecksumLayer() = default;
82
84 ChecksumLayer(const ChecksumLayer&) = default;
85
88
90 ~ChecksumLayer() noexcept = default;
91
93 ChecksumLayer& operator=(const ChecksumLayer&) = default;
94
96 ChecksumLayer& operator=(ChecksumLayer&&) = default;
97
102 static constexpr bool hasExtendingClass()
103 {
104 return ParsedOptionsInternal::HasExtendingClass;
105 }
106
109 static constexpr bool hasVerifyBeforeRead()
110 {
111 return ParsedOptionsInternal::HasVerifyBeforeRead;
112 }
113
144 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
146 Field& field,
147 TMsg& msg,
148 TIter& iter,
149 std::size_t size,
150 TNextLayerReader&& nextLayerReader,
151 TExtraValues... extraValues)
152 {
153 using IterType = typename std::decay<decltype(iter)>::type;
154 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
155 "The read operation is expected to use random access iterator");
156
157 using VerifyTag =
158 typename comms::util::LazyShallowConditional<
159 ParsedOptionsInternal::HasVerifyBeforeRead
160 >::template Type<
161 VerifyBeforeReadTag,
162 VerifyAfterReadTag
163 >;
164
165 return
166 readInternal(
167 field,
168 msg,
169 iter,
170 size,
171 std::forward<TNextLayerReader>(nextLayerReader),
172 VerifyTag(),
173 extraValues...);
174 }
175
201 template <typename TMsg, typename TIter, typename TNextLayerWriter>
203 Field& field,
204 const TMsg& msg,
205 TIter& iter,
206 std::size_t size,
207 TNextLayerWriter&& nextLayerWriter) const
208 {
209 using IterType = typename std::decay<decltype(iter)>::type;
210 using Tag = typename std::iterator_traits<IterType>::iterator_category;
211
212 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
213 }
214
224 template <typename TIter, typename TNextLayerUpdater>
226 Field& field,
227 TIter& iter,
228 std::size_t size,
229 TNextLayerUpdater&& nextLayerUpdater) const
230 {
231 auto fromIter = iter;
232 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
233 if (es != comms::ErrorStatus::Success) {
234 return es;
235 }
236
237 using MsgPtr = typename BaseImpl::MsgPtr;
238 static_assert(
239 !std::is_void<MsgPtr>::value,
240 "Please use update() overload that accepts message object as its first parameter");
241
242 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
243 return fieldUpdateInternal(msgPtr, fromIter, iter, size, field);
244 }
245
255 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
257 const TMsg& msg,
258 Field& field,
259 TIter& iter,
260 std::size_t size,
261 TNextLayerUpdater&& nextLayerUpdater) const
262 {
263 auto fromIter = iter;
264 auto& thisObj = BaseImpl::thisLayer();
265 auto fieldLen = thisObj.doFieldLength(msg);
266 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
267 if (es != comms::ErrorStatus::Success) {
268 return es;
269 }
270
271 return fieldUpdateInternal(&msg, fromIter, iter, size, field);
272 }
273
274protected:
284 template <typename TMsg, typename TIter>
285 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
286 {
287 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
288 }
289
299 template <typename TMsg, typename TIter>
300 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
301 {
302 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
303 }
304
316 template <typename TMsg, typename TIter>
317 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
318 {
319 static_cast<void>(msg);
320 checksumValid = true;
321 return TCalc()(iter, len);
322 }
323
331 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
332 {
333 return field.getValue();
334 }
335
347 template <typename TChecksum, typename TMsg>
348 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
349 {
350 static_cast<void>(msg);
351 field.setValue(checksum);
352 }
353
354private:
355 template <typename... TParams>
356 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
357
358 template <typename... TParams>
359 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
360
361 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
362 ErrorStatus verifyRead(
363 Field& field,
364 TMsg& msg,
365 TIter& iter,
366 std::size_t size,
367 TReader&& nextLayerReader,
368 TExtraValues... extraValues)
369 {
370 auto fromIter = iter;
371 auto* msgPtr = BaseImpl::toMsgPtr(msg);
372 auto fieldLen = Field::minLength();
373 auto& thisObj = BaseImpl::thisLayer();
374 if (msgPtr != nullptr) {
375 fieldLen = thisObj.doFieldLength(*msgPtr);
376 }
377 auto toIter = fromIter + (size - fieldLen);
378 auto len = static_cast<std::size_t>(std::distance(fromIter, toIter));
379
380 auto checksumEs = thisObj.readField(msgPtr, field, toIter, fieldLen);
381 if (checksumEs != ErrorStatus::Success) {
382 return checksumEs;
383 }
384
385 bool checksumValid = false;
386 auto checksum =
387 thisObj.calculateChecksum(
388 msgPtr,
389 fromIter,
390 len,
391 checksumValid);
392
393 if (!checksumValid) {
395 }
396
397 auto expectedValue = thisObj.getChecksumFromField(field);
398
399 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
400 BaseImpl::resetMsg(msg);
401 return ErrorStatus::ProtocolError;
402 }
403
404 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
405 if (es == ErrorStatus::Success) {
406 iter = toIter;
407 }
408
409 return es;
410 }
411
412 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
413 ErrorStatus readVerify(
414 Field& field,
415 TMsg& msg,
416 TIter& iter,
417 std::size_t size,
418 TReader&& nextLayerReader,
419 TExtraValues... extraValues)
420 {
421 auto fromIter = iter;
422
423 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
424 if ((es == ErrorStatus::NotEnoughData) ||
425 (es == ErrorStatus::ProtocolError)) {
426 return es;
427 }
428
429 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
430 COMMS_ASSERT(len <= size);
431 auto remSize = size - len;
432 auto* msgPtr = BaseImpl::toMsgPtr(msg);
433 auto& thisObj = BaseImpl::thisLayer();
434 auto checksumEs = thisObj.readField(msgPtr, field, iter, remSize);
435 if (checksumEs == ErrorStatus::NotEnoughData) {
436 BaseImpl::updateMissingSize(field, remSize, extraValues...);
437 }
438
439 if (checksumEs != ErrorStatus::Success) {
440 BaseImpl::resetMsg(msg);
441 return checksumEs;
442 }
443
444 bool checksumValid = false;
445 auto checksum =
446 thisObj.calculateChecksum(
447 BaseImpl::toMsgPtr(msg),
448 fromIter,
449 len,
450 checksumValid);
451
452 if (!checksumValid) {
454 }
455
456 auto expectedValue = thisObj.getChecksumFromField(field);
457
458 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
459 BaseImpl::resetMsg(msg);
460 return ErrorStatus::ProtocolError;
461 }
462
463 return es;
464 }
465
466 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
467 ErrorStatus readInternal(
468 Field& field,
469 TMsg& msg,
470 TIter& iter,
471 std::size_t size,
472 TReader&& nextLayerReader,
473 VerifyBeforeReadTag<>,
474 TExtraValues... extraValues)
475 {
476 return
477 verifyRead(
478 field,
479 msg,
480 iter,
481 size,
482 std::forward<TReader>(nextLayerReader),
483 extraValues...);
484 }
485
486 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
487 ErrorStatus readInternal(
488 Field& field,
489 TMsg& msg,
490 TIter& iter,
491 std::size_t size,
492 TReader&& nextLayerReader,
493 VerifyAfterReadTag<>,
494 TExtraValues... extraValues)
495 {
496 return
497 readVerify(
498 field,
499 msg,
500 iter,
501 size,
502 std::forward<TReader>(nextLayerReader),
503 extraValues...);
504 }
505
506 template <typename TMsg, typename TIter, typename TWriter>
507 ErrorStatus writeInternalRandomAccess(
508 Field& field,
509 const TMsg& msg,
510 TIter& iter,
511 std::size_t size,
512 TWriter&& nextLayerWriter) const
513 {
514 auto fromIter = iter;
515 auto es = nextLayerWriter.write(msg, iter, size);
516 if ((es != comms::ErrorStatus::Success) &&
518 return es;
519 }
520
521 COMMS_ASSERT(fromIter <= iter);
522 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
523 auto remSize = size - len;
524 auto& thisObj = BaseImpl::thisLayer();
525
527 thisObj.prepareFieldForWrite(0, &msg, field);
528 auto esTmp = thisObj.writeField(&msg, field, iter, remSize);
529 if (esTmp != comms::ErrorStatus::Success) {
530 return esTmp;
531 }
532
533 return es;
534 }
535
536 bool checksumValid = false;
537 auto checksum =
538 thisObj.calculateChecksum(
539 &msg,
540 fromIter,
541 len,
542 checksumValid);
543
544 if (!checksumValid) {
546 }
547
548 thisObj.prepareFieldForWrite(checksum, &msg, field);
549 return thisObj.writeField(&msg, field, iter, remSize);
550 }
551
552 template <typename TMsg, typename TIter, typename TWriter>
553 ErrorStatus writeInternalOutput(
554 Field& field,
555 const TMsg& msg,
556 TIter& iter,
557 std::size_t size,
558 TWriter&& nextLayerWriter) const
559 {
560 auto& thisObj = BaseImpl::thisLayer();
561 auto fieldLen = thisObj.doFieldLength(msg);
562 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
563 if ((es != comms::ErrorStatus::Success) &&
565 return es;
566 }
567
568 auto esTmp = thisObj.writeField(&msg, field, iter, fieldLen);
569 static_cast<void>(esTmp);
572 }
573
574 template <typename TMsg, typename TIter, typename TWriter>
575 ErrorStatus writeInternal(
576 Field& field,
577 const TMsg& msg,
578 TIter& iter,
579 std::size_t size,
580 TWriter&& nextLayerWriter,
581 std::random_access_iterator_tag) const
582 {
583 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
584 }
585
586 template <typename TMsg, typename TIter, typename TWriter>
587 ErrorStatus writeInternal(
588 Field& field,
589 const TMsg& msg,
590 TIter& iter,
591 std::size_t size,
592 TWriter&& nextLayerWriter,
593 std::output_iterator_tag) const
594 {
595 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
596 }
597
598 template <typename TMsg, typename TIter>
599 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
600 {
601 COMMS_ASSERT(from <= to);
602 auto len = static_cast<std::size_t>(std::distance(from, to));
603 auto& thisObj = BaseImpl::thisLayer();
604 if (msgPtr != nullptr) {
605 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
606 }
607 else {
608 COMMS_ASSERT(len == (size - Field::maxLength()));
609 }
610 auto remSize = size - len;
611
612 bool checksumValid = false;
613 auto checksum =
614 thisObj.calculateChecksum(
615 msgPtr,
616 from,
617 len,
618 checksumValid);
619
620 if (!checksumValid) {
622 }
623
624 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
625 return thisObj.writeField(msgPtr, field, to, remSize);
626 }
627};
628
629namespace details
630{
631template <typename T>
632struct ChecksumLayerCheckHelper
633{
634 static const bool Value = false;
635};
636
637template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
638struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
639{
640 static const bool Value = true;
641};
642
643} // namespace details
644
648template <typename T>
649constexpr bool isChecksumLayer()
650{
651 return details::ChecksumLayerCheckHelper<T>::Value;
652}
653
654} // namespace frame
655
656} // namespace comms
657
658COMMS_MSVC_WARNING_POP
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Contains various compiler related definitions.
Contains definition of comms::field::IntValue.
Contains definition of various casts.
Base class to all the field classes.
Definition Field.h:33
Protocol layer that is responsible to calculate checksum on the data written by all the wrapped inter...
Definition ChecksumLayer.h:64
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumLayer.h:109
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumLayer.h:70
~ChecksumLayer() noexcept=default
Destructor.
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumLayer.h:317
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumLayer.h:348
comms::ErrorStatus doUpdate(const TMsg &msg, Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumLayer.h:256
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumLayer.h:331
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumLayer.h:73
ErrorStatus doRead(Field &field, TMsg &msg, TIter &iter, std::size_t size, TNextLayerReader &&nextLayerReader, TExtraValues... extraValues)
Customized read functionality, invoked by read().
Definition ChecksumLayer.h:145
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumLayer.h:300
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumLayer.h:285
ErrorStatus doWrite(Field &field, const TMsg &msg, TIter &iter, std::size_t size, TNextLayerWriter &&nextLayerWriter) const
Customized write functionality, invoked by write().
Definition ChecksumLayer.h:202
ChecksumLayer()=default
Default constructor.
ChecksumLayer(ChecksumLayer &&)=default
Move constructor.
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumLayer.h:225
ChecksumLayer(const ChecksumLayer &)=default
Copy constructor.
constexpr bool isChecksumLayer()
Compile time check of whether the provided type is a variant of ChecksumLayer.
Definition ChecksumLayer.h:649
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumLayer.h:78
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:96
comms::frame::ChecksumLayer< TField, TCalc, TNextLayer, TOptions... > ChecksumLayer
Alias to the comms::frame::ChecksumLayer.
Definition ChecksumLayer.h:25
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.
Replacement to some types from standard type_traits.