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 - 2026 (C). Alex Robenko. All rights reserved.
3//
4// SPDX-License-Identifier: MPL-2.0
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
12
13#pragma once
14
15#include "comms/cast.h"
17#include "comms/details/tag.h"
19#include "comms/frame/details/ChecksumLayerBase.h"
20#include "comms/frame/details/ChecksumLayerOptionsParser.h"
22
23#include <cstddef>
24#include <iterator>
25#include <type_traits>
26#include <utility>
27
28COMMS_MSVC_WARNING_PUSH
29COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
30
31namespace comms
32{
33
34namespace frame
35{
66template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
67class ChecksumLayer : public comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>
68{
69 using BaseImpl = comms::frame::details::ChecksumLayerBase<TField, TCalc, TNextLayer, TOptions...>;
70 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
71
72public:
74 using Field = typename BaseImpl::Field;
75
77 using ChecksumCalc = TCalc;
78
80 ChecksumLayer() = default;
81
83 ChecksumLayer(const ChecksumLayer&) = default;
84
87
89 ~ChecksumLayer() noexcept = default;
90
92 ChecksumLayer& operator=(const ChecksumLayer&) = default;
93
95 ChecksumLayer& operator=(ChecksumLayer&&) = default;
96
99 static constexpr bool hasExtendingClass()
100 {
101 return ParsedOptionsInternal::HasExtendingClass;
102 }
103
106 static constexpr bool hasVerifyBeforeRead()
107 {
108 return ParsedOptionsInternal::HasVerifyBeforeRead;
109 }
110
141 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
143 Field& field,
144 TMsg& msg,
145 TIter& iter,
146 std::size_t size,
147 TNextLayerReader&& nextLayerReader,
148 TExtraValues... extraValues)
149 {
150 using IterType = typename std::decay<decltype(iter)>::type;
151 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
152 "The read operation is expected to use random access iterator");
153
154 using VerifyTag =
155 typename comms::util::LazyShallowConditional<
156 ParsedOptionsInternal::HasVerifyBeforeRead
157 >::template Type<
158 VerifyBeforeReadTag,
159 VerifyAfterReadTag
160 >;
161
162 return
163 readInternal(
164 field,
165 msg,
166 iter,
167 size,
168 std::forward<TNextLayerReader>(nextLayerReader),
169 VerifyTag(),
170 extraValues...);
171 }
172
198 template <typename TMsg, typename TIter, typename TNextLayerWriter>
200 Field& field,
201 const TMsg& msg,
202 TIter& iter,
203 std::size_t size,
204 TNextLayerWriter&& nextLayerWriter) const
205 {
206 using IterType = typename std::decay<decltype(iter)>::type;
207 using Tag = typename std::iterator_traits<IterType>::iterator_category;
208
209 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
210 }
211
221 template <typename TIter, typename TNextLayerUpdater>
223 Field& field,
224 TIter& iter,
225 std::size_t size,
226 TNextLayerUpdater&& nextLayerUpdater) const
227 {
228 auto fromIter = iter;
229 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
230 if (es != comms::ErrorStatus::Success) {
231 return es;
232 }
233
234 using MsgPtr = typename BaseImpl::MsgPtr;
235 static_assert(
236 !std::is_void<MsgPtr>::value,
237 "Please use update() overload that accepts message object as its first parameter");
238
239 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
240 return fieldUpdateInternal(msgPtr, fromIter, iter, size, field);
241 }
242
252 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
254 const TMsg& msg,
255 Field& field,
256 TIter& iter,
257 std::size_t size,
258 TNextLayerUpdater&& nextLayerUpdater) const
259 {
260 auto fromIter = iter;
261 auto& thisObj = BaseImpl::thisLayer();
262 auto fieldLen = thisObj.doFieldLength(msg);
263 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
264 if (es != comms::ErrorStatus::Success) {
265 return es;
266 }
267
268 return fieldUpdateInternal(&msg, fromIter, iter, size, field);
269 }
270
271protected:
281 template <typename TMsg, typename TIter>
282 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
283 {
284 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
285 }
286
296 template <typename TMsg, typename TIter>
297 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
298 {
299 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
300 }
301
313 template <typename TMsg, typename TIter>
314 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
315 {
316 static_cast<void>(msg);
317 checksumValid = true;
318 return TCalc()(iter, len);
319 }
320
328 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
329 {
330 return field.getValue();
331 }
332
344 template <typename TChecksum, typename TMsg>
345 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
346 {
347 static_cast<void>(msg);
348 field.setValue(checksum);
349 }
350
351private:
352 template <typename... TParams>
353 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
354
355 template <typename... TParams>
356 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
357
358 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
359 ErrorStatus verifyRead(
360 Field& field,
361 TMsg& msg,
362 TIter& iter,
363 std::size_t size,
364 TReader&& nextLayerReader,
365 TExtraValues... extraValues)
366 {
367 auto fromIter = iter;
368 auto* msgPtr = BaseImpl::toMsgPtr(msg);
369 auto fieldLen = Field::minLength();
370 auto& thisObj = BaseImpl::thisLayer();
371 if (msgPtr != nullptr) {
372 fieldLen = thisObj.doFieldLength(*msgPtr);
373 }
374 auto toIter = fromIter + (size - fieldLen);
375 auto len = static_cast<std::size_t>(std::distance(fromIter, toIter));
376
377 auto checksumEs = thisObj.readField(msgPtr, field, toIter, fieldLen);
378 if (checksumEs != ErrorStatus::Success) {
379 return checksumEs;
380 }
381
382 bool checksumValid = false;
383 auto checksum =
384 thisObj.calculateChecksum(
385 msgPtr,
386 fromIter,
387 len,
388 checksumValid);
389
390 if (!checksumValid) {
392 }
393
394 auto expectedValue = thisObj.getChecksumFromField(field);
395
396 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
397 BaseImpl::resetMsg(msg);
398 return ErrorStatus::ProtocolError;
399 }
400
401 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
402 if (es == ErrorStatus::Success) {
403 iter = toIter;
404 }
405
406 return es;
407 }
408
409 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
410 ErrorStatus readVerify(
411 Field& field,
412 TMsg& msg,
413 TIter& iter,
414 std::size_t size,
415 TReader&& nextLayerReader,
416 TExtraValues... extraValues)
417 {
418 auto fromIter = iter;
419
420 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
421 if ((es == ErrorStatus::NotEnoughData) ||
422 (es == ErrorStatus::ProtocolError)) {
423 return es;
424 }
425
426 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
427 COMMS_ASSERT(len <= size);
428 auto remSize = size - len;
429 auto* msgPtr = BaseImpl::toMsgPtr(msg);
430 auto& thisObj = BaseImpl::thisLayer();
431 auto checksumEs = thisObj.readField(msgPtr, field, iter, remSize);
432 if (checksumEs == ErrorStatus::NotEnoughData) {
433 BaseImpl::updateMissingSize(field, remSize, extraValues...);
434 }
435
436 if (checksumEs != ErrorStatus::Success) {
437 BaseImpl::resetMsg(msg);
438 return checksumEs;
439 }
440
441 bool checksumValid = false;
442 auto checksum =
443 thisObj.calculateChecksum(
444 BaseImpl::toMsgPtr(msg),
445 fromIter,
446 len,
447 checksumValid);
448
449 if (!checksumValid) {
451 }
452
453 auto expectedValue = thisObj.getChecksumFromField(field);
454
455 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
456 BaseImpl::resetMsg(msg);
457 return ErrorStatus::ProtocolError;
458 }
459
460 return es;
461 }
462
463 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
464 ErrorStatus readInternal(
465 Field& field,
466 TMsg& msg,
467 TIter& iter,
468 std::size_t size,
469 TReader&& nextLayerReader,
470 VerifyBeforeReadTag<>,
471 TExtraValues... extraValues)
472 {
473 return
474 verifyRead(
475 field,
476 msg,
477 iter,
478 size,
479 std::forward<TReader>(nextLayerReader),
480 extraValues...);
481 }
482
483 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
484 ErrorStatus readInternal(
485 Field& field,
486 TMsg& msg,
487 TIter& iter,
488 std::size_t size,
489 TReader&& nextLayerReader,
490 VerifyAfterReadTag<>,
491 TExtraValues... extraValues)
492 {
493 return
494 readVerify(
495 field,
496 msg,
497 iter,
498 size,
499 std::forward<TReader>(nextLayerReader),
500 extraValues...);
501 }
502
503 template <typename TMsg, typename TIter, typename TWriter>
504 ErrorStatus writeInternalRandomAccess(
505 Field& field,
506 const TMsg& msg,
507 TIter& iter,
508 std::size_t size,
509 TWriter&& nextLayerWriter) const
510 {
511 auto fromIter = iter;
512 auto es = nextLayerWriter.write(msg, iter, size);
513 if ((es != comms::ErrorStatus::Success) &&
515 return es;
516 }
517
518 COMMS_ASSERT(fromIter <= iter);
519 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
520 auto remSize = size - len;
521 auto& thisObj = BaseImpl::thisLayer();
522
524 thisObj.prepareFieldForWrite(0, &msg, field);
525 auto esTmp = thisObj.writeField(&msg, field, iter, remSize);
526 if (esTmp != comms::ErrorStatus::Success) {
527 return esTmp;
528 }
529
530 return es;
531 }
532
533 bool checksumValid = false;
534 auto checksum =
535 thisObj.calculateChecksum(
536 &msg,
537 fromIter,
538 len,
539 checksumValid);
540
541 if (!checksumValid) {
543 }
544
545 thisObj.prepareFieldForWrite(checksum, &msg, field);
546 return thisObj.writeField(&msg, field, iter, remSize);
547 }
548
549 template <typename TMsg, typename TIter, typename TWriter>
550 ErrorStatus writeInternalOutput(
551 Field& field,
552 const TMsg& msg,
553 TIter& iter,
554 std::size_t size,
555 TWriter&& nextLayerWriter) const
556 {
557 auto& thisObj = BaseImpl::thisLayer();
558 auto fieldLen = thisObj.doFieldLength(msg);
559 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
560 if ((es != comms::ErrorStatus::Success) &&
562 return es;
563 }
564
565 auto esTmp = thisObj.writeField(&msg, field, iter, fieldLen);
566 static_cast<void>(esTmp);
569 }
570
571 template <typename TMsg, typename TIter, typename TWriter>
572 ErrorStatus writeInternal(
573 Field& field,
574 const TMsg& msg,
575 TIter& iter,
576 std::size_t size,
577 TWriter&& nextLayerWriter,
578 std::random_access_iterator_tag) const
579 {
580 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
581 }
582
583 template <typename TMsg, typename TIter, typename TWriter>
584 ErrorStatus writeInternal(
585 Field& field,
586 const TMsg& msg,
587 TIter& iter,
588 std::size_t size,
589 TWriter&& nextLayerWriter,
590 std::output_iterator_tag) const
591 {
592 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
593 }
594
595 template <typename TMsg, typename TIter>
596 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
597 {
598 COMMS_ASSERT(from <= to);
599 auto len = static_cast<std::size_t>(std::distance(from, to));
600 auto& thisObj = BaseImpl::thisLayer();
601 if (msgPtr != nullptr) {
602 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
603 }
604 else {
605 COMMS_ASSERT(len == (size - Field::maxLength()));
606 }
607 auto remSize = size - len;
608
609 bool checksumValid = false;
610 auto checksum =
611 thisObj.calculateChecksum(
612 msgPtr,
613 from,
614 len,
615 checksumValid);
616
617 if (!checksumValid) {
619 }
620
621 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
622 return thisObj.writeField(msgPtr, field, to, remSize);
623 }
624};
625
626namespace details
627{
628template <typename T>
629struct ChecksumLayerCheckHelper
630{
631 static const bool Value = false;
632};
633
634template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
635struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
636{
637 static const bool Value = true;
638};
639
640} // namespace details
641
645template <typename T>
646constexpr bool isChecksumLayer()
647{
648 return details::ChecksumLayerCheckHelper<T>::Value;
649}
650
651} // namespace frame
652
653} // namespace comms
654
655COMMS_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:36
Frame layer that is responsible to calculate checksum on the data written by all the wrapped internal...
Definition ChecksumLayer.h:68
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::FrameLayerVerifyBeforeRead options has been used.
Definition ChecksumLayer.h:106
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumLayer.h:74
~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:314
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumLayer.h:345
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:253
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumLayer.h:328
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumLayer.h:77
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:142
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumLayer.h:297
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumLayer.h:282
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:199
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:222
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:646
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:100
comms::frame::ChecksumLayer< TField, TCalc, TNextLayer, TOptions... > ChecksumLayer
Alias to the comms::frame::ChecksumLayer.
Definition ChecksumLayer.h:27
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:19
@ Success
Used to indicate successful outcome of the operation.
Replacement to some types from standard type_traits.