COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
SyncSuffixLayer.h
Go to the documentation of this file.
1//
2// Copyright 2025 - 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/SyncSuffixLayerBase.h"
18#include "comms/frame/details/SyncSuffixLayerOptionsParser.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{
58template <typename TField, typename TNextLayer, typename... TOptions>
59class SyncSuffixLayer : public comms::frame::details::SyncSuffixLayerBase<TField, TNextLayer, TOptions...>
60{
61 using BaseImpl = comms::frame::details::SyncSuffixLayerBase<TField, TNextLayer, TOptions...>;
62 using ParsedOptionsInternal = details::SyncSuffixLayerOptionsParser<TOptions...>;
63
64public:
66 using Field = typename BaseImpl::Field;
67
70 using EscField = typename ParsedOptionsInternal::EscField;
71
73 SyncSuffixLayer() = default;
74
77
80
82 ~SyncSuffixLayer() noexcept = default;
83
85 SyncSuffixLayer& operator=(const SyncSuffixLayer&) = default;
86
88 SyncSuffixLayer& operator=(SyncSuffixLayer&&) = default;
89
92 static constexpr bool hasExtendingClass()
93 {
94 return ParsedOptionsInternal::HasExtendingClass;
95 }
96
99 static constexpr bool hasVerifyBeforeRead()
100 {
101 return ParsedOptionsInternal::HasVerifyBeforeRead;
102 }
103
106 static constexpr bool hasSeekField()
107 {
108 return ParsedOptionsInternal::HasSeekField;
109 }
110
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 using SeekTag =
154 typename comms::util::LazyShallowConditional<
155 ParsedOptionsInternal::HasSeekField
156 >::template Type<
157 SeekFieldTag,
158 InstantOpTag
159 >;
160
161 return
162 readInternal(
163 field,
164 msg,
165 iter,
166 size,
167 std::forward<TNextLayerReader>(nextLayerReader),
168 SeekTag(),
169 extraValues...);
170 }
171
188 template <typename TMsg, typename TIter, typename TNextLayerWriter>
190 Field& field,
191 const TMsg& msg,
192 TIter& iter,
193 std::size_t size,
194 TNextLayerWriter&& nextLayerWriter) const
195 {
196 using IterType = typename std::decay<decltype(iter)>::type;
197 using Tag = typename std::iterator_traits<IterType>::iterator_category;
198
199 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), Tag());
200 }
201
202protected:
209 static bool verifyFieldValue(const Field& field)
210 {
211 return field == Field();
212 }
213
220 template <typename TEscField>
221 static bool verifyEscFieldValue(const TEscField& field)
222 {
223 return field == TEscField();
224 }
225
233 static void prepareFieldForWrite(Field& field)
234 {
235 static_cast<void>(field);
236 }
237
238private:
239 template <typename... TParams>
240 using SeekFieldTag = comms::details::tag::Tag1<>;
241
242 template <typename... TParams>
243 using InstantOpTag = comms::details::tag::Tag2<>;
244
245 template <typename... TParams>
246 using VerifyBeforeReadTag = comms::details::tag::Tag3<>;
247
248 template <typename... TParams>
249 using VerifyAfterReadTag = comms::details::tag::Tag4<>;
250
251 template <typename... TParams>
252 using EscapeSupportedTag = comms::details::tag::Tag5<>;
253
254 template <typename... TParams>
255 using NoEscapeTag = comms::details::tag::Tag6<>;
256
257 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
258 ErrorStatus verifyRead(
259 Field& field,
260 TMsg& msg,
261 TIter& iter,
262 std::size_t size,
263 TReader&& nextLayerReader,
264 TExtraValues... extraValues)
265 {
266 auto fromIter = iter;
267 auto* msgPtr = BaseImpl::toMsgPtr(msg);
268 auto fieldLen = Field::minLength();
269
270 static_assert(Field::minLength() == Field::maxLength(), "Cannot verify variable length suffix before reading rest of the message frame");
271 auto& thisObj = BaseImpl::thisLayer();
272 if (msgPtr != nullptr) {
273 fieldLen = thisObj.doFieldLength(*msgPtr);
274 }
275 auto toIter = fromIter + (size - fieldLen);
276
277 auto fieldEs = thisObj.doReadField(msgPtr, field, toIter, fieldLen);
278 if (fieldEs != ErrorStatus::Success) {
279 return fieldEs;
280 }
281
282 bool verified = thisObj.verifyFieldValue(field);
283 if (!verified) {
285 }
286
287 auto es = nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
288 if (es == ErrorStatus::Success) {
289 iter = toIter;
290 }
291
292 return es;
293 }
294
295 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
296 ErrorStatus readVerify(
297 Field& field,
298 TMsg& msg,
299 TIter& iter,
300 std::size_t size,
301 TReader&& nextLayerReader,
302 TExtraValues... extraValues)
303 {
304 auto fromIter = iter;
305
306 auto es = nextLayerReader.read(msg, iter, size, extraValues...);
307 if ((es == ErrorStatus::NotEnoughData) ||
308 (es == ErrorStatus::ProtocolError)) {
309 return es;
310 }
311
312 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
313 COMMS_ASSERT(len <= size);
314 auto remSize = size - len;
315 auto* msgPtr = BaseImpl::toMsgPtr(msg);
316 auto& thisObj = BaseImpl::thisLayer();
317 auto fieldEs = thisObj.doReadField(msgPtr, field, iter, remSize);
318 if (fieldEs == ErrorStatus::NotEnoughData) {
319 BaseImpl::updateMissingSize(field, remSize, extraValues...);
320 }
321
322 if (fieldEs != ErrorStatus::Success) {
323 BaseImpl::resetMsg(msg);
324 return fieldEs;
325 }
326
327 bool verified = thisObj.verifyFieldValue(field);
328 if (!verified) {
330 }
331
332 return es;
333 }
334
335 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
336 ErrorStatus seekFieldRead(
337 Field& field,
338 TMsg& msg,
339 TIter& iter,
340 std::size_t size,
341 TReader&& nextLayerReader,
342 TExtraValues... extraValues)
343 {
344 auto& thisObj = BaseImpl::thisLayer();
345 auto* msgPtr = BaseImpl::toMsgPtr(msg);
346
347 auto fromIter = iter;
348 std::size_t consumed = 0U;
349 while (consumed < size) {
350 auto iterTmp = iter;
351 auto remSize = size - consumed;
352
353 auto fieldEs = thisObj.doReadField(msgPtr, field, iterTmp, remSize);
354 if (fieldEs == ErrorStatus::NotEnoughData) {
355 BaseImpl::updateMissingSize(field, remSize, extraValues...);
356 BaseImpl::resetMsg(msg);
357 return fieldEs;
358 }
359
360 if ((fieldEs == ErrorStatus::Success) &&
361 (!fieldEscapedInternal(fromIter, iter)) &&
362 (thisObj.verifyFieldValue(field))) {
363 // Set iter to point after field
364 iter = iterTmp;
365 break;
366 }
367
368 ++iter;
369 ++consumed;
370 }
371
372 if (size <= consumed) {
373 // Field hasn't been recognized
374 return ErrorStatus::NotEnoughData;
375 }
376
377 // iter is already past the sync field
378 return nextLayerReader.read(msg, fromIter, consumed, extraValues...);
379 }
380
381 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
382 ErrorStatus readInternal(
383 Field& field,
384 TMsg& msg,
385 TIter& iter,
386 std::size_t size,
387 TReader&& nextLayerReader,
388 SeekFieldTag<>,
389 TExtraValues... extraValues)
390 {
391 return
392 seekFieldRead(
393 field,
394 msg,
395 iter,
396 size,
397 std::forward<TReader>(nextLayerReader),
398 extraValues...);
399 }
400
401 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
402 ErrorStatus readInternal(
403 Field& field,
404 TMsg& msg,
405 TIter& iter,
406 std::size_t size,
407 TReader&& nextLayerReader,
408 InstantOpTag<>,
409 TExtraValues... extraValues)
410 {
411 using VerifyTag =
412 typename comms::util::LazyShallowConditional<
413 ParsedOptionsInternal::HasVerifyBeforeRead
414 >::template Type<
415 VerifyBeforeReadTag,
416 VerifyAfterReadTag
417 >;
418
419 return
420 readInternal(
421 field,
422 msg,
423 iter,
424 size,
425 std::forward<TReader>(nextLayerReader),
426 VerifyTag(),
427 extraValues...);
428 }
429
430 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
431 ErrorStatus readInternal(
432 Field& field,
433 TMsg& msg,
434 TIter& iter,
435 std::size_t size,
436 TReader&& nextLayerReader,
437 VerifyBeforeReadTag<>,
438 TExtraValues... extraValues)
439 {
440 return
441 verifyRead(
442 field,
443 msg,
444 iter,
445 size,
446 std::forward<TReader>(nextLayerReader),
447 extraValues...);
448 }
449
450 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
451 ErrorStatus readInternal(
452 Field& field,
453 TMsg& msg,
454 TIter& iter,
455 std::size_t size,
456 TReader&& nextLayerReader,
457 VerifyAfterReadTag<>,
458 TExtraValues... extraValues)
459 {
460 return
461 readVerify(
462 field,
463 msg,
464 iter,
465 size,
466 std::forward<TReader>(nextLayerReader),
467 extraValues...);
468 }
469
470 template <typename TMsg, typename TIter, typename TWriter>
471 ErrorStatus writeInternalRandomAccess(
472 Field& field,
473 const TMsg& msg,
474 TIter& iter,
475 std::size_t size,
476 TWriter&& nextLayerWriter) const
477 {
478 auto fromIter = iter;
479 auto es = nextLayerWriter.write(msg, iter, size);
480 if ((es != comms::ErrorStatus::Success) &&
482 return es;
483 }
484
485 COMMS_ASSERT(fromIter <= iter);
486 auto len = static_cast<std::size_t>(std::distance(fromIter, iter));
487 auto remSize = size - len;
488 auto& thisObj = BaseImpl::thisLayer();
489
490 thisObj.prepareFieldForWrite(field);
491 auto esTmp = thisObj.doWriteField(&msg, field, iter, remSize);
492 if (esTmp != comms::ErrorStatus::Success) {
493 return esTmp;
494 }
495
496 return es;
497 }
498
499 template <typename TMsg, typename TIter, typename TWriter>
500 ErrorStatus writeInternalOutput(
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 fieldLen = thisObj.doFieldLength(msg);
509 auto es = nextLayerWriter.write(msg, iter, size - fieldLen);
510 if ((es != comms::ErrorStatus::Success) &&
512 return es;
513 }
514
515 auto esTmp = thisObj.doWriteField(&msg, field, iter, fieldLen);
516 static_cast<void>(esTmp);
519 }
520
521 template <typename TMsg, typename TIter, typename TWriter>
522 ErrorStatus writeInternal(
523 Field& field,
524 const TMsg& msg,
525 TIter& iter,
526 std::size_t size,
527 TWriter&& nextLayerWriter,
528 std::random_access_iterator_tag) const
529 {
530 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
531 }
532
533 template <typename TMsg, typename TIter, typename TWriter>
534 ErrorStatus writeInternal(
535 Field& field,
536 const TMsg& msg,
537 TIter& iter,
538 std::size_t size,
539 TWriter&& nextLayerWriter,
540 std::output_iterator_tag) const
541 {
542 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
543 }
544
545 template <typename TMsg, typename TIter>
546 ErrorStatus fieldUpdateInternal(const TMsg* msgPtr, TIter from, TIter to, std::size_t size, Field& field) const
547 {
548 COMMS_ASSERT(from <= to);
549 auto len = static_cast<std::size_t>(std::distance(from, to));
550 auto& thisObj = BaseImpl::thisLayer();
551 if (msgPtr != nullptr) {
552 COMMS_ASSERT(len == (size - thisObj.doFieldLength(*msgPtr)));
553 }
554 else {
555 COMMS_ASSERT(len == (size - Field::maxLength()));
556 }
557 auto remSize = size - len;
558
559 bool checksumValid = false;
560 auto checksum =
561 thisObj.calculateSyncSuffix(
562 msgPtr,
563 from,
564 len,
565 checksumValid);
566
567 if (!checksumValid) {
569 }
570
571 thisObj.prepareFieldForWrite(checksum, msgPtr, field);
572 return thisObj.doWriteField(msgPtr, field, to, remSize);
573 }
574
575 template <typename TIter>
576 bool fieldEscapedInternal(TIter from, TIter to, NoEscapeTag<>)
577 {
578 static_cast<void>(from);
579 static_cast<void>(to);
580 return false;
581 }
582
583 template <typename TIter>
584 bool fieldEscapedInternal(TIter from, TIter to, EscapeSupportedTag<>)
585 {
586 unsigned escCount = 0U;
587 auto& thisObj = BaseImpl::thisLayer();
588
589 while (from != to) {
590 auto dist = static_cast<std::size_t>(std::distance(from, to));
591 dist = std::min(dist, EscField::maxLength());
592 if (dist < EscField::minLength()) {
593 break;
594 }
595
596 auto maxLenIter = to;
597 std::advance(maxLenIter, -static_cast<int>(dist));
598
599 auto iter = to;
600 std::advance(iter, -static_cast<int>(EscField::minLength()));
601 auto prevCount = escCount;
602 while (true) {
603 auto iterTmp = iter;
604 EscField escField;
605 auto len = static_cast<std::size_t>(std::distance(iterTmp, to));
606 auto es = escField.read(iterTmp, len);
607 if ((es == comms::ErrorStatus::Success) &&
608 (iterTmp == to) &&
609 (thisObj.verifyEscFieldValue(escField))) {
610 ++escCount;
611 break;
612 }
613
614 if (iter == maxLenIter) {
615 break;
616 }
617
618 std::advance(iter, -1);
619 }
620
621 if (prevCount == escCount) {
622 // Failed to detect escField
623 break;
624 }
625
626 // esc field was detected, trying again
627 to = iter;
628 }
629
630 // Escaped only if odd number of esc field is detected
631 return (escCount & 0x1) != 0U;
632 }
633
634 template <typename TIter>
635 bool fieldEscapedInternal(TIter from, TIter to)
636 {
637 using EscTag =
638 typename comms::util::LazyShallowConditional<
639 std::is_same<EscField, void>::value
640 >::template Type<
641 NoEscapeTag,
642 EscapeSupportedTag
643 >;
644
645 return fieldEscapedInternal(from, to, EscTag());
646 }
647
648 static_assert(!(hasVerifyBeforeRead() && hasSeekField()),
649 "Usage of both comms::option::def::FrameLayerVerifyBeforeRead and comms::option::def::FrameLayerSeekField at the same time are not allowed");
650};
651
652namespace details
653{
654template <typename T>
655struct SyncSuffixLayerCheckHelper
656{
657 static const bool Value = false;
658};
659
660template <typename TField, typename TNextLayer, typename... TOptions>
661struct SyncSuffixLayerCheckHelper<SyncSuffixLayer<TField, TNextLayer, TOptions...> >
662{
663 static const bool Value = true;
664};
665
666} // namespace details
667
671template <typename T>
672constexpr bool isSyncSuffixLayer()
673{
674 return details::SyncSuffixLayerCheckHelper<T>::Value;
675}
676
677} // namespace frame
678
679} // namespace comms
680
681COMMS_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 uses "sync" field as a suffix (after message payload) to all the preceding data writ...
Definition SyncSuffixLayer.h:60
static bool verifyFieldValue(const Field &field)
Verify the validity of the field.
Definition SyncSuffixLayer.h:209
ErrorStatus doWrite(Field &field, const TMsg &msg, TIter &iter, std::size_t size, TNextLayerWriter &&nextLayerWriter) const
Customized write functionality, invoked by write().
Definition SyncSuffixLayer.h:189
SyncSuffixLayer(SyncSuffixLayer &&)=default
Move constructor.
SyncSuffixLayer(const SyncSuffixLayer &)=default
Copy constructor.
static constexpr bool hasVerifyBeforeRead()
Compile time inquiry of whether comms::option::def::FrameLayerVerifyBeforeRead options has been used.
Definition SyncSuffixLayer.h:99
ErrorStatus doRead(Field &field, TMsg &msg, TIter &iter, std::size_t size, TNextLayerReader &&nextLayerReader, TExtraValues... extraValues)
Customized read functionality, invoked by read().
Definition SyncSuffixLayer.h:141
SyncSuffixLayer()=default
Default constructor.
static bool verifyEscFieldValue(const TEscField &field)
Verify the validity of the escapte field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncSuffixLayer.h:221
static constexpr bool hasSeekField()
Compile time inquiry of whether comms::option::def::FrameLayerSeekField options has been used.
Definition SyncSuffixLayer.h:106
typename ParsedOptionsInternal::EscField EscField
Type of the escape field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncSuffixLayer.h:70
~SyncSuffixLayer() noexcept=default
Destructor.
typename BaseImpl::Field Field
Type of the field object used to read/write checksum value.
Definition SyncSuffixLayer.h:66
constexpr bool isSyncSuffixLayer()
Compile time check of whether the provided type is a variant of SyncSuffixLayer.
Definition SyncSuffixLayer.h:672
static void prepareFieldForWrite(Field &field)
Prepare field for writing.
Definition SyncSuffixLayer.h:233
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.