COMMS
Template library intended to help with implementation of communication protocols.
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 
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 #include "comms/details/tag.h"
23 #include "comms/cast.h"
24 
25 COMMS_MSVC_WARNING_PUSH
26 COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
27 
28 namespace comms
29 {
30 
31 namespace protocol
32 {
62 template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
63 class 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 
87 public:
89  using Field = typename BaseImpl::Field;
90 
92  using ChecksumCalc = TCalc;
93 
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 
293 protected:
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 
373 private:
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 
648 namespace details
649 {
650 template <typename T>
651 struct ChecksumLayerCheckHelper
652 {
653  static const bool Value = false;
654 };
655 
656 template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
657 struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
658 {
659  static const bool Value = true;
660 };
661 
662 } // namespace details
663 
667 template <typename T>
668 constexpr bool isChecksumLayer()
669 {
670  return details::ChecksumLayerCheckHelper<T>::Value;
671 }
672 
673 } // namespace protocol
674 
675 } // namespace comms
676 
677 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.
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
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.