COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
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:95
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.