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
80
83
86
88 ~ChecksumPrefixLayer() noexcept = default;
89
91 ChecksumPrefixLayer& operator=(const ChecksumPrefixLayer&) = default;
92
95
98 static constexpr bool hasExtendingClass()
99 {
100 return ParsedOptionsInternal::HasExtendingClass;
101 }
102
105 static constexpr bool hasVerifyBeforeRead()
106 {
107 return ParsedOptionsInternal::HasVerifyBeforeRead;
108 }
109
140 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
142 Field& field,
143 TMsg& msg,
144 TIter& iter,
145 std::size_t size,
146 TNextLayerReader&& nextLayerReader,
147 TExtraValues... extraValues)
148 {
149 using IterType = typename std::decay<decltype(iter)>::type;
150 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
151 "The read operation is expected to use random access iterator");
152
153 auto* msgPtr = BaseImpl::toMsgPtr(msg);
154 auto& thisObj = BaseImpl::thisLayer();
155 auto beforeFieldReadIter = iter;
156 auto checksumEs = thisObj.readField(msgPtr, field, iter, size);
157 if (checksumEs == ErrorStatus::NotEnoughData) {
158 BaseImpl::updateMissingSize(field, size, extraValues...);
159 }
160
161 if (checksumEs != ErrorStatus::Success) {
162 return checksumEs;
163 }
164
165 using VerifyTag =
166 typename comms::util::LazyShallowConditional<
167 ParsedOptionsInternal::HasVerifyBeforeRead
168 >::template Type<
169 VerifyBeforeReadTag,
170 VerifyAfterReadTag
171 >;
172
173 auto fieldLen = static_cast<std::size_t>(std::distance(beforeFieldReadIter, iter));
174 return
175 readInternal(
176 field,
177 msg,
178 iter,
179 size - fieldLen,
180 std::forward<TNextLayerReader>(nextLayerReader),
181 VerifyTag(),
182 extraValues...);
183 }
184
212 template <typename TMsg, typename TIter, typename TNextLayerWriter>
214 Field& field,
215 const TMsg& msg,
216 TIter& iter,
217 std::size_t size,
218 TNextLayerWriter&& nextLayerWriter) const
219 {
220 using IterType = typename std::decay<decltype(iter)>::type;
221 using Tag = typename std::iterator_traits<IterType>::iterator_category;
222
223 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
224 }
225
235 template <typename TIter, typename TNextLayerUpdater>
237 Field& field,
238 TIter& iter,
239 std::size_t size,
240 TNextLayerUpdater&& nextLayerUpdater) const
241 {
242 auto checksumIter = iter;
243 std::advance(iter, Field::maxLength());
244
245 auto fromIter = iter;
246 auto es = nextLayerUpdater.update(iter, size - Field::maxLength());
247 if (es != comms::ErrorStatus::Success) {
248 return es;
249 }
250
251 using MsgPtr = typename BaseImpl::MsgPtr;
252 static_assert(
253 !std::is_void<MsgPtr>::value,
254 "Please use update() overload that accepts message object as its first parameter");
255
256 auto* msgPtr = static_cast<typename MsgPtr::element_type*>(nullptr);
257 return fieldUpdateInternal(msgPtr, checksumIter, fromIter, iter, size, field);
258 }
259
271 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
273 const TMsg& msg,
274 Field& field,
275 TIter& iter,
276 std::size_t size,
277 TNextLayerUpdater&& nextLayerUpdater) const
278 {
279 auto& thisObj = BaseImpl::thisLayer();
280 auto checksumIter = iter;
281 auto fieldLen = thisObj.doFieldLength(msg);
282 std::advance(iter, fieldLen);
283
284 auto fromIter = iter;
285 auto es = nextLayerUpdater.update(msg, iter, size - fieldLen);
286 if (es != comms::ErrorStatus::Success) {
287 return es;
288 }
289
290 return fieldUpdateInternal(&msg, checksumIter, fromIter, iter, size, field);
291 }
292
293protected:
303 template <typename TMsg, typename TIter>
304 comms::ErrorStatus readField(const TMsg* msgPtr, Field& field, TIter& iter, std::size_t len)
305 {
306 return BaseImpl::thisLayer().doReadField(msgPtr, field, iter, len);
307 }
308
318 template <typename TMsg, typename TIter>
319 comms::ErrorStatus writeField(const TMsg* msgPtr, const Field& field, TIter& iter, std::size_t len) const
320 {
321 return BaseImpl::thisLayer().doWriteField(msgPtr, field, iter, len);
322 }
323
335 template <typename TMsg, typename TIter>
336 static auto calculateChecksum(const TMsg* msg, TIter& iter, std::size_t len, bool& checksumValid) -> decltype(TCalc()(iter, len))
337 {
338 static_cast<void>(msg);
339 checksumValid = true;
340 return TCalc()(iter, len);
341 }
342
350 static auto getChecksumFromField(const Field& field) -> decltype(field.getValue())
351 {
352 return field.getValue();
353 }
354
366 template <typename TChecksum, typename TMsg>
367 static void prepareFieldForWrite(TChecksum checksum, const TMsg* msg, Field& field)
368 {
369 static_cast<void>(msg);
370 field.setValue(checksum);
371 }
372
373private:
374
375 template <typename... TParams>
376 using VerifyBeforeReadTag = comms::details::tag::Tag1<>;
377
378 template <typename... TParams>
379 using VerifyAfterReadTag = comms::details::tag::Tag2<>;
380
381 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
382 ErrorStatus verifyRead(
383 Field& field,
384 TMsg& msg,
385 TIter& iter,
386 std::size_t size,
387 TReader&& nextLayerReader,
388 TExtraValues... extraValues)
389 {
390 auto fromIter = iter;
391 auto& thisObj = BaseImpl::thisLayer();
392 auto* msgPtr = BaseImpl::toMsgPtr(msg);
393
394 bool checksumValid = false;
395 auto checksum =
396 thisObj.calculateChecksum(
397 msgPtr,
398 fromIter,
399 size,
400 checksumValid);
401
402 if (!checksumValid) {
404 }
405
406 auto expectedValue = thisObj.getChecksumFromField(field);
407 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
408 BaseImpl::resetMsg(msg);
409 return ErrorStatus::ProtocolError;
410 }
411
412 return nextLayerReader.read(msg, iter, size, extraValues...);
413 }
414
415 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
416 ErrorStatus readVerify(
417 Field& field,
418 TMsg& msg,
419 TIter& iter,
420 std::size_t size,
421 TReader&& nextLayerReader,
422 TExtraValues... extraValues)
423 {
424 auto fromIter = iter;
425
426 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
427 if ((es == ErrorStatus::NotEnoughData) ||
428 (es == ErrorStatus::ProtocolError)) {
429 return es;
430 }
431
432 auto* msgPtr = BaseImpl::toMsgPtr(msg);
433 auto& thisObj = BaseImpl::thisLayer();
434 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
435 bool checksumValid = false;
436 auto checksum =
437 thisObj.calculateChecksum(
438 msgPtr,
439 fromIter,
440 len,
441 checksumValid);
442
443 if (!checksumValid) {
445 }
446
447 auto expectedValue = thisObj.getChecksumFromField(field);
448 if (expectedValue != static_cast<decltype(expectedValue)>(checksum)) {
449 BaseImpl::resetMsg(msg);
450 return ErrorStatus::ProtocolError;
451 }
452
453 return es;
454 }
455
456 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
457 ErrorStatus readInternal(
458 Field& field,
459 TMsg& msg,
460 TIter& iter,
461 std::size_t size,
462 TReader&& nextLayerReader,
463 VerifyBeforeReadTag<>,
464 TExtraValues... extraValues)
465 {
466 return
467 verifyRead(
468 field,
469 msg,
470 iter,
471 size,
472 std::forward<TReader>(nextLayerReader),
473 extraValues...);
474 }
475
476 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
477 ErrorStatus readInternal(
478 Field& field,
479 TMsg& msg,
480 TIter& iter,
481 std::size_t size,
482 TReader&& nextLayerReader,
483 VerifyAfterReadTag<>,
484 TExtraValues... extraValues)
485 {
486 return
487 readVerify(
488 field,
489 msg,
490 iter,
491 size,
492 std::forward<TReader>(nextLayerReader),
493 extraValues...);
494 }
495
496 template <typename TMsg, typename TIter, typename TWriter>
497 ErrorStatus writeInternalRandomAccess(
498 Field& field,
499 const TMsg& msg,
500 TIter& iter,
501 std::size_t size,
502 TWriter&& nextLayerWriter) const
503 {
504 auto& thisObj = BaseImpl::thisLayer();
505 auto checksumIter = iter;
506 thisObj.prepareFieldForWrite(0U, &msg, field);
507 auto es = thisObj.writeField(&msg, field, iter, size);
508 if (es != comms::ErrorStatus::Success) {
509 return es;
510 }
511
512 auto checksumLen =
513 static_cast<std::size_t>(std::distance(checksumIter, iter));
514
515 auto fromIter = iter;
516 es = nextLayerWriter.write(msg, iter, size - checksumLen);
517 if (es != comms::ErrorStatus::Success) {
518 return es;
519 }
520
521 COMMS_ASSERT(fromIter <= iter);
522 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
523
524 bool checksumValid = false;
525 auto checksum =
526 thisObj.calculateChecksum(
527 &msg,
528 fromIter,
529 len,
530 checksumValid);
531
532 if (!checksumValid) {
534 }
535
536 thisObj.prepareFieldForWrite(checksum, &msg, field);
537 auto checksumEs = thisObj.writeField(&msg, field, checksumIter, checksumLen);
538 static_cast<void>(checksumEs);
540 return es;
541 }
542
543 template <typename TMsg, typename TIter, typename TWriter>
544 ErrorStatus writeInternalOutput(
545 Field& field,
546 const TMsg& msg,
547 TIter& iter,
548 std::size_t size,
549 TWriter&& nextLayerWriter) const
550 {
551 auto& thisObj = BaseImpl::thisLayer();
552 thisObj.prepareFieldForWrite(0U, &msg, field);
553 auto es = thisObj.writeField(&msg, field, iter, size);
554 if (es != comms::ErrorStatus::Success) {
555 return es;
556 }
557
558 auto fieldLen = thisObj.doFieldLength(msg);
559 es = nextLayerWriter.write(msg, iter, size - fieldLen);
560 if (es != comms::ErrorStatus::Success) {
561 return es;
562 }
563
565 }
566
567 template <typename TMsg, typename TIter, typename TWriter>
568 ErrorStatus writeInternal(
569 Field& field,
570 const TMsg& msg,
571 TIter& iter,
572 std::size_t size,
573 TWriter&& nextLayerWriter,
574 std::random_access_iterator_tag) const
575 {
576 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
577 }
578
579 template <typename TMsg, typename TIter, typename TWriter>
580 ErrorStatus writeInternal(
581 Field& field,
582 const TMsg& msg,
583 TIter& iter,
584 std::size_t size,
585 TWriter&& nextLayerWriter,
586 std::output_iterator_tag) const
587 {
588 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
589 }
590
591 template <typename TMsg, typename TIter>
592 ErrorStatus fieldUpdateInternal(
593 const TMsg* msgPtr,
594 TIter checksumIter,
595 TIter from,
596 TIter to,
597 std::size_t size,
598 Field& field) const
599 {
600 static_cast<void>(size);
601 COMMS_ASSERT(from <= to);
602 auto& thisObj = BaseImpl::thisLayer();
603 auto len = static_cast<std::size_t>(std::distance(from, to));
604 auto fieldLen = Field::maxLength();
605 if (msgPtr != nullptr) {
606 fieldLen = thisObj.doFieldLength(*msgPtr);
607 }
608 COMMS_ASSERT(len == (size - fieldLen));
609
610 bool checksumValid = false;
611 auto checksum =
612 thisObj.calculateChecksum(
613 msgPtr,
614 from,
615 len,
616 checksumValid);
617
618 if (!checksumValid) {
620 }
621
622 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
623 return thisObj.doWriteField(msgPtr, field, checksumIter, fieldLen);
624 }
625};
626
627namespace details
628{
629template <typename T>
630struct ChecksumPrefixLayerCheckHelper
631{
632 static const bool Value = false;
633};
634
635template <typename TField, typename TCalc, typename TNextLayer, typename... TOptions>
636struct ChecksumPrefixLayerCheckHelper<ChecksumPrefixLayer<TField, TCalc, TNextLayer, TOptions...> >
637{
638 static const bool Value = true;
639};
640
641} // namespace details
642
646template <typename T>
647constexpr bool isChecksumPrefixLayer()
648{
649 return details::ChecksumPrefixLayerCheckHelper<T>::Value;
650}
651
652} // namespace frame
653
654} // namespace comms
655
656COMMS_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.
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:647
Frame layer that is responsible to calculate checksum on the data written by all the wrapped internal...
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:141
static void prepareFieldForWrite(TChecksum checksum, const TMsg *msg, Field &field)
Prepare field for writing.
Definition ChecksumPrefixLayer.h:367
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:213
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:304
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition ChecksumPrefixLayer.h:236
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::FrameLayerVerifyBeforeRead options has been used.
Definition ChecksumPrefixLayer.h:105
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:272
static auto calculateChecksum(const TMsg *msg, TIter &iter, std::size_t len, bool &checksumValid) -> decltype(TCalc()(iter, len))
Calculate checksum.
Definition ChecksumPrefixLayer.h:336
ChecksumPrefixLayer(ChecksumPrefixLayer &&)=default
Move constructor.
static auto getChecksumFromField(const Field &field) -> decltype(field.getValue())
Retrieve checksum value from the field.
Definition ChecksumPrefixLayer.h:350
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition ChecksumPrefixLayer.h:73
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:319
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.