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
77 ChecksumLayer() = default;
78
80 ChecksumLayer(const ChecksumLayer&) = default;
81
84
86 ~ChecksumLayer() noexcept = default;
87
89 ChecksumLayer& operator=(const ChecksumLayer&) = default;
90
92 ChecksumLayer& operator=(ChecksumLayer&&) = default;
93
96 static constexpr bool hasExtendingClass()
97 {
98 return ParsedOptionsInternal::HasExtendingClass;
99 }
100
103 static constexpr bool hasVerifyBeforeRead()
104 {
105 return ParsedOptionsInternal::HasVerifyBeforeRead;
106 }
107
138 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
140 Field& field,
141 TMsg& msg,
142 TIter& iter,
143 std::size_t size,
144 TNextLayerReader&& nextLayerReader,
145 TExtraValues... extraValues)
146 {
147 using IterType = typename std::decay<decltype(iter)>::type;
148 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
149 "The read operation is expected to use random access iterator");
150
151 using VerifyTag =
152 typename comms::util::LazyShallowConditional<
153 ParsedOptionsInternal::HasVerifyBeforeRead
154 >::template Type<
155 VerifyBeforeReadTag,
156 VerifyAfterReadTag
157 >;
158
159 return
160 readInternal(
161 field,
162 msg,
163 iter,
164 size,
165 std::forward<TNextLayerReader>(nextLayerReader),
166 VerifyTag(),
167 extraValues...);
168 }
169
195 template <typename TMsg, typename TIter, typename TNextLayerWriter>
197 Field& field,
198 const TMsg& msg,
199 TIter& iter,
200 std::size_t size,
201 TNextLayerWriter&& nextLayerWriter) const
202 {
203 using IterType = typename std::decay<decltype(iter)>::type;
204 using Tag = typename std::iterator_traits<IterType>::iterator_category;
205
206 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
207 }
208
218 template <typename TIter, typename TNextLayerUpdater>
220 Field& field,
221 TIter& iter,
222 std::size_t size,
223 TNextLayerUpdater&& nextLayerUpdater) const
224 {
225 auto fromIter = iter;
226 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
227 if (es != comms::ErrorStatus::Success) {
228 return es;
229 }
230
231 using MsgPtr = typename BaseImpl::MsgPtr;
232 static_assert(
233 !std::is_void<MsgPtr>::value,
234 "Please use update() overload that accepts message object as its first parameter");
235
236 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
237 return fieldUpdateInternal(msgPtr, fromIter, iter, size, field);
238 }
239
249 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
251 const TMsg& msg,
252 Field& field,
253 TIter& iter,
254 std::size_t size,
255 TNextLayerUpdater&& nextLayerUpdater) const
256 {
257 auto fromIter = iter;
258 auto& thisObj = BaseImpl::thisLayer();
259 auto fieldLen = thisObj.doFieldLength(msg);
260 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
261 if (es != comms::ErrorStatus::Success) {
262 return es;
263 }
264
265 return fieldUpdateInternal(&msg, fromIter, iter, size, field);
266 }
267
268protected:
278 template <typename TMsg, typename TIter>
279 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
280 {
281 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
282 }
283
293 template <typename TMsg, typename TIter>
294 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
295 {
296 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
297 }
298
310 template <typename TMsg, typename TIter>
311 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
312 {
313 static_cast<void>(msg);
314 checksumValid = true;
315 return TCalc()(iter, len);
316 }
317
325 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
326 {
327 return field.getValue();
328 }
329
341 template <typename TChecksum, typename TMsg>
342 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
343 {
344 static_cast<void>(msg);
345 field.setValue(checksum);
346 }
347
348private:
349 template <typename... TParams>
350 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
351
352 template <typename... TParams>
353 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
354
355 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
356 ErrorStatus verifyRead(
357 Field& field,
358 TMsg& msg,
359 TIter& iter,
360 std::size_t size,
361 TReader&& nextLayerReader,
362 TExtraValues... extraValues)
363 {
364 auto fromIter = iter;
365 auto* msgPtr = BaseImpl::toMsgPtr(msg);
366 auto fieldLen = Field::minLength();
367 auto& thisObj = BaseImpl::thisLayer();
368 if (msgPtr != nullptr) {
369 fieldLen = thisObj.doFieldLength(*msgPtr);
370 }
371 auto toIter = fromIter + (size - fieldLen);
372 auto len = static_cast<std::size_t>(std::distance(fromIter, toIter));
373
374 auto checksumEs = thisObj.readField(msgPtr, field, toIter, fieldLen);
375 if (checksumEs != ErrorStatus::Success) {
376 return checksumEs;
377 }
378
379 bool checksumValid = false;
380 auto checksum =
381 thisObj.calculateChecksum(
382 msgPtr,
383 fromIter,
384 len,
385 checksumValid);
386
387 if (!checksumValid) {
389 }
390
391 auto expectedValue = thisObj.getChecksumFromField(field);
392
393 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
394 BaseImpl::resetMsg(msg);
395 return ErrorStatus::ProtocolError;
396 }
397
398 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
399 if (es == ErrorStatus::Success) {
400 iter = toIter;
401 }
402
403 return es;
404 }
405
406 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
407 ErrorStatus readVerify(
408 Field& field,
409 TMsg& msg,
410 TIter& iter,
411 std::size_t size,
412 TReader&& nextLayerReader,
413 TExtraValues... extraValues)
414 {
415 auto fromIter = iter;
416
417 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
418 if ((es == ErrorStatus::NotEnoughData) ||
419 (es == ErrorStatus::ProtocolError)) {
420 return es;
421 }
422
423 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
424 COMMS_ASSERT(len <= size);
425 auto remSize = size - len;
426 auto* msgPtr = BaseImpl::toMsgPtr(msg);
427 auto& thisObj = BaseImpl::thisLayer();
428 auto checksumEs = thisObj.readField(msgPtr, field, iter, remSize);
429 if (checksumEs == ErrorStatus::NotEnoughData) {
430 BaseImpl::updateMissingSize(field, remSize, extraValues...);
431 }
432
433 if (checksumEs != ErrorStatus::Success) {
434 BaseImpl::resetMsg(msg);
435 return checksumEs;
436 }
437
438 bool checksumValid = false;
439 auto checksum =
440 thisObj.calculateChecksum(
441 BaseImpl::toMsgPtr(msg),
442 fromIter,
443 len,
444 checksumValid);
445
446 if (!checksumValid) {
448 }
449
450 auto expectedValue = thisObj.getChecksumFromField(field);
451
452 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
453 BaseImpl::resetMsg(msg);
454 return ErrorStatus::ProtocolError;
455 }
456
457 return es;
458 }
459
460 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
461 ErrorStatus readInternal(
462 Field& field,
463 TMsg& msg,
464 TIter& iter,
465 std::size_t size,
466 TReader&& nextLayerReader,
467 VerifyBeforeReadTag<>,
468 TExtraValues... extraValues)
469 {
470 return
471 verifyRead(
472 field,
473 msg,
474 iter,
475 size,
476 std::forward<TReader>(nextLayerReader),
477 extraValues...);
478 }
479
480 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
481 ErrorStatus readInternal(
482 Field& field,
483 TMsg& msg,
484 TIter& iter,
485 std::size_t size,
486 TReader&& nextLayerReader,
487 VerifyAfterReadTag<>,
488 TExtraValues... extraValues)
489 {
490 return
491 readVerify(
492 field,
493 msg,
494 iter,
495 size,
496 std::forward<TReader>(nextLayerReader),
497 extraValues...);
498 }
499
500 template <typename TMsg, typename TIter, typename TWriter>
501 ErrorStatus writeInternalRandomAccess(
502 Field& field,
503 const TMsg& msg,
504 TIter& iter,
505 std::size_t size,
506 TWriter&& nextLayerWriter) const
507 {
508 auto fromIter = iter;
509 auto es = nextLayerWriter.write(msg, iter, size);
510 if ((es != comms::ErrorStatus::Success) &&
512 return es;
513 }
514
515 COMMS_ASSERT(fromIter <= iter);
516 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
517 auto remSize = size - len;
518 auto& thisObj = BaseImpl::thisLayer();
519
521 thisObj.prepareFieldForWrite(0, &msg, field);
522 auto esTmp = thisObj.writeField(&msg, field, iter, remSize);
523 if (esTmp != comms::ErrorStatus::Success) {
524 return esTmp;
525 }
526
527 return es;
528 }
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 return thisObj.writeField(&msg, field, iter, remSize);
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 auto fieldLen = thisObj.doFieldLength(msg);
556 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
557 if ((es != comms::ErrorStatus::Success) &&
559 return es;
560 }
561
562 auto esTmp = thisObj.writeField(&msg, field, iter, fieldLen);
563 static_cast<void>(esTmp);
566 }
567
568 template <typename TMsg, typename TIter, typename TWriter>
569 ErrorStatus writeInternal(
570 Field& field,
571 const TMsg& msg,
572 TIter& iter,
573 std::size_t size,
574 TWriter&& nextLayerWriter,
575 std::random_access_iterator_tag) const
576 {
577 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
578 }
579
580 template <typename TMsg, typename TIter, typename TWriter>
581 ErrorStatus writeInternal(
582 Field& field,
583 const TMsg& msg,
584 TIter& iter,
585 std::size_t size,
586 TWriter&& nextLayerWriter,
587 std::output_iterator_tag) const
588 {
589 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
590 }
591
592 template <typename TMsg, typename TIter>
593 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
594 {
595 COMMS_ASSERT(from <= to);
596 auto len = static_cast<std::size_t>(std::distance(from, to));
597 auto& thisObj = BaseImpl::thisLayer();
598 if (msgPtr != nullptr) {
599 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
600 }
601 else {
602 COMMS_ASSERT(len == (size - Field::maxLength()));
603 }
604 auto remSize = size - len;
605
606 bool checksumValid = false;
607 auto checksum =
608 thisObj.calculateChecksum(
609 msgPtr,
610 from,
611 len,
612 checksumValid);
613
614 if (!checksumValid) {
616 }
617
618 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
619 return thisObj.writeField(msgPtr, field, to, remSize);
620 }
621};
622
623namespace details
624{
625template <typename T>
626struct ChecksumLayerCheckHelper
627{
628 static const bool Value = false;
629};
630
631template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
632struct ChecksumLayerCheckHelper<ChecksumLayer<TField, TCalc, TNextLayer, TOptions...> >
633{
634 static const bool Value = true;
635};
636
637} // namespace details
638
642template <typename T>
643constexpr bool isChecksumLayer()
644{
645 return details::ChecksumLayerCheckHelper<T>::Value;
646}
647
648} // namespace frame
649
650} // namespace comms
651
652COMMS_MSVC_WARNING_POP
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:168
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
Frame layer that is responsible to calculate checksum on the data written by all the wrapped internal...
Definition ChecksumLayer.h:65
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::FrameLayerVerifyBeforeRead options has been used.
Definition ChecksumLayer.h:103
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:311
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumLayer.h:342
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:250
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumLayer.h:325
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:139
comms::ErrorStatus writeField(const TMsg *msgPtr, const Field &field, TIter &iter, std::size_t len) const
Write the checksum field.
Definition ChecksumLayer.h:294
comms::ErrorStatus readField(const TMsg *msgPtr, Field &field, TIter &iter, std::size_t len)
Read the checksum field.
Definition ChecksumLayer.h:279
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:196
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:219
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:643
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.