COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
ChecksumPrefixLayer.h
Go to the documentation of this file.
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
10
11#pragma once
12
13#include <iterator>
14#include <type_traits>
15
18#include "comms/protocol/details/ProtocolLayerBase.h"
19#include "comms/protocol/details/ChecksumLayerOptionsParser.h"
20#include "comms/protocol/details/ProtocolLayerExtendingClassHelper.h"
22
23COMMS_MSVC_WARNING_PUSH
24COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
25
26namespace comms
27{
28
29namespace protocol
30{
63template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
64class ChecksumPrefixLayer : public
66 TField,
67 TNextLayer,
68 details::ProtocolLayerExtendingClassT<
69 ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...>,
70 details::ChecksumLayerOptionsParser<TOptions...>
71 >,
72 typename details::template ChecksumLayerOptionsParser<TOptions...>::template SuppressForVerifyBeforeRead<
73 comms::option::def::ProtocolLayerDisallowReadUntilDataSplit
74 >
75 >
76{
77 using BaseImpl =
79 TField,
80 TNextLayer,
81 details::ProtocolLayerExtendingClassT<
82 ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...>,
83 details::ChecksumLayerOptionsParser<TOptions...>
84 >,
85 typename details::template ChecksumLayerOptionsParser<TOptions...>::template SuppressForVerifyBeforeRead<
87 >
88 >;
89
90 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
91
92public:
93
95 using Field = typename BaseImpl::Field;
96
98 using ChecksumCalc = TCalc;
99
103 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
104
107
110
113
115 ~ChecksumPrefixLayer() noexcept = default;
116
118 ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
119
122
127 static constexpr bool hasExtendingClass()
128 {
129 return ParsedOptionsInternal::HasExtendingClass;
130 }
131
134 static constexpr bool hasVerifyBeforeRead()
135 {
136 return ParsedOptionsInternal::HasVerifyBeforeRead;
137 }
138
169 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
171 Field& field,
172 TMsg& msg,
173 TIter& iter,
174 std::size_t size,
175 TNextLayerReader&& nextLayerReader,
176 TExtraValues... extraValues)
177 {
178 using IterType = typename std::decay<decltype(iter)>::type;
179 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
180 "The read operation is expected to use random access iterator");
181
182 auto* msgPtr = BaseImpl::toMsgPtr(msg);
183 auto& thisObj = BaseImpl::thisLayer();
184 auto beforeFieldReadIter = iter;
185 auto checksumEs = thisObj.readField(msgPtr, field, iter, size);
186 if (checksumEs == ErrorStatus::NotEnoughData) {
187 BaseImpl::updateMissingSize(field, size, extraValues...);
188 }
189
190 if (checksumEs != ErrorStatus::Success) {
191 return checksumEs;
192 }
193
194 using VerifyTag =
195 typename comms::util::LazyShallowConditional<
196 ParsedOptionsInternal::HasVerifyBeforeRead
197 >::template Type<
198 VerifyBeforeReadTag,
199 VerifyAfterReadTag
200 >;
201
202 auto fieldLen = static_cast<std::size_t>(std::distance(beforeFieldReadIter, iter));
203 return
204 readInternal(
205 field,
206 msg,
207 iter,
208 size - fieldLen,
209 std::forward<TNextLayerReader>(nextLayerReader),
210 VerifyTag(),
211 extraValues...);
212 }
213
241 template <typename TMsg, typename TIter, typename TNextLayerWriter>
243 Field& field,
244 const TMsg& msg,
245 TIter& iter,
246 std::size_t size,
247 TNextLayerWriter&& nextLayerWriter) const
248 {
249 using IterType = typename std::decay<decltype(iter)>::type;
250 using Tag = typename std::iterator_traits<IterType>::iterator_category;
251
252 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
253 }
254
264 template <typename TIter, typename TNextLayerUpdater>
266 Field& field,
267 TIter& iter,
268 std::size_t size,
269 TNextLayerUpdater&& nextLayerUpdater) const
270 {
271 auto checksumIter = iter;
272 std::advance(iter, Field::maxLength());
273
274 auto fromIter = iter;
275 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
276 if (es != comms::ErrorStatus::Success) {
277 return es;
278 }
279
280 using MsgPtr = typename BaseImpl::MsgPtr;
281 static_assert(
282 !std::is_void<MsgPtr>::value,
283 "Please use update() overload that accepts message object as its first parameter");
284
285 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
286 return fieldUpdateInternal(msgPtr, checksumIter, fromIter, iter, size, field);
287 }
288
300 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
302 const TMsg& msg,
303 Field& field,
304 TIter& iter,
305 std::size_t size,
306 TNextLayerUpdater&& nextLayerUpdater) const
307 {
308 auto& thisObj = BaseImpl::thisLayer();
309 auto checksumIter = iter;
310 auto fieldLen = thisObj.doFieldLength(msg);
311 std::advance(iter, fieldLen);
312
313 auto fromIter = iter;
314 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
315 if (es != comms::ErrorStatus::Success) {
316 return es;
317 }
318
319 return fieldUpdateInternal(&msg, checksumIter, fromIter, iter, size, field);
320 }
321
322protected:
332 template <typename TMsg, typename TIter>
333 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
334 {
335 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
336 }
337
347 template <typename TMsg, typename TIter>
348 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
349 {
350 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
351 }
352
364 template <typename TMsg, typename TIter>
365 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
366 {
367 static_cast<void>(msg);
368 checksumValid = true;
369 return TCalc()(iter, len);
370 }
371
379 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
380 {
381 return field.getValue();
382 }
383
395 template <typename TChecksum, typename TMsg>
396 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
397 {
398 static_cast<void>(msg);
399 field.setValue(checksum);
400 }
401
402private:
403
404 template <typename... TParams>
405 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
406
407 template <typename... TParams>
408 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
409
410 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
411 ErrorStatus verifyRead(
412 Field& field,
413 TMsg& msg,
414 TIter& iter,
415 std::size_t size,
416 TReader&& nextLayerReader,
417 TExtraValues... extraValues)
418 {
419 auto fromIter = iter;
420 auto& thisObj = BaseImpl::thisLayer();
421 auto* msgPtr = BaseImpl::toMsgPtr(msg);
422
423 bool checksumValid = false;
424 auto checksum =
425 thisObj.calculateChecksum(
426 msgPtr,
427 fromIter,
428 size,
429 checksumValid);
430
431 if (!checksumValid) {
433 }
434
435 auto expectedValue = thisObj.getChecksumFromField(field);
436 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
437 BaseImpl::resetMsg(msg);
438 return ErrorStatus::ProtocolError;
439 }
440
441 return nextLayerReader.read(msg, iter, size, extraValues...);
442 }
443
444 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
445 ErrorStatus readVerify(
446 Field& field,
447 TMsg& msg,
448 TIter& iter,
449 std::size_t size,
450 TReader&& nextLayerReader,
451 TExtraValues... extraValues)
452 {
453 auto fromIter = iter;
454
455 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
456 if ((es == ErrorStatus::NotEnoughData) ||
457 (es == ErrorStatus::ProtocolError)) {
458 return es;
459 }
460
461 auto* msgPtr = BaseImpl::toMsgPtr(msg);
462 auto& thisObj = BaseImpl::thisLayer();
463 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
464 bool checksumValid = false;
465 auto checksum =
466 thisObj.calculateChecksum(
467 msgPtr,
468 fromIter,
469 len,
470 checksumValid);
471
472 if (!checksumValid) {
474 }
475
476 auto expectedValue = thisObj.getChecksumFromField(field);
477 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
478 BaseImpl::resetMsg(msg);
479 return ErrorStatus::ProtocolError;
480 }
481
482 return es;
483 }
484
485 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
486 ErrorStatus readInternal(
487 Field& field,
488 TMsg& msg,
489 TIter& iter,
490 std::size_t size,
491 TReader&& nextLayerReader,
492 VerifyBeforeReadTag<>,
493 TExtraValues... extraValues)
494 {
495 return
496 verifyRead(
497 field,
498 msg,
499 iter,
500 size,
501 std::forward<TReader>(nextLayerReader),
502 extraValues...);
503 }
504
505 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
506 ErrorStatus readInternal(
507 Field& field,
508 TMsg& msg,
509 TIter& iter,
510 std::size_t size,
511 TReader&& nextLayerReader,
512 VerifyAfterReadTag<>,
513 TExtraValues... extraValues)
514 {
515 return
516 readVerify(
517 field,
518 msg,
519 iter,
520 size,
521 std::forward<TReader>(nextLayerReader),
522 extraValues...);
523 }
524
525 template <typename TMsg, typename TIter, typename TWriter>
526 ErrorStatus writeInternalRandomAccess(
527 Field& field,
528 const TMsg& msg,
529 TIter& iter,
530 std::size_t size,
531 TWriter&& nextLayerWriter) const
532 {
533 auto& thisObj = BaseImpl::thisLayer();
534 auto checksumIter = iter;
535 thisObj.prepareFieldForWrite(0U, &msg, field);
536 auto es = thisObj.writeField(&msg, field, iter, size);
537 if (es != comms::ErrorStatus::Success) {
538 return es;
539 }
540
541 auto checksumLen =
542 static_cast<std::size_t>(std::distance(checksumIter, iter));
543
544 auto fromIter = iter;
545 es = nextLayerWriter.write(msg, iter, size - checksumLen);
546 if (es != comms::ErrorStatus::Success) {
547 return es;
548 }
549
550 COMMS_ASSERT(fromIter <= iter);
551 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
552
553 bool checksumValid = false;
554 auto checksum =
555 thisObj.calculateChecksum(
556 &msg,
557 fromIter,
558 len,
559 checksumValid);
560
561 if (!checksumValid) {
563 }
564
565 thisObj.prepareFieldForWrite(checksum, &msg, field);
566 auto checksumEs = thisObj.writeField(&msg, field, checksumIter, checksumLen);
567 static_cast<void>(checksumEs);
569 return es;
570 }
571
572 template <typename TMsg, typename TIter, typename TWriter>
573 ErrorStatus writeInternalOutput(
574 Field& field,
575 const TMsg& msg,
576 TIter& iter,
577 std::size_t size,
578 TWriter&& nextLayerWriter) const
579 {
580 auto& thisObj = BaseImpl::thisLayer();
581 thisObj.prepareFieldForWrite(0U, &msg, field);
582 auto es = thisObj.writeField(&msg, field, iter, size);
583 if (es != comms::ErrorStatus::Success) {
584 return es;
585 }
586
587 auto fieldLen = thisObj.doFieldLength(msg);
588 es = nextLayerWriter.write(msg, iter, size - fieldLen);
589 if (es != comms::ErrorStatus::Success) {
590 return es;
591 }
592
594 }
595
596 template <typename TMsg, typename TIter, typename TWriter>
597 ErrorStatus writeInternal(
598 Field& field,
599 const TMsg& msg,
600 TIter& iter,
601 std::size_t size,
602 TWriter&& nextLayerWriter,
603 std::random_access_iterator_tag) const
604 {
605 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
606 }
607
608 template <typename TMsg, typename TIter, typename TWriter>
609 ErrorStatus writeInternal(
610 Field& field,
611 const TMsg& msg,
612 TIter& iter,
613 std::size_t size,
614 TWriter&& nextLayerWriter,
615 std::output_iterator_tag) const
616 {
617 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
618 }
619
620 template <typename TMsg, typename TIter>
621 ErrorStatus fieldUpdateInternal(
622 const TMsg* msgPtr,
623 TIter checksumIter,
624 TIter from,
625 TIter to,
626 std::size_t size,
627 Field& field) const
628 {
629 static_cast<void>(size);
630 COMMS_ASSERT(from <= to);
631 auto& thisObj = BaseImpl::thisLayer();
632 auto len = static_cast<std::size_t>(std::distance(from, to));
633 auto fieldLen = Field::maxLength();
634 if (msgPtr != nullptr) {
635 fieldLen = thisObj.doFieldLength(*msgPtr);
636 }
637 COMMS_ASSERT(len == (size - fieldLen));
638
639 bool checksumValid = false;
640 auto checksum =
641 thisObj.calculateChecksum(
642 msgPtr,
643 from,
644 len,
645 checksumValid);
646
647 if (!checksumValid) {
649 }
650
651 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
652 return thisObj.doWriteField(msgPtr, field, checksumIter, fieldLen);
653 }
654};
655
656
657namespace details
658{
659template <typename T>
660struct ChecksumPrefixLayerCheckHelper
661{
662 static const bool Value = false;
663};
664
665template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
666struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
667{
668 static const bool Value = true;
669};
670
671} // namespace details
672
676template <typename T>
677constexpr bool isChecksumPrefixLayer()
678{
679 return details::ChecksumPrefixLayerCheckHelper<T>::Value;
680}
681
682} // namespace protocol
683
684} // namespace comms
685
686COMMS_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.
Base class to all the field classes.
Definition Field.h:33
constexpr bool isChecksumPrefixLayer()
Compile time check of whether the provided type is a variant of ChecksumPrefixLayer.
Definition ChecksumPrefixLayer.h:677
Protocol layer that is responsible to calculate checksum on the data written by all the wrapped inter...
Definition ChecksumPrefixLayer.h:76
comms::ErrorStatus doUpdate(const TMsg &msg, Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:301
comms::ErrorStatus doRead(Field &field, TMsg &msg, TIter &iter, std::size_t size, TNextLayerReader &&nextLayerReader, TExtraValues... extraValues)
Customized read functionality, invoked by read().
Definition ChecksumPrefixLayer.h:170
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumPrefixLayer.h:348
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumPrefixLayer.h:103
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumPrefixLayer.h:134
ChecksumPrefixLayer(const ChecksumPrefixLayer &)=default
Copy constructor.
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumPrefixLayer.h:333
comms::ErrorStatus doWrite(Field &field, const TMsg &msg, TIter &iter, std::size_t size, TNextLayerWriter &&nextLayerWriter) const
Customized write functionality, invoked by write().
Definition ChecksumPrefixLayer.h:242
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumPrefixLayer.h:379
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:265
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumPrefixLayer.h:98
~ChecksumPrefixLayer() noexcept=default
Destructor.
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumPrefixLayer.h:95
ChecksumPrefixLayer()=default
Default constructor.
ChecksumPrefixLayer(ChecksumPrefixLayer &&)=default
Move constructor.
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumPrefixLayer.h:396
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumPrefixLayer.h:365
Base class for all the middle (non MsgDataLayer) protocol transport layers.
Definition ProtocolLayerBase.h:61
typename details::ProtocolLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition ProtocolLayerBase.h:96
TField Field
Type of the field used for this layer.
Definition ProtocolLayerBase.h:64
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.
Disallow usage of ProtocolLayerForceReadUntilDataSplit option in earlier (outer wrapping) layers.
Definition options.h:1122
Replacement to some types from standard type_traits.