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#include <utility>
24
25COMMS_MSVC_WARNING_PUSH
26COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
27
28namespace comms
29{
30
31namespace frame
32{
63template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
64class ChecksumLayer : public comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>
65{
66 using BaseImpl = comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>;
67 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
68
69public:
71 using Field = typename BaseImpl::Field;
72
74 using ChecksumCalc = TCalc;
75
79 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
80
82 ChecksumLayer() = default;
83
85 ChecksumLayer(const ChecksumLayer&) = default;
86
89
91 ~ChecksumLayer() noexcept = default;
92
94 ChecksumLayer& operator=(const ChecksumLayer&) = default;
95
97 ChecksumLayer& operator=(ChecksumLayer&&) = default;
98
103 static constexpr bool hasExtendingClass()
104 {
105 return ParsedOptionsInternal::HasExtendingClass;
106 }
107
110 static constexpr bool hasVerifyBeforeRead()
111 {
112 return ParsedOptionsInternal::HasVerifyBeforeRead;
113 }
114
145 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
147 Field& field,
148 TMsg& msg,
149 TIter& iter,
150 std::size_t size,
151 TNextLayerReader&& nextLayerReader,
152 TExtraValues... extraValues)
153 {
154 using IterType = typename std::decay<decltype(iter)>::type;
155 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
156 "The read operation is expected to use random access iterator");
157
158 using VerifyTag =
159 typename comms::util::LazyShallowConditional<
160 ParsedOptionsInternal::HasVerifyBeforeRead
161 >::template Type<
162 VerifyBeforeReadTag,
163 VerifyAfterReadTag
164 >;
165
166 return
167 readInternal(
168 field,
169 msg,
170 iter,
171 size,
172 std::forward<TNextLayerReader>(nextLayerReader),
173 VerifyTag(),
174 extraValues...);
175 }
176
202 template <typename TMsg, typename TIter, typename TNextLayerWriter>
204 Field& field,
205 const TMsg& msg,
206 TIter& iter,
207 std::size_t size,
208 TNextLayerWriter&& nextLayerWriter) const
209 {
210 using IterType = typename std::decay<decltype(iter)>::type;
211 using Tag = typename std::iterator_traits<IterType>::iterator_category;
212
213 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
214 }
215
225 template <typename TIter, typename TNextLayerUpdater>
227 Field& field,
228 TIter& iter,
229 std::size_t size,
230 TNextLayerUpdater&& nextLayerUpdater) const
231 {
232 auto fromIter = iter;
233 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
234 if (es != comms::ErrorStatus::Success) {
235 return es;
236 }
237
238 using MsgPtr = typename BaseImpl::MsgPtr;
239 static_assert(
240 !std::is_void<MsgPtr>::value,
241 "Please use update() overload that accepts message object as its first parameter");
242
243 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
244 return fieldUpdateInternal(msgPtr, fromIter, iter, size, field);
245 }
246
256 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
258 const TMsg& msg,
259 Field& field,
260 TIter& iter,
261 std::size_t size,
262 TNextLayerUpdater&& nextLayerUpdater) const
263 {
264 auto fromIter = iter;
265 auto& thisObj = BaseImpl::thisLayer();
266 auto fieldLen = thisObj.doFieldLength(msg);
267 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
268 if (es != comms::ErrorStatus::Success) {
269 return es;
270 }
271
272 return fieldUpdateInternal(&msg, fromIter, iter, size, field);
273 }
274
275protected:
285 template <typename TMsg, typename TIter>
286 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
287 {
288 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
289 }
290
300 template <typename TMsg, typename TIter>
301 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
302 {
303 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
304 }
305
317 template <typename TMsg, typename TIter>
318 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
319 {
320 static_cast<void>(msg);
321 checksumValid = true;
322 return TCalc()(iter, len);
323 }
324
332 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
333 {
334 return field.getValue();
335 }
336
348 template <typename TChecksum, typename TMsg>
349 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
350 {
351 static_cast<void>(msg);
352 field.setValue(checksum);
353 }
354
355private:
356 template <typename... TParams>
357 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
358
359 template <typename... TParams>
360 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
361
362 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
363 ErrorStatus verifyRead(
364 Field& field,
365 TMsg& msg,
366 TIter& iter,
367 std::size_t size,
368 TReader&& nextLayerReader,
369 TExtraValues... extraValues)
370 {
371 auto fromIter = iter;
372 auto* msgPtr = BaseImpl::toMsgPtr(msg);
373 auto fieldLen = Field::minLength();
374 auto& thisObj = BaseImpl::thisLayer();
375 if (msgPtr != nullptr) {
376 fieldLen = thisObj.doFieldLength(*msgPtr);
377 }
378 auto toIter = fromIter + (size - fieldLen);
379 auto len = static_cast<std::size_t>(std::distance(fromIter, toIter));
380
381 auto checksumEs = thisObj.readField(msgPtr, field, toIter, fieldLen);
382 if (checksumEs != ErrorStatus::Success) {
383 return checksumEs;
384 }
385
386 bool checksumValid = false;
387 auto checksum =
388 thisObj.calculateChecksum(
389 msgPtr,
390 fromIter,
391 len,
392 checksumValid);
393
394 if (!checksumValid) {
396 }
397
398 auto expectedValue = thisObj.getChecksumFromField(field);
399
400 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
401 BaseImpl::resetMsg(msg);
402 return ErrorStatus::ProtocolError;
403 }
404
405 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
406 if (es == ErrorStatus::Success) {
407 iter = toIter;
408 }
409
410 return es;
411 }
412
413 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
414 ErrorStatus readVerify(
415 Field& field,
416 TMsg& msg,
417 TIter& iter,
418 std::size_t size,
419 TReader&& nextLayerReader,
420 TExtraValues... extraValues)
421 {
422 auto fromIter = iter;
423
424 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
425 if ((es == ErrorStatus::NotEnoughData) ||
426 (es == ErrorStatus::ProtocolError)) {
427 return es;
428 }
429
430 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
431 COMMS_ASSERT(len <= size);
432 auto remSize = size - len;
433 auto* msgPtr = BaseImpl::toMsgPtr(msg);
434 auto& thisObj = BaseImpl::thisLayer();
435 auto checksumEs = thisObj.readField(msgPtr, field, iter, remSize);
436 if (checksumEs == ErrorStatus::NotEnoughData) {
437 BaseImpl::updateMissingSize(field, remSize, extraValues...);
438 }
439
440 if (checksumEs != ErrorStatus::Success) {
441 BaseImpl::resetMsg(msg);
442 return checksumEs;
443 }
444
445 bool checksumValid = false;
446 auto checksum =
447 thisObj.calculateChecksum(
448 BaseImpl::toMsgPtr(msg),
449 fromIter,
450 len,
451 checksumValid);
452
453 if (!checksumValid) {
455 }
456
457 auto expectedValue = thisObj.getChecksumFromField(field);
458
459 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
460 BaseImpl::resetMsg(msg);
461 return ErrorStatus::ProtocolError;
462 }
463
464 return es;
465 }
466
467 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
468 ErrorStatus readInternal(
469 Field& field,
470 TMsg& msg,
471 TIter& iter,
472 std::size_t size,
473 TReader&& nextLayerReader,
474 VerifyBeforeReadTag<>,
475 TExtraValues... extraValues)
476 {
477 return
478 verifyRead(
479 field,
480 msg,
481 iter,
482 size,
483 std::forward<TReader>(nextLayerReader),
484 extraValues...);
485 }
486
487 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
488 ErrorStatus readInternal(
489 Field& field,
490 TMsg& msg,
491 TIter& iter,
492 std::size_t size,
493 TReader&& nextLayerReader,
494 VerifyAfterReadTag<>,
495 TExtraValues... extraValues)
496 {
497 return
498 readVerify(
499 field,
500 msg,
501 iter,
502 size,
503 std::forward<TReader>(nextLayerReader),
504 extraValues...);
505 }
506
507 template <typename TMsg, typename TIter, typename TWriter>
508 ErrorStatus writeInternalRandomAccess(
509 Field& field,
510 const TMsg& msg,
511 TIter& iter,
512 std::size_t size,
513 TWriter&& nextLayerWriter) const
514 {
515 auto fromIter = iter;
516 auto es = nextLayerWriter.write(msg, iter, size);
517 if ((es != comms::ErrorStatus::Success) &&
519 return es;
520 }
521
522 COMMS_ASSERT(fromIter <= iter);
523 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
524 auto remSize = size - len;
525 auto& thisObj = BaseImpl::thisLayer();
526
528 thisObj.prepareFieldForWrite(0, &msg, field);
529 auto esTmp = thisObj.writeField(&msg, field, iter, remSize);
530 if (esTmp != comms::ErrorStatus::Success) {
531 return esTmp;
532 }
533
534 return es;
535 }
536
537 bool checksumValid = false;
538 auto checksum =
539 thisObj.calculateChecksum(
540 &msg,
541 fromIter,
542 len,
543 checksumValid);
544
545 if (!checksumValid) {
547 }
548
549 thisObj.prepareFieldForWrite(checksum, &msg, field);
550 return thisObj.writeField(&msg, field, iter, remSize);
551 }
552
553 template <typename TMsg, typename TIter, typename TWriter>
554 ErrorStatus writeInternalOutput(
555 Field& field,
556 const TMsg& msg,
557 TIter& iter,
558 std::size_t size,
559 TWriter&& nextLayerWriter) const
560 {
561 auto& thisObj = BaseImpl::thisLayer();
562 auto fieldLen = thisObj.doFieldLength(msg);
563 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
564 if ((es != comms::ErrorStatus::Success) &&
566 return es;
567 }
568
569 auto esTmp = thisObj.writeField(&msg, field, iter, fieldLen);
570 static_cast<void>(esTmp);
573 }
574
575 template <typename TMsg, typename TIter, typename TWriter>
576 ErrorStatus writeInternal(
577 Field& field,
578 const TMsg& msg,
579 TIter& iter,
580 std::size_t size,
581 TWriter&& nextLayerWriter,
582 std::random_access_iterator_tag) const
583 {
584 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
585 }
586
587 template <typename TMsg, typename TIter, typename TWriter>
588 ErrorStatus writeInternal(
589 Field& field,
590 const TMsg& msg,
591 TIter& iter,
592 std::size_t size,
593 TWriter&& nextLayerWriter,
594 std::output_iterator_tag) const
595 {
596 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
597 }
598
599 template <typename TMsg, typename TIter>
600 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
601 {
602 COMMS_ASSERT(from <= to);
603 auto len = static_cast<std::size_t>(std::distance(from, to));
604 auto& thisObj = BaseImpl::thisLayer();
605 if (msgPtr != nullptr) {
606 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
607 }
608 else {
609 COMMS_ASSERT(len == (size - Field::maxLength()));
610 }
611 auto remSize = size - len;
612
613 bool checksumValid = false;
614 auto checksum =
615 thisObj.calculateChecksum(
616 msgPtr,
617 from,
618 len,
619 checksumValid);
620
621 if (!checksumValid) {
623 }
624
625 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
626 return thisObj.writeField(msgPtr, field, to, remSize);
627 }
628};
629
630namespace details
631{
632template <typename T>
633struct ChecksumLayerCheckHelper
634{
635 static const bool Value = false;
636};
637
638template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
639struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
640{
641 static const bool Value = true;
642};
643
644} // namespace details
645
649template <typename T>
650constexpr bool isChecksumLayer()
651{
652 return details::ChecksumLayerCheckHelper<T>::Value;
653}
654
655} // namespace frame
656
657} // namespace comms
658
659COMMS_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:65
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumLayer.h:110
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumLayer.h:71
~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:318
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumLayer.h:349
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:257
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumLayer.h:332
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumLayer.h:74
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:146
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumLayer.h:301
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumLayer.h:286
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:203
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:226
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:650
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumLayer.h:79
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:96
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.