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 - 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#include "comms/details/tag.h"
23#include "comms/cast.h"
24
25COMMS_MSVC_WARNING_PUSH
26COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
27
28namespace comms
29{
30
31namespace protocol
32{
62template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
63class ChecksumLayer : public
65 TField,
66 TNextLayer,
67 details::ProtocolLayerExtendingClassT<
68 ChecksumLayer<TField, TCalc, TNextLayer, TOptions...>,
69 details::ChecksumLayerOptionsParser<TOptions...>
70 >,
71 comms::option::def::ProtocolLayerDisallowReadUntilDataSplit
72 >
73{
74 using BaseImpl =
76 TField,
77 TNextLayer,
78 details::ProtocolLayerExtendingClassT<
79 ChecksumLayer<TField, TCalc, TNextLayer, TOptions...>,
80 details::ChecksumLayerOptionsParser<TOptions...>
81 >,
83 >;
84
85 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
86
87public:
89 using Field = typename BaseImpl::Field;
90
92 using ChecksumCalc = TCalc;
93
97 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
98
100 ChecksumLayer() = default;
101
103 ChecksumLayer(const ChecksumLayer&) = default;
104
107
109 ~ChecksumLayer() noexcept = default;
110
112 ChecksumLayer& operator=(const ChecksumLayer&) = default;
113
115 ChecksumLayer& operator=(ChecksumLayer&&) = default;
116
121 static constexpr bool hasExtendingClass()
122 {
123 return ParsedOptionsInternal::HasExtendingClass;
124 }
125
128 static constexpr bool hasVerifyBeforeRead()
129 {
130 return ParsedOptionsInternal::HasVerifyBeforeRead;
131 }
132
163 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
165 Field& field,
166 TMsg& msg,
167 TIter& iter,
168 std::size_t size,
169 TNextLayerReader&& nextLayerReader,
170 TExtraValues... extraValues)
171 {
172 using IterType = typename std::decay<decltype(iter)>::type;
173 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
174 "The read operation is expected to use random access iterator");
175
176 using VerifyTag =
177 typename comms::util::LazyShallowConditional<
178 ParsedOptionsInternal::HasVerifyBeforeRead
179 >::template Type<
180 VerifyBeforeReadTag,
181 VerifyAfterReadTag
182 >;
183
184 return
185 readInternal(
186 field,
187 msg,
188 iter,
189 size,
190 std::forward<TNextLayerReader>(nextLayerReader),
191 VerifyTag(),
192 extraValues...);
193 }
194
220 template <typename TMsg, typename TIter, typename TNextLayerWriter>
222 Field& field,
223 const TMsg& msg,
224 TIter& iter,
225 std::size_t size,
226 TNextLayerWriter&& nextLayerWriter) const
227 {
228 using IterType = typename std::decay<decltype(iter)>::type;
229 using Tag = typename std::iterator_traits<IterType>::iterator_category;
230
231 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
232 }
233
243 template <typename TIter, typename TNextLayerUpdater>
245 Field& field,
246 TIter& iter,
247 std::size_t size,
248 TNextLayerUpdater&& nextLayerUpdater) const
249 {
250 auto fromIter = iter;
251 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
252 if (es != comms::ErrorStatus::Success) {
253 return es;
254 }
255
256 using MsgPtr = typename BaseImpl::MsgPtr;
257 static_assert(
258 !std::is_void<MsgPtr>::value,
259 "Please use update() overload that accepts message object as its first parameter");
260
261 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
262 return fieldUpdateInternal(msgPtr, fromIter, iter, size, field);
263 }
264
274 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
276 const TMsg& msg,
277 Field& field,
278 TIter& iter,
279 std::size_t size,
280 TNextLayerUpdater&& nextLayerUpdater) const
281 {
282 auto fromIter = iter;
283 auto& thisObj = BaseImpl::thisLayer();
284 auto fieldLen = thisObj.doFieldLength(msg);
285 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
286 if (es != comms::ErrorStatus::Success) {
287 return es;
288 }
289
290 return fieldUpdateInternal(&msg, fromIter, iter, size, field);
291 }
292
293protected:
303 template <typename TMsg, typename TIter>
304 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
305 {
306 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
307 }
308
318 template <typename TMsg, typename TIter>
319 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
320 {
321 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
322 }
323
335 template <typename TMsg, typename TIter>
336 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
337 {
338 static_cast<void>(msg);
339 checksumValid = true;
340 return TCalc()(iter, len);
341 }
342
350 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
351 {
352 return field.getValue();
353 }
354
366 template <typename TChecksum, typename TMsg>
367 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
368 {
369 static_cast<void>(msg);
370 field.setValue(checksum);
371 }
372
373private:
374 template <typename... TParams>
375 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
376
377 template <typename... TParams>
378 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
379
380 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
381 ErrorStatus verifyRead(
382 Field& field,
383 TMsg& msg,
384 TIter& iter,
385 std::size_t size,
386 TReader&& nextLayerReader,
387 TExtraValues... extraValues)
388 {
389 auto fromIter = iter;
390 auto* msgPtr = BaseImpl::toMsgPtr(msg);
391 auto fieldLen = Field::minLength();
392 auto& thisObj = BaseImpl::thisLayer();
393 if (msgPtr != nullptr) {
394 fieldLen = thisObj.doFieldLength(*msgPtr);
395 }
396 auto toIter = fromIter + (size - fieldLen);
397 auto len = static_cast<std::size_t>(std::distance(fromIter, toIter));
398
399 auto checksumEs = thisObj.readField(msgPtr, field, toIter, fieldLen);
400 if (checksumEs != ErrorStatus::Success) {
401 return checksumEs;
402 }
403
404 bool checksumValid = false;
405 auto checksum =
406 thisObj.calculateChecksum(
407 msgPtr,
408 fromIter,
409 len,
410 checksumValid);
411
412 if (!checksumValid) {
414 }
415
416 auto expectedValue = thisObj.getChecksumFromField(field);
417
418 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
419 BaseImpl::resetMsg(msg);
420 return ErrorStatus::ProtocolError;
421 }
422
423 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
424 if (es == ErrorStatus::Success) {
425 iter = toIter;
426 }
427
428 return es;
429 }
430
431 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
432 ErrorStatus readVerify(
433 Field& field,
434 TMsg& msg,
435 TIter& iter,
436 std::size_t size,
437 TReader&& nextLayerReader,
438 TExtraValues... extraValues)
439 {
440 auto fromIter = iter;
441
442 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
443 if ((es == ErrorStatus::NotEnoughData) ||
444 (es == ErrorStatus::ProtocolError)) {
445 return es;
446 }
447
448 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
449 COMMS_ASSERT(len <= size);
450 auto remSize = size - len;
451 auto* msgPtr = BaseImpl::toMsgPtr(msg);
452 auto& thisObj = BaseImpl::thisLayer();
453 auto checksumEs = thisObj.readField(msgPtr, field, iter, remSize);
454 if (checksumEs == ErrorStatus::NotEnoughData) {
455 BaseImpl::updateMissingSize(field, remSize, extraValues...);
456 }
457
458 if (checksumEs != ErrorStatus::Success) {
459 BaseImpl::resetMsg(msg);
460 return checksumEs;
461 }
462
463 bool checksumValid = false;
464 auto checksum =
465 thisObj.calculateChecksum(
466 BaseImpl::toMsgPtr(msg),
467 fromIter,
468 len,
469 checksumValid);
470
471 if (!checksumValid) {
473 }
474
475 auto expectedValue = thisObj.getChecksumFromField(field);
476
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 fromIter = iter;
534 auto es = nextLayerWriter.write(msg, iter, size);
535 if ((es != comms::ErrorStatus::Success) &&
537 return es;
538 }
539
540 COMMS_ASSERT(fromIter <= iter);
541 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
542 auto remSize = size - len;
543 auto& thisObj = BaseImpl::thisLayer();
544
546 thisObj.prepareFieldForWrite(0, &msg, field);
547 auto esTmp = thisObj.writeField(&msg, field, iter, remSize);
548 if (esTmp != comms::ErrorStatus::Success) {
549 return esTmp;
550 }
551
552 return es;
553 }
554
555 bool checksumValid = false;
556 auto checksum =
557 thisObj.calculateChecksum(
558 &msg,
559 fromIter,
560 len,
561 checksumValid);
562
563 if (!checksumValid) {
565 }
566
567 thisObj.prepareFieldForWrite(checksum, &msg, field);
568 return thisObj.writeField(&msg, field, iter, remSize);
569 }
570
571 template <typename TMsg, typename TIter, typename TWriter>
572 ErrorStatus writeInternalOutput(
573 Field& field,
574 const TMsg& msg,
575 TIter& iter,
576 std::size_t size,
577 TWriter&& nextLayerWriter) const
578 {
579 auto& thisObj = BaseImpl::thisLayer();
580 auto fieldLen = thisObj.doFieldLength(msg);
581 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
582 if ((es != comms::ErrorStatus::Success) &&
584 return es;
585 }
586
587 auto esTmp = thisObj.writeField(&msg, field, iter, fieldLen);
588 static_cast<void>(esTmp);
591 }
592
593 template <typename TMsg, typename TIter, typename TWriter>
594 ErrorStatus writeInternal(
595 Field& field,
596 const TMsg& msg,
597 TIter& iter,
598 std::size_t size,
599 TWriter&& nextLayerWriter,
600 std::random_access_iterator_tag) const
601 {
602 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
603 }
604
605 template <typename TMsg, typename TIter, typename TWriter>
606 ErrorStatus writeInternal(
607 Field& field,
608 const TMsg& msg,
609 TIter& iter,
610 std::size_t size,
611 TWriter&& nextLayerWriter,
612 std::output_iterator_tag) const
613 {
614 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
615 }
616
617 template <typename TMsg, typename TIter>
618 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
619 {
620 COMMS_ASSERT(from <= to);
621 auto len = static_cast<std::size_t>(std::distance(from, to));
622 auto& thisObj = BaseImpl::thisLayer();
623 if (msgPtr != nullptr) {
624 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
625 }
626 else {
627 COMMS_ASSERT(len == (size - Field::maxLength()));
628 }
629 auto remSize = size - len;
630
631 bool checksumValid = false;
632 auto checksum =
633 thisObj.calculateChecksum(
634 msgPtr,
635 from,
636 len,
637 checksumValid);
638
639 if (!checksumValid) {
641 }
642
643 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
644 return thisObj.writeField(msgPtr, field, to, remSize);
645 }
646};
647
648namespace details
649{
650template <typename T>
651struct ChecksumLayerCheckHelper
652{
653 static const bool Value = false;
654};
655
656template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
657struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
658{
659 static const bool Value = true;
660};
661
662} // namespace details
663
667template <typename T>
668constexpr bool isChecksumLayer()
669{
670 return details::ChecksumLayerCheckHelper<T>::Value;
671}
672
673} // namespace protocol
674
675} // namespace comms
676
677COMMS_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:73
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumLayer.h:92
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumLayer.h:336
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:275
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:221
ChecksumLayer(ChecksumLayer &&)=default
Move constructor.
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumLayer.h:350
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumLayer.h:304
ChecksumLayer(const ChecksumLayer &)=default
Copy constructor.
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:164
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumLayer.h:367
~ChecksumLayer() noexcept=default
Destructor.
ChecksumLayer()=default
Default constructor.
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumLayer.h:97
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumLayer.h:244
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumLayer.h:319
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumLayer.h:128
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumLayer.h:89
constexpr bool isChecksumLayer()
Compile time check of whether the provided type is a variant of ChecksumLayer.
Definition ChecksumLayer.h:668
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.