COMMS
Template library intended to help with implementation of communication protocols.
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 
16 #include "comms/CompileControl.h"
17 #include "comms/field/IntValue.h"
18 #include "comms/protocol/details/ProtocolLayerBase.h"
19 #include "comms/protocol/details/ChecksumLayerOptionsParser.h"
20 #include "comms/protocol/details/ProtocolLayerExtendingClassHelper.h"
21 #include "comms/util/type_traits.h"
22 
23 COMMS_MSVC_WARNING_PUSH
24 COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
25 
26 namespace comms
27 {
28 
29 namespace protocol
30 {
63 template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
64 class 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 
92 public:
93 
95  using Field = typename BaseImpl::Field;
96 
98  using ChecksumCalc = TCalc;
99 
104 
106  ChecksumPrefixLayer() = default;
107 
110 
113 
115  ~ChecksumPrefixLayer() noexcept = default;
116 
118  ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
119 
121  ChecksumPrefixLayer& operator=(ChecksumPrefixLayer&&) = default;
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 
322 protected:
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 
402 private:
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 
657 namespace details
658 {
659 template <typename T>
660 struct ChecksumPrefixLayerCheckHelper
661 {
662  static const bool Value = false;
663 };
664 
665 template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
666 struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
667 {
668  static const bool Value = true;
669 };
670 
671 } // namespace details
672 
676 template <typename T>
677 constexpr bool isChecksumPrefixLayer()
678 {
679  return details::ChecksumPrefixLayerCheckHelper<T>::Value;
680 }
681 
682 } // namespace protocol
683 
684 } // namespace comms
685 
686 COMMS_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
comms::option::def::ExtendingClass< T > ExtendingClass
Same as comms::option::def::ExtendingClass.
Definition: options.h:1822
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.