COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
ChecksumPrefixLayer.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
15#include "comms/frame/details/ChecksumLayerOptionsParser.h"
16#include "comms/frame/details/ChecksumPrefixLayerBase.h"
18
19#include <iterator>
20#include <type_traits>
21#include <utility>
22
23COMMS_MSVC_WARNING_PUSH
24COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
25
26namespace comms
27{
28
29namespace frame
30{
64template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
65class ChecksumPrefixLayer : public comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>
66{
67 using BaseImpl = comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>;
68 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
69
70public:
71
73 using Field = typename BaseImpl::Field;
74
76 using ChecksumCalc = TCalc;
77
81 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
82
85
88
91
93 ~ChecksumPrefixLayer() noexcept = default;
94
96 ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
97
100
105 static constexpr bool hasExtendingClass()
106 {
107 return ParsedOptionsInternal::HasExtendingClass;
108 }
109
112 static constexpr bool hasVerifyBeforeRead()
113 {
114 return ParsedOptionsInternal::HasVerifyBeforeRead;
115 }
116
147 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
149 Field& field,
150 TMsg& msg,
151 TIter& iter,
152 std::size_t size,
153 TNextLayerReader&& nextLayerReader,
154 TExtraValues... extraValues)
155 {
156 using IterType = typename std::decay<decltype(iter)>::type;
157 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
158 "The read operation is expected to use random access iterator");
159
160 auto* msgPtr = BaseImpl::toMsgPtr(msg);
161 auto& thisObj = BaseImpl::thisLayer();
162 auto beforeFieldReadIter = iter;
163 auto checksumEs = thisObj.readField(msgPtr, field, iter, size);
164 if (checksumEs == ErrorStatus::NotEnoughData) {
165 BaseImpl::updateMissingSize(field, size, extraValues...);
166 }
167
168 if (checksumEs != ErrorStatus::Success) {
169 return checksumEs;
170 }
171
172 using VerifyTag =
173 typename comms::util::LazyShallowConditional<
174 ParsedOptionsInternal::HasVerifyBeforeRead
175 >::template Type<
176 VerifyBeforeReadTag,
177 VerifyAfterReadTag
178 >;
179
180 auto fieldLen = static_cast<std::size_t>(std::distance(beforeFieldReadIter, iter));
181 return
182 readInternal(
183 field,
184 msg,
185 iter,
186 size - fieldLen,
187 std::forward<TNextLayerReader>(nextLayerReader),
188 VerifyTag(),
189 extraValues...);
190 }
191
219 template <typename TMsg, typename TIter, typename TNextLayerWriter>
221 Field& field,
222 const TMsg& msg,
223 TIter& iter,
224 std::size_t size,
225 TNextLayerWriter&& nextLayerWriter) const
226 {
227 using IterType = typename std::decay<decltype(iter)>::type;
228 using Tag = typename std::iterator_traits<IterType>::iterator_category;
229
230 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
231 }
232
242 template <typename TIter, typename TNextLayerUpdater>
244 Field& field,
245 TIter& iter,
246 std::size_t size,
247 TNextLayerUpdater&& nextLayerUpdater) const
248 {
249 auto checksumIter = iter;
250 std::advance(iter, Field::maxLength());
251
252 auto fromIter = iter;
253 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
254 if (es != comms::ErrorStatus::Success) {
255 return es;
256 }
257
258 using MsgPtr = typename BaseImpl::MsgPtr;
259 static_assert(
260 !std::is_void<MsgPtr>::value,
261 "Please use update() overload that accepts message object as its first parameter");
262
263 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
264 return fieldUpdateInternal(msgPtr, checksumIter, fromIter, iter, size, field);
265 }
266
278 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
280 const TMsg& msg,
281 Field& field,
282 TIter& iter,
283 std::size_t size,
284 TNextLayerUpdater&& nextLayerUpdater) const
285 {
286 auto& thisObj = BaseImpl::thisLayer();
287 auto checksumIter = iter;
288 auto fieldLen = thisObj.doFieldLength(msg);
289 std::advance(iter, fieldLen);
290
291 auto fromIter = iter;
292 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
293 if (es != comms::ErrorStatus::Success) {
294 return es;
295 }
296
297 return fieldUpdateInternal(&msg, checksumIter, fromIter, iter, size, field);
298 }
299
300protected:
310 template <typename TMsg, typename TIter>
311 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
312 {
313 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
314 }
315
325 template <typename TMsg, typename TIter>
326 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
327 {
328 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
329 }
330
342 template <typename TMsg, typename TIter>
343 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
344 {
345 static_cast<void>(msg);
346 checksumValid = true;
347 return TCalc()(iter, len);
348 }
349
357 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
358 {
359 return field.getValue();
360 }
361
373 template <typename TChecksum, typename TMsg>
374 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
375 {
376 static_cast<void>(msg);
377 field.setValue(checksum);
378 }
379
380private:
381
382 template <typename... TParams>
383 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
384
385 template <typename... TParams>
386 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
387
388 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
389 ErrorStatus verifyRead(
390 Field& field,
391 TMsg& msg,
392 TIter& iter,
393 std::size_t size,
394 TReader&& nextLayerReader,
395 TExtraValues... extraValues)
396 {
397 auto fromIter = iter;
398 auto& thisObj = BaseImpl::thisLayer();
399 auto* msgPtr = BaseImpl::toMsgPtr(msg);
400
401 bool checksumValid = false;
402 auto checksum =
403 thisObj.calculateChecksum(
404 msgPtr,
405 fromIter,
406 size,
407 checksumValid);
408
409 if (!checksumValid) {
411 }
412
413 auto expectedValue = thisObj.getChecksumFromField(field);
414 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
415 BaseImpl::resetMsg(msg);
416 return ErrorStatus::ProtocolError;
417 }
418
419 return nextLayerReader.read(msg, iter, size, extraValues...);
420 }
421
422 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
423 ErrorStatus readVerify(
424 Field& field,
425 TMsg& msg,
426 TIter& iter,
427 std::size_t size,
428 TReader&& nextLayerReader,
429 TExtraValues... extraValues)
430 {
431 auto fromIter = iter;
432
433 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
434 if ((es == ErrorStatus::NotEnoughData) ||
435 (es == ErrorStatus::ProtocolError)) {
436 return es;
437 }
438
439 auto* msgPtr = BaseImpl::toMsgPtr(msg);
440 auto& thisObj = BaseImpl::thisLayer();
441 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
442 bool checksumValid = false;
443 auto checksum =
444 thisObj.calculateChecksum(
445 msgPtr,
446 fromIter,
447 len,
448 checksumValid);
449
450 if (!checksumValid) {
452 }
453
454 auto expectedValue = thisObj.getChecksumFromField(field);
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& thisObj = BaseImpl::thisLayer();
512 auto checksumIter = iter;
513 thisObj.prepareFieldForWrite(0U, &msg, field);
514 auto es = thisObj.writeField(&msg, field, iter, size);
515 if (es != comms::ErrorStatus::Success) {
516 return es;
517 }
518
519 auto checksumLen =
520 static_cast<std::size_t>(std::distance(checksumIter, iter));
521
522 auto fromIter = iter;
523 es = nextLayerWriter.write(msg, iter, size - checksumLen);
524 if (es != comms::ErrorStatus::Success) {
525 return es;
526 }
527
528 COMMS_ASSERT(fromIter <= iter);
529 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
530
531 bool checksumValid = false;
532 auto checksum =
533 thisObj.calculateChecksum(
534 &msg,
535 fromIter,
536 len,
537 checksumValid);
538
539 if (!checksumValid) {
541 }
542
543 thisObj.prepareFieldForWrite(checksum, &msg, field);
544 auto checksumEs = thisObj.writeField(&msg, field, checksumIter, checksumLen);
545 static_cast<void>(checksumEs);
547 return es;
548 }
549
550 template <typename TMsg, typename TIter, typename TWriter>
551 ErrorStatus writeInternalOutput(
552 Field& field,
553 const TMsg& msg,
554 TIter& iter,
555 std::size_t size,
556 TWriter&& nextLayerWriter) const
557 {
558 auto& thisObj = BaseImpl::thisLayer();
559 thisObj.prepareFieldForWrite(0U, &msg, field);
560 auto es = thisObj.writeField(&msg, field, iter, size);
561 if (es != comms::ErrorStatus::Success) {
562 return es;
563 }
564
565 auto fieldLen = thisObj.doFieldLength(msg);
566 es = nextLayerWriter.write(msg, iter, size - fieldLen);
567 if (es != comms::ErrorStatus::Success) {
568 return es;
569 }
570
572 }
573
574 template <typename TMsg, typename TIter, typename TWriter>
575 ErrorStatus writeInternal(
576 Field& field,
577 const TMsg& msg,
578 TIter& iter,
579 std::size_t size,
580 TWriter&& nextLayerWriter,
581 std::random_access_iterator_tag) const
582 {
583 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
584 }
585
586 template <typename TMsg, typename TIter, typename TWriter>
587 ErrorStatus writeInternal(
588 Field& field,
589 const TMsg& msg,
590 TIter& iter,
591 std::size_t size,
592 TWriter&& nextLayerWriter,
593 std::output_iterator_tag) const
594 {
595 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
596 }
597
598 template <typename TMsg, typename TIter>
599 ErrorStatus fieldUpdateInternal(
600 const TMsg* msgPtr,
601 TIter checksumIter,
602 TIter from,
603 TIter to,
604 std::size_t size,
605 Field& field) const
606 {
607 static_cast<void>(size);
608 COMMS_ASSERT(from <= to);
609 auto& thisObj = BaseImpl::thisLayer();
610 auto len = static_cast<std::size_t>(std::distance(from, to));
611 auto fieldLen = Field::maxLength();
612 if (msgPtr != nullptr) {
613 fieldLen = thisObj.doFieldLength(*msgPtr);
614 }
615 COMMS_ASSERT(len == (size - fieldLen));
616
617 bool checksumValid = false;
618 auto checksum =
619 thisObj.calculateChecksum(
620 msgPtr,
621 from,
622 len,
623 checksumValid);
624
625 if (!checksumValid) {
627 }
628
629 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
630 return thisObj.doWriteField(msgPtr, field, checksumIter, fieldLen);
631 }
632};
633
634
635namespace details
636{
637template <typename T>
638struct ChecksumPrefixLayerCheckHelper
639{
640 static const bool Value = false;
641};
642
643template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
644struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
645{
646 static const bool Value = true;
647};
648
649} // namespace details
650
654template <typename T>
655constexpr bool isChecksumPrefixLayer()
656{
657 return details::ChecksumPrefixLayerCheckHelper<T>::Value;
658}
659
660} // namespace frame
661
662} // namespace comms
663
664COMMS_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:655
Protocol layer that is responsible to calculate checksum on the data written by all the wrapped inter...
Definition ChecksumPrefixLayer.h:66
~ChecksumPrefixLayer() noexcept=default
Destructor.
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:148
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumPrefixLayer.h:374
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:220
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumPrefixLayer.h:76
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:311
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:243
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumPrefixLayer.h:112
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:279
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumPrefixLayer.h:343
ChecksumPrefixLayer(ChecksumPrefixLayer &&)=default
Move constructor.
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumPrefixLayer.h:357
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumPrefixLayer.h:73
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumPrefixLayer.h:81
ChecksumPrefixLayer()=default
Default constructor.
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumPrefixLayer.h:326
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:96
comms::frame::ChecksumPrefixLayer< TField, TCalc, TNextLayer, TOptions... > ChecksumPrefixLayer
Alias to the comms::frame::ChecksumPrefixLayer.
Definition ChecksumPrefixLayer.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.