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 - 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
17#include "comms/frame/details/ChecksumLayerOptionsParser.h"
18#include "comms/frame/details/ChecksumPrefixLayerBase.h"
20
21#include <cstddef>
22#include <iterator>
23#include <type_traits>
24#include <utility>
25
26COMMS_MSVC_WARNING_PUSH
27COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
28
29namespace comms
30{
31
32namespace frame
33{
67template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
68class ChecksumPrefixLayer : public comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>
69{
70 using BaseImpl = comms::frame::details::ChecksumPrefixLayerBase<TField, TCalc, TNextLayer, TOptions...>;
71 using ParsedOptionsInternal = details::ChecksumLayerOptionsParser<TOptions...>;
72
73public:
74
76 using Field = typename BaseImpl::Field;
77
79 using ChecksumCalc = TCalc;
80
83
86
89
91 ~ChecksumPrefixLayer() noexcept = default;
92
94 ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
95
98
101 static constexpr bool hasExtendingClass()
102 {
103 return ParsedOptionsInternal::HasExtendingClass;
104 }
105
108 static constexpr bool hasVerifyBeforeRead()
109 {
110 return ParsedOptionsInternal::HasVerifyBeforeRead;
111 }
112
143 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
145 Field& field,
146 TMsg& msg,
147 TIter& iter,
148 std::size_t size,
149 TNextLayerReader&& nextLayerReader,
150 TExtraValues... extraValues)
151 {
152 using IterType = typename std::decay<decltype(iter)>::type;
153 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
154 "The read operation is expected to use random access iterator");
155
156 auto* msgPtr = BaseImpl::toMsgPtr(msg);
157 auto& thisObj = BaseImpl::thisLayer();
158 auto beforeFieldReadIter = iter;
159 auto checksumEs = thisObj.readField(msgPtr, field, iter, size);
160 if (checksumEs == ErrorStatus::NotEnoughData) {
161 BaseImpl::updateMissingSize(field, size, extraValues...);
162 }
163
164 if (checksumEs != ErrorStatus::Success) {
165 return checksumEs;
166 }
167
168 using VerifyTag =
169 typename comms::util::LazyShallowConditional<
170 ParsedOptionsInternal::HasVerifyBeforeRead
171 >::template Type<
172 VerifyBeforeReadTag,
173 VerifyAfterReadTag
174 >;
175
176 auto fieldLen = static_cast<std::size_t>(std::distance(beforeFieldReadIter, iter));
177 return
178 readInternal(
179 field,
180 msg,
181 iter,
182 size - fieldLen,
183 std::forward<TNextLayerReader>(nextLayerReader),
184 VerifyTag(),
185 extraValues...);
186 }
187
215 template <typename TMsg, typename TIter, typename TNextLayerWriter>
217 Field& field,
218 const TMsg& msg,
219 TIter& iter,
220 std::size_t size,
221 TNextLayerWriter&& nextLayerWriter) const
222 {
223 using IterType = typename std::decay<decltype(iter)>::type;
224 using Tag = typename std::iterator_traits<IterType>::iterator_category;
225
226 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
227 }
228
238 template <typename TIter, typename TNextLayerUpdater>
240 Field& field,
241 TIter& iter,
242 std::size_t size,
243 TNextLayerUpdater&& nextLayerUpdater) const
244 {
245 auto checksumIter = iter;
246 std::advance(iter, Field::maxLength());
247
248 auto fromIter = iter;
249 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
250 if (es != comms::ErrorStatus::Success) {
251 return es;
252 }
253
254 using MsgPtr = typename BaseImpl::MsgPtr;
255 static_assert(
256 !std::is_void<MsgPtr>::value,
257 "Please use update() overload that accepts message object as its first parameter");
258
259 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
260 return fieldUpdateInternal(msgPtr, checksumIter, fromIter, iter, size, field);
261 }
262
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& thisObj = BaseImpl::thisLayer();
283 auto checksumIter = iter;
284 auto fieldLen = thisObj.doFieldLength(msg);
285 std::advance(iter, fieldLen);
286
287 auto fromIter = iter;
288 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
289 if (es != comms::ErrorStatus::Success) {
290 return es;
291 }
292
293 return fieldUpdateInternal(&msg, checksumIter, fromIter, iter, size, field);
294 }
295
296protected:
306 template <typename TMsg, typename TIter>
307 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
308 {
309 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
310 }
311
321 template <typename TMsg, typename TIter>
322 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
323 {
324 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
325 }
326
338 template <typename TMsg, typename TIter>
339 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
340 {
341 static_cast<void>(msg);
342 checksumValid = true;
343 return TCalc()(iter, len);
344 }
345
353 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
354 {
355 return field.getValue();
356 }
357
369 template <typename TChecksum, typename TMsg>
370 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
371 {
372 static_cast<void>(msg);
373 field.setValue(checksum);
374 }
375
376private:
377
378 template <typename... TParams>
379 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
380
381 template <typename... TParams>
382 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
383
384 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
385 ErrorStatus verifyRead(
386 Field& field,
387 TMsg& msg,
388 TIter& iter,
389 std::size_t size,
390 TReader&& nextLayerReader,
391 TExtraValues... extraValues)
392 {
393 auto fromIter = iter;
394 auto& thisObj = BaseImpl::thisLayer();
395 auto* msgPtr = BaseImpl::toMsgPtr(msg);
396
397 bool checksumValid = false;
398 auto checksum =
399 thisObj.calculateChecksum(
400 msgPtr,
401 fromIter,
402 size,
403 checksumValid);
404
405 if (!checksumValid) {
407 }
408
409 auto expectedValue = thisObj.getChecksumFromField(field);
410 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
411 BaseImpl::resetMsg(msg);
412 return ErrorStatus::ProtocolError;
413 }
414
415 return nextLayerReader.read(msg, iter, size, extraValues...);
416 }
417
418 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
419 ErrorStatus readVerify(
420 Field& field,
421 TMsg& msg,
422 TIter& iter,
423 std::size_t size,
424 TReader&& nextLayerReader,
425 TExtraValues... extraValues)
426 {
427 auto fromIter = iter;
428
429 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
430 if ((es == ErrorStatus::NotEnoughData) ||
431 (es == ErrorStatus::ProtocolError)) {
432 return es;
433 }
434
435 auto* msgPtr = BaseImpl::toMsgPtr(msg);
436 auto& thisObj = BaseImpl::thisLayer();
437 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
438 bool checksumValid = false;
439 auto checksum =
440 thisObj.calculateChecksum(
441 msgPtr,
442 fromIter,
443 len,
444 checksumValid);
445
446 if (!checksumValid) {
448 }
449
450 auto expectedValue = thisObj.getChecksumFromField(field);
451 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
452 BaseImpl::resetMsg(msg);
453 return ErrorStatus::ProtocolError;
454 }
455
456 return es;
457 }
458
459 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
460 ErrorStatus readInternal(
461 Field& field,
462 TMsg& msg,
463 TIter& iter,
464 std::size_t size,
465 TReader&& nextLayerReader,
466 VerifyBeforeReadTag<>,
467 TExtraValues... extraValues)
468 {
469 return
470 verifyRead(
471 field,
472 msg,
473 iter,
474 size,
475 std::forward<TReader>(nextLayerReader),
476 extraValues...);
477 }
478
479 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
480 ErrorStatus readInternal(
481 Field& field,
482 TMsg& msg,
483 TIter& iter,
484 std::size_t size,
485 TReader&& nextLayerReader,
486 VerifyAfterReadTag<>,
487 TExtraValues... extraValues)
488 {
489 return
490 readVerify(
491 field,
492 msg,
493 iter,
494 size,
495 std::forward<TReader>(nextLayerReader),
496 extraValues...);
497 }
498
499 template <typename TMsg, typename TIter, typename TWriter>
500 ErrorStatus writeInternalRandomAccess(
501 Field& field,
502 const TMsg& msg,
503 TIter& iter,
504 std::size_t size,
505 TWriter&& nextLayerWriter) const
506 {
507 auto& thisObj = BaseImpl::thisLayer();
508 auto checksumIter = iter;
509 thisObj.prepareFieldForWrite(0U, &msg, field);
510 auto es = thisObj.writeField(&msg, field, iter, size);
511 if (es != comms::ErrorStatus::Success) {
512 return es;
513 }
514
515 auto checksumLen =
516 static_cast<std::size_t>(std::distance(checksumIter, iter));
517
518 auto fromIter = iter;
519 es = nextLayerWriter.write(msg, iter, size - checksumLen);
520 if (es != comms::ErrorStatus::Success) {
521 return es;
522 }
523
524 COMMS_ASSERT(fromIter <= iter);
525 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
526
527 bool checksumValid = false;
528 auto checksum =
529 thisObj.calculateChecksum(
530 &msg,
531 fromIter,
532 len,
533 checksumValid);
534
535 if (!checksumValid) {
537 }
538
539 thisObj.prepareFieldForWrite(checksum, &msg, field);
540 auto checksumEs = thisObj.writeField(&msg, field, checksumIter, checksumLen);
541 static_cast<void>(checksumEs);
543 return es;
544 }
545
546 template <typename TMsg, typename TIter, typename TWriter>
547 ErrorStatus writeInternalOutput(
548 Field& field,
549 const TMsg& msg,
550 TIter& iter,
551 std::size_t size,
552 TWriter&& nextLayerWriter) const
553 {
554 auto& thisObj = BaseImpl::thisLayer();
555 thisObj.prepareFieldForWrite(0U, &msg, field);
556 auto es = thisObj.writeField(&msg, field, iter, size);
557 if (es != comms::ErrorStatus::Success) {
558 return es;
559 }
560
561 auto fieldLen = thisObj.doFieldLength(msg);
562 es = nextLayerWriter.write(msg, iter, size - fieldLen);
563 if (es != comms::ErrorStatus::Success) {
564 return es;
565 }
566
568 }
569
570 template <typename TMsg, typename TIter, typename TWriter>
571 ErrorStatus writeInternal(
572 Field& field,
573 const TMsg& msg,
574 TIter& iter,
575 std::size_t size,
576 TWriter&& nextLayerWriter,
577 std::random_access_iterator_tag) const
578 {
579 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
580 }
581
582 template <typename TMsg, typename TIter, typename TWriter>
583 ErrorStatus writeInternal(
584 Field& field,
585 const TMsg& msg,
586 TIter& iter,
587 std::size_t size,
588 TWriter&& nextLayerWriter,
589 std::output_iterator_tag) const
590 {
591 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
592 }
593
594 template <typename TMsg, typename TIter>
595 ErrorStatus fieldUpdateInternal(
596 const TMsg* msgPtr,
597 TIter checksumIter,
598 TIter from,
599 TIter to,
600 std::size_t size,
601 Field& field) const
602 {
603 static_cast<void>(size);
604 COMMS_ASSERT(from <= to);
605 auto& thisObj = BaseImpl::thisLayer();
606 auto len = static_cast<std::size_t>(std::distance(from, to));
607 auto fieldLen = Field::maxLength();
608 if (msgPtr != nullptr) {
609 fieldLen = thisObj.doFieldLength(*msgPtr);
610 }
611 COMMS_ASSERT(len == (size - fieldLen));
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.doWriteField(msgPtr, field, checksumIter, fieldLen);
627 }
628};
629
630namespace details
631{
632template <typename T>
633struct ChecksumPrefixLayerCheckHelper
634{
635 static const bool Value = false;
636};
637
638template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
639struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
640{
641 static const bool Value = true;
642};
643
644} // namespace details
645
649template <typename T>
650constexpr bool isChecksumPrefixLayer()
651{
652 return details::ChecksumPrefixLayerCheckHelper<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.
Base class to all the field classes.
Definition Field.h:36
constexpr bool isChecksumPrefixLayer()
Compile time check of whether the provided type is a variant of ChecksumPrefixLayer.
Definition ChecksumPrefixLayer.h:650
Frame layer that is responsible to calculate checksum on the data written by all the wrapped internal...
Definition ChecksumPrefixLayer.h:69
~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:144
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumPrefixLayer.h:370
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:216
TCalc ChecksumCalc
Provided checksum calculation algorithm.
Definition ChecksumPrefixLayer.h:79
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:307
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:239
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::FrameLayerVerifyBeforeRead options has been used.
Definition ChecksumPrefixLayer.h:108
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:275
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumPrefixLayer.h:339
ChecksumPrefixLayer(ChecksumPrefixLayer &&)=default
Move constructor.
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumPrefixLayer.h:353
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumPrefixLayer.h:76
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:322
typename details::FrameLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition FrameLayerBase.h:100
comms::frame::ChecksumPrefixLayer< TField, TCalc, TNextLayer, TOptions... > ChecksumPrefixLayer
Alias to the comms::frame::ChecksumPrefixLayer.
Definition ChecksumPrefixLayer.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.