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
22COMMS_MSVC_WARNING_PUSH
23COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
24
25namespace comms
26{
27
28namespace frame
29{
63template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
64class ChecksumPrefixLayer : public comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>
65{
66 using BaseImpl = comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>;
67 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
68
69public:
70
72 using Field = typename BaseImpl::Field;
73
75 using ChecksumCalc = TCalc;
76
80 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
81
84
87
90
92 ~ChecksumPrefixLayer() noexcept = default;
93
95 ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
96
99
104 static constexpr bool hasExtendingClass()
105 {
106 return ParsedOptionsInternal::HasExtendingClass;
107 }
108
111 static constexpr bool hasVerifyBeforeRead()
112 {
113 return ParsedOptionsInternal::HasVerifyBeforeRead;
114 }
115
146 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
148 Field& field,
149 TMsg& msg,
150 TIter& iter,
151 std::size_t size,
152 TNextLayerReader&& nextLayerReader,
153 TExtraValues... extraValues)
154 {
155 using IterType = typename std::decay<decltype(iter)>::type;
156 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
157 "The read operation is expected to use random access iterator");
158
159 auto* msgPtr = BaseImpl::toMsgPtr(msg);
160 auto& thisObj = BaseImpl::thisLayer();
161 auto beforeFieldReadIter = iter;
162 auto checksumEs = thisObj.readField(msgPtr, field, iter, size);
163 if (checksumEs == ErrorStatus::NotEnoughData) {
164 BaseImpl::updateMissingSize(field, size, extraValues...);
165 }
166
167 if (checksumEs != ErrorStatus::Success) {
168 return checksumEs;
169 }
170
171 using VerifyTag =
172 typename comms::util::LazyShallowConditional<
173 ParsedOptionsInternal::HasVerifyBeforeRead
174 >::template Type<
175 VerifyBeforeReadTag,
176 VerifyAfterReadTag
177 >;
178
179 auto fieldLen = static_cast<std::size_t>(std::distance(beforeFieldReadIter, iter));
180 return
181 readInternal(
182 field,
183 msg,
184 iter,
185 size - fieldLen,
186 std::forward<TNextLayerReader>(nextLayerReader),
187 VerifyTag(),
188 extraValues...);
189 }
190
218 template <typename TMsg, typename TIter, typename TNextLayerWriter>
220 Field& field,
221 const TMsg& msg,
222 TIter& iter,
223 std::size_t size,
224 TNextLayerWriter&& nextLayerWriter) const
225 {
226 using IterType = typename std::decay<decltype(iter)>::type;
227 using Tag = typename std::iterator_traits<IterType>::iterator_category;
228
229 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
230 }
231
241 template <typename TIter, typename TNextLayerUpdater>
243 Field& field,
244 TIter& iter,
245 std::size_t size,
246 TNextLayerUpdater&& nextLayerUpdater) const
247 {
248 auto checksumIter = iter;
249 std::advance(iter, Field::maxLength());
250
251 auto fromIter = iter;
252 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
253 if (es != comms::ErrorStatus::Success) {
254 return es;
255 }
256
257 using MsgPtr = typename BaseImpl::MsgPtr;
258 static_assert(
259 !std::is_void<MsgPtr>::value,
260 "Please use update() overload that accepts message object as its first parameter");
261
262 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
263 return fieldUpdateInternal(msgPtr, checksumIter, fromIter, iter, size, field);
264 }
265
277 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
279 const TMsg& msg,
280 Field& field,
281 TIter& iter,
282 std::size_t size,
283 TNextLayerUpdater&& nextLayerUpdater) const
284 {
285 auto& thisObj = BaseImpl::thisLayer();
286 auto checksumIter = iter;
287 auto fieldLen = thisObj.doFieldLength(msg);
288 std::advance(iter, fieldLen);
289
290 auto fromIter = iter;
291 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
292 if (es != comms::ErrorStatus::Success) {
293 return es;
294 }
295
296 return fieldUpdateInternal(&msg, checksumIter, fromIter, iter, size, field);
297 }
298
299protected:
309 template <typename TMsg, typename TIter>
310 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
311 {
312 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
313 }
314
324 template <typename TMsg, typename TIter>
325 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
326 {
327 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
328 }
329
341 template <typename TMsg, typename TIter>
342 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
343 {
344 static_cast<void>(msg);
345 checksumValid = true;
346 return TCalc()(iter, len);
347 }
348
356 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
357 {
358 return field.getValue();
359 }
360
372 template <typename TChecksum, typename TMsg>
373 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
374 {
375 static_cast<void>(msg);
376 field.setValue(checksum);
377 }
378
379private:
380
381 template <typename... TParams>
382 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
383
384 template <typename... TParams>
385 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
386
387 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
388 ErrorStatus verifyRead(
389 Field& field,
390 TMsg& msg,
391 TIter& iter,
392 std::size_t size,
393 TReader&& nextLayerReader,
394 TExtraValues... extraValues)
395 {
396 auto fromIter = iter;
397 auto& thisObj = BaseImpl::thisLayer();
398 auto* msgPtr = BaseImpl::toMsgPtr(msg);
399
400 bool checksumValid = false;
401 auto checksum =
402 thisObj.calculateChecksum(
403 msgPtr,
404 fromIter,
405 size,
406 checksumValid);
407
408 if (!checksumValid) {
410 }
411
412 auto expectedValue = thisObj.getChecksumFromField(field);
413 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
414 BaseImpl::resetMsg(msg);
415 return ErrorStatus::ProtocolError;
416 }
417
418 return nextLayerReader.read(msg, iter, size, extraValues...);
419 }
420
421 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
422 ErrorStatus readVerify(
423 Field& field,
424 TMsg& msg,
425 TIter& iter,
426 std::size_t size,
427 TReader&& nextLayerReader,
428 TExtraValues... extraValues)
429 {
430 auto fromIter = iter;
431
432 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
433 if ((es == ErrorStatus::NotEnoughData) ||
434 (es == ErrorStatus::ProtocolError)) {
435 return es;
436 }
437
438 auto* msgPtr = BaseImpl::toMsgPtr(msg);
439 auto& thisObj = BaseImpl::thisLayer();
440 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
441 bool checksumValid = false;
442 auto checksum =
443 thisObj.calculateChecksum(
444 msgPtr,
445 fromIter,
446 len,
447 checksumValid);
448
449 if (!checksumValid) {
451 }
452
453 auto expectedValue = thisObj.getChecksumFromField(field);
454 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
455 BaseImpl::resetMsg(msg);
456 return ErrorStatus::ProtocolError;
457 }
458
459 return es;
460 }
461
462 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
463 ErrorStatus readInternal(
464 Field& field,
465 TMsg& msg,
466 TIter& iter,
467 std::size_t size,
468 TReader&& nextLayerReader,
469 VerifyBeforeReadTag<>,
470 TExtraValues... extraValues)
471 {
472 return
473 verifyRead(
474 field,
475 msg,
476 iter,
477 size,
478 std::forward<TReader>(nextLayerReader),
479 extraValues...);
480 }
481
482 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
483 ErrorStatus readInternal(
484 Field& field,
485 TMsg& msg,
486 TIter& iter,
487 std::size_t size,
488 TReader&& nextLayerReader,
489 VerifyAfterReadTag<>,
490 TExtraValues... extraValues)
491 {
492 return
493 readVerify(
494 field,
495 msg,
496 iter,
497 size,
498 std::forward<TReader>(nextLayerReader),
499 extraValues...);
500 }
501
502 template <typename TMsg, typename TIter, typename TWriter>
503 ErrorStatus writeInternalRandomAccess(
504 Field& field,
505 const TMsg& msg,
506 TIter& iter,
507 std::size_t size,
508 TWriter&& nextLayerWriter) const
509 {
510 auto& thisObj = BaseImpl::thisLayer();
511 auto checksumIter = iter;
512 thisObj.prepareFieldForWrite(0U, &msg, field);
513 auto es = thisObj.writeField(&msg, field, iter, size);
514 if (es != comms::ErrorStatus::Success) {
515 return es;
516 }
517
518 auto checksumLen =
519 static_cast<std::size_t>(std::distance(checksumIter, iter));
520
521 auto fromIter = iter;
522 es = nextLayerWriter.write(msg, iter, size - checksumLen);
523 if (es != comms::ErrorStatus::Success) {
524 return es;
525 }
526
527 COMMS_ASSERT(fromIter <= iter);
528 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
529
530 bool checksumValid = false;
531 auto checksum =
532 thisObj.calculateChecksum(
533 &msg,
534 fromIter,
535 len,
536 checksumValid);
537
538 if (!checksumValid) {
540 }
541
542 thisObj.prepareFieldForWrite(checksum, &msg, field);
543 auto checksumEs = thisObj.writeField(&msg, field, checksumIter, checksumLen);
544 static_cast<void>(checksumEs);
546 return es;
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 thisObj.prepareFieldForWrite(0U, &msg, field);
559 auto es = thisObj.writeField(&msg, field, iter, size);
560 if (es != comms::ErrorStatus::Success) {
561 return es;
562 }
563
564 auto fieldLen = thisObj.doFieldLength(msg);
565 es = nextLayerWriter.write(msg, iter, size - fieldLen);
566 if (es != comms::ErrorStatus::Success) {
567 return es;
568 }
569
571 }
572
573 template <typename TMsg, typename TIter, typename TWriter>
574 ErrorStatus writeInternal(
575 Field& field,
576 const TMsg& msg,
577 TIter& iter,
578 std::size_t size,
579 TWriter&& nextLayerWriter,
580 std::random_access_iterator_tag) const
581 {
582 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
583 }
584
585 template <typename TMsg, typename TIter, typename TWriter>
586 ErrorStatus writeInternal(
587 Field& field,
588 const TMsg& msg,
589 TIter& iter,
590 std::size_t size,
591 TWriter&& nextLayerWriter,
592 std::output_iterator_tag) const
593 {
594 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
595 }
596
597 template <typename TMsg, typename TIter>
598 ErrorStatus fieldUpdateInternal(
599 const TMsg* msgPtr,
600 TIter checksumIter,
601 TIter from,
602 TIter to,
603 std::size_t size,
604 Field& field) const
605 {
606 static_cast<void>(size);
607 COMMS_ASSERT(from <= to);
608 auto& thisObj = BaseImpl::thisLayer();
609 auto len = static_cast<std::size_t>(std::distance(from, to));
610 auto fieldLen = Field::maxLength();
611 if (msgPtr != nullptr) {
612 fieldLen = thisObj.doFieldLength(*msgPtr);
613 }
614 COMMS_ASSERT(len == (size - fieldLen));
615
616 bool checksumValid = false;
617 auto checksum =
618 thisObj.calculateChecksum(
619 msgPtr,
620 from,
621 len,
622 checksumValid);
623
624 if (!checksumValid) {
626 }
627
628 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
629 return thisObj.doWriteField(msgPtr, field, checksumIter, fieldLen);
630 }
631};
632
633
634namespace details
635{
636template <typename T>
637struct ChecksumPrefixLayerCheckHelper
638{
639 static const bool Value = false;
640};
641
642template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
643struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
644{
645 static const bool Value = true;
646};
647
648} // namespace details
649
653template <typename T>
654constexpr bool isChecksumPrefixLayer()
655{
656 return details::ChecksumPrefixLayerCheckHelper<T>::Value;
657}
658
659} // namespace frame
660
661} // namespace comms
662
663COMMS_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:654
Protocol layer that is responsible to calculate checksum on the data written by all the wrapped inter...
Definition ChecksumPrefixLayer.h:65
~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:147
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumPrefixLayer.h:373
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:219
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumPrefixLayer.h:75
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:310
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:242
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::ChecksumLayerVerifyBeforeRead options has been us...
Definition ChecksumPrefixLayer.h:111
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:278
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumPrefixLayer.h:342
ChecksumPrefixLayer(ChecksumPrefixLayer &&)=default
Move constructor.
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumPrefixLayer.h:356
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumPrefixLayer.h:72
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition ChecksumPrefixLayer.h:80
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:325
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:95
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.