COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
MsgSizeLayer.h
Go to the documentation of this file.
1//
2// Copyright 2014 - 2024 (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 <iterator>
14#include <type_traits>
15
18#include "comms/MessageBase.h"
19#include "comms/details/tag.h"
20#include "comms/protocol/details/ProtocolLayerBase.h"
21#include "comms/protocol/details/MsgSizeLayerOptionsParser.h"
22#include "comms/protocol/details/ProtocolLayerExtendingClassHelper.h"
24
25COMMS_MSVC_WARNING_PUSH
26COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
27
28namespace comms
29{
30
31namespace protocol
32{
33
34namespace details
35{
36template <bool TValidPtr>
37struct MsgSizeLayerConstNullPtrCastHelper
38{
39 template <typename TPtr>
40 using Type = const typename TPtr::element_type*;
41};
42
43template <>
44struct MsgSizeLayerConstNullPtrCastHelper<false>
45{
46 template <typename TPtr>
47 using Type = const void*;
48};
49
50} // namespace details
51
64template <typename TField, typename TNextLayer, typename... TOptions>
65class MsgSizeLayer : public
67 TField,
68 TNextLayer,
69 details::ProtocolLayerExtendingClassT<
70 MsgSizeLayer<TField, TNextLayer, TOptions...>,
71 details::MsgSizeLayerOptionsParser<TOptions...>
72 >,
73 comms::option::ProtocolLayerDisallowReadUntilDataSplit
74 >
75{
76 using BaseImpl =
78 TField,
79 TNextLayer,
80 details::ProtocolLayerExtendingClassT<
81 MsgSizeLayer<TField, TNextLayer, TOptions...>,
82 details::MsgSizeLayerOptionsParser<TOptions...>
83 >,
85 >;
86
87 using ParsedOptionsInternal = details::MsgSizeLayerOptionsParser<TOptions...>;
88public:
90 using Field = typename BaseImpl::Field;
91
95 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
96
98 explicit MsgSizeLayer() = default;
99
101 MsgSizeLayer(const MsgSizeLayer&) = default;
102
105
107 ~MsgSizeLayer() noexcept = default;
108
110 MsgSizeLayer& operator=(const MsgSizeLayer&) = default;
111
113 MsgSizeLayer& operator=(MsgSizeLayer&&) = default;
114
119 static constexpr bool hasExtendingClass()
120 {
121 return ParsedOptionsInternal::HasExtendingClass;
122 }
123
125
126 static constexpr std::size_t doFieldLength()
127 {
128 return BaseImpl::doFieldLength();
129 }
130
131 template <typename TMsg>
132 constexpr std::size_t doFieldLength(const TMsg& msg) const
133 {
134 return fieldLengthInternal(msg, LengthTag<>());
135 }
137
170 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
172 Field& field,
173 TMsg& msg,
174 TIter& iter,
175 std::size_t size,
176 TNextLayerReader&& nextLayerReader,
177 TExtraValues... extraValues)
178 {
179 using IterType = typename std::decay<decltype(iter)>::type;
180 using IterTag = typename std::iterator_traits<IterType>::iterator_category;
181 static_assert(
182 std::is_base_of<std::random_access_iterator_tag, IterTag>::value,
183 "Current implementation of MsgSizeLayer requires iterator used for reading to be random-access one.");
184
185 auto begIter = iter;
186 auto* msgPtr = BaseImpl::toMsgPtr(msg);
187 auto& thisObj = BaseImpl::thisLayer();
188 auto es = thisObj.doReadField(msgPtr, field, iter, size);
189 if (es == ErrorStatus::NotEnoughData) {
190 BaseImpl::updateMissingSize(field, size, extraValues...);
191 }
192
193 if (es != ErrorStatus::Success) {
194 return es;
195 }
196
197 auto fromIter = iter;
198 auto readFieldLength = static_cast<std::size_t>(std::distance(begIter, iter));
199 std::size_t actualRemainingSize = (size - readFieldLength);
200 std::size_t requiredRemainingSize = thisObj.getRemainingSizeFromField(field);
201
202 if (actualRemainingSize < requiredRemainingSize) {
203 BaseImpl::setMissingSize(requiredRemainingSize - actualRemainingSize, extraValues...);
204 return ErrorStatus::NotEnoughData;
205 }
206
207 thisObj.beforeRead(field, msgPtr);
208 es = nextLayerReader.read(msg, iter, requiredRemainingSize, extraValues...);
209 if (es == ErrorStatus::NotEnoughData) {
210 BaseImpl::resetMsg(msg);
211 return ErrorStatus::ProtocolError;
212 }
213
214 if (es != ErrorStatus::ProtocolError) {
215 iter = fromIter;
216 std::advance(iter, requiredRemainingSize);
217 }
218
219 auto consumed =
220 static_cast<std::size_t>(std::distance(fromIter, iter));
221 if (consumed < requiredRemainingSize) {
222 auto diff = requiredRemainingSize - consumed;
223 std::advance(iter, diff);
224 }
225 return es;
226 }
227
228
248 template <typename TMsg, typename TIter, typename TNextLayerWriter>
250 Field& field,
251 const TMsg& msg,
252 TIter& iter,
253 std::size_t size,
254 TNextLayerWriter&& nextLayerWriter) const
255 {
256 using MsgType = typename std::decay<decltype(msg)>::type;
257 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), MsgLengthTag<MsgType>());
258 }
259
269 template <typename TIter, typename TNextLayerUpdater>
271 Field& field,
272 TIter& iter,
273 std::size_t size,
274 TNextLayerUpdater&& nextLayerUpdater) const
275 {
276 using LocalMsgPtr = typename BaseImpl::MsgPtr;
277 using ConstNullptrType =
278 typename details::MsgSizeLayerConstNullPtrCastHelper<
279 !std::is_void<LocalMsgPtr>::value
280 >::template Type<LocalMsgPtr>;
281 auto noMsgPtr = static_cast<ConstNullptrType>(nullptr);
282 return doUpdateInternal(noMsgPtr, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), NoMsgTypeTag<>());
283 }
284
294 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
296 const TMsg& msg,
297 Field& field,
298 TIter& iter,
299 std::size_t size,
300 TNextLayerUpdater&& nextLayerUpdater) const
301 {
302 return doUpdateInternal(&msg, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), ValidMsgTypeTag<>());
303 }
304
305protected:
309 static std::size_t getRemainingSizeFromField(const Field& field)
310 {
311 return static_cast<std::size_t>(field.getValue());
312 }
313
322 template <typename TMsg>
323 static void beforeRead(const Field& field, TMsg* msg)
324 {
325 static_cast<void>(field);
326 static_cast<void>(msg);
327 }
328
337 template <typename TMsg>
338 static void prepareFieldForWrite(std::size_t size, const TMsg* msg, Field& field)
339 {
340 static_cast<void>(msg);
341 field.setValue(size);
342 }
343
344private:
345 template <typename... TParams>
346 using FixedLengthTag = typename BaseImpl::template FixedLengthTag<TParams...>;
347
348 template <typename...TParams>
349 using VarLengthTag = typename BaseImpl::template VarLengthTag<TParams...>;
350
351 template <typename... TParams>
352 using LengthTag = typename BaseImpl::template LengthTag<TParams...>;
353
354 template <typename... TParams>
355 using MsgHasLengthTag = comms::details::tag::Tag3<>;
356
357 template <typename... TParams>
358 using MsgNoLengthTag = comms::details::tag::Tag4<>;
359
360 template <typename... TParams>
361 using ValidMsgTypeTag = comms::details::tag::Tag5<>;
362
363 template <typename... TParams>
364 using NoMsgTypeTag = comms::details::tag::Tag6<>;
365
366 template<typename TMsg>
367 using MsgLengthTag =
368 typename comms::util::LazyShallowConditional<
369 details::ProtocolLayerHasFieldsImpl<TMsg>::Value || TMsg::hasLength()
370 >::template Type<
371 MsgHasLengthTag,
372 MsgNoLengthTag
373 >;
374
375 template <typename TMsg, typename TIter, typename TWriter>
376 ErrorStatus writeInternalHasLength(
377 Field& field,
378 const TMsg& msg,
379 TIter& iter,
380 std::size_t size,
381 TWriter&& nextLayerWriter) const
382 {
383 std::size_t lenValue = BaseImpl::nextLayer().length(msg);
384 auto& thisObj = BaseImpl::thisLayer();
385
386 thisObj.prepareFieldForWrite(lenValue, &msg, field);
387 auto es = thisObj.doWriteField(&msg, field, iter, size);
388 if (es != ErrorStatus::Success) {
389 return es;
390 }
391
392 COMMS_ASSERT(field.length() <= size);
393 return nextLayerWriter.write(msg, iter, size - field.length());
394 }
395
396 template <typename TMsg, typename TIter, typename TWriter>
397 ErrorStatus writeInternalRandomAccess(
398 Field& field,
399 const TMsg& msg,
400 TIter& iter,
401 std::size_t size,
402 TWriter&& nextLayerWriter) const
403 {
404 auto valueIter = iter;
405 auto& thisObj = BaseImpl::thisLayer();
406 thisObj.prepareFieldForWrite(0U, &msg, field);
407 auto es = thisObj.doWriteField(&msg, field, iter, size);
408 if (es != ErrorStatus::Success) {
409 return es;
410 }
411
412 auto dataIter = iter;
413
414 auto sizeLen = field.length();
415 es = nextLayerWriter.write(msg, iter, size - sizeLen);
416 if (es != ErrorStatus::Success) {
417 return es;
418 }
419
420 auto dist = static_cast<std::size_t>(std::distance(dataIter, iter));
421 thisObj.prepareFieldForWrite(dist, &msg, field);
422 COMMS_ASSERT(field.length() == sizeLen);
423 return thisObj.doWriteField(&msg, field, valueIter, sizeLen);
424 }
425
426 template <typename TMsg, typename TIter, typename TWriter>
427 ErrorStatus writeInternalOutput(
428 Field& field,
429 const TMsg& msg,
430 TIter& iter,
431 std::size_t size,
432 TWriter&& nextLayerWriter) const
433 {
434 auto& thisObj = BaseImpl::thisLayer();
435 thisObj.prepareFieldForWrite(0U, &msg, field);
436 auto es = thisObj.doWriteField(&msg, field, iter, size);
437 if (es != ErrorStatus::Success) {
438 return es;
439 }
440
441 es = nextLayerWriter.write(msg, iter, size - field.length());
442 if ((es != ErrorStatus::Success) &&
443 (es != ErrorStatus::UpdateRequired)) {
444 return es;
445 }
446
447 return ErrorStatus::UpdateRequired;
448 }
449
450 template <typename TMsg, typename TIter, typename TWriter>
451 ErrorStatus writeInternalNoLengthTagged(
452 Field& field,
453 const TMsg& msg,
454 TIter& iter,
455 std::size_t size,
456 TWriter&& nextLayerWriter,
457 std::random_access_iterator_tag) const
458 {
459 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
460 }
461
462 template <typename TMsg, typename TIter, typename TWriter>
463 ErrorStatus writeInternalNoLengthTagged(
464 Field& field,
465 const TMsg& msg,
466 TIter& iter,
467 std::size_t size,
468 TWriter&& nextLayerWriter,
469 std::output_iterator_tag) const
470 {
471 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
472 }
473
474 template <typename TMsg, typename TIter, typename TWriter>
475 ErrorStatus writeInternalNoLength(
476 Field& field,
477 const TMsg& msg,
478 TIter& iter,
479 std::size_t size,
480 TWriter&& nextLayerWriter) const
481 {
482 static_assert(
483 (BaseImpl::MinFieldLength == BaseImpl::MaxFieldLength) ||
484 (comms::isMessageBase<typename std::decay<decltype(msg)>::type>()),
485 "Unable to perform write with size field having variable length and "
486 "no polymorphic length calculation available.");
487 using IterType = typename std::decay<decltype(iter)>::type;
488 using Tag = typename std::iterator_traits<IterType>::iterator_category;
489 return writeInternalNoLengthTagged(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter), Tag());
490 }
491
492 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
493 ErrorStatus writeInternal(
494 Field& field,
495 const TMsg& msg,
496 TIter& iter,
497 std::size_t size,
498 TWriter&& nextLayerWriter,
499 MsgHasLengthTag<TParams...>) const
500 {
501 return writeInternalHasLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
502 }
503
504 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
505 ErrorStatus writeInternal(
506 Field& field,
507 const TMsg& msg,
508 TIter& iter,
509 std::size_t size,
510 TWriter&& nextLayerWriter,
511 MsgNoLengthTag<TParams...>) const
512 {
513 return writeInternalNoLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
514 }
515
516 template <typename TMsg, typename... TParams>
517 constexpr std::size_t fieldLengthInternal(const TMsg& msg, FixedLengthTag<TParams...>) const
518 {
519 return BaseImpl::doFieldLength(msg);
520 }
521
522 template <typename TMsg, typename... TParams>
523 std::size_t fieldLengthInternal(const TMsg& msg, VarLengthTag<TParams...>) const
524 {
525 auto& thisObj = BaseImpl::thisLayer();
526 auto remSize = BaseImpl::nextLayer().length(msg);
527 Field fieldTmp;
528 thisObj.prepareFieldForWrite(remSize, &msg, fieldTmp);
529 return fieldTmp.length();
530 }
531
532 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename TForwardTag>
533 comms::ErrorStatus doUpdateInternal(
534 const TMsg* msg,
535 Field& field,
536 TIter& iter,
537 std::size_t size,
538 TNextLayerUpdater&& nextLayerUpdater,
539 TForwardTag&& tag) const
540 {
541 std::size_t lenValue = size - Field::maxLength();
542 auto& thisObj = BaseImpl::thisLayer();
543 thisObj.prepareFieldForWrite(lenValue, msg, field);
544
545 if (field.length() != Field::maxLength()) {
546 lenValue = size - field.length();
547 thisObj.prepareFieldForWrite(lenValue, msg, field);
548 }
549
550 auto es = thisObj.doWriteField(msg, field, iter, size);
551 if (es != ErrorStatus::Success) {
552 return es;
553 }
554
555 return doUpdateForward(msg, iter, size - field.length(), std::forward<TNextLayerUpdater>(nextLayerUpdater), tag);
556 }
557
558 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
559 comms::ErrorStatus doUpdateForward(
560 const TMsg* msg,
561 TIter& iter,
562 std::size_t size,
563 TNextLayerUpdater&& nextLayerUpdater,
564 NoMsgTypeTag<TParams...>) const
565 {
566 static_cast<void>(msg);
567 return nextLayerUpdater.update(iter, size);
568 }
569
570 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
571 comms::ErrorStatus doUpdateForward(
572 const TMsg* msg,
573 TIter& iter,
574 std::size_t size,
575 TNextLayerUpdater&& nextLayerUpdater,
576 ValidMsgTypeTag<TParams...>) const
577 {
578 COMMS_ASSERT(msg != nullptr);
579 return nextLayerUpdater.update(*msg, iter, size);
580 }
581};
582
583namespace details
584{
585template <typename T>
586struct MsgSizeLayerCheckHelper
587{
588 static const bool Value = false;
589};
590
591template <typename TField, typename TNextLayer>
592struct MsgSizeLayerCheckHelper<MsgSizeLayer<TField, TNextLayer> >
593{
594 static const bool Value = true;
595};
596
597} // namespace details
598
602template <typename T>
603constexpr bool isMsgSizeLayer()
604{
605 return details::MsgSizeLayerCheckHelper<T>::Value;
606}
607
608} // namespace protocol
609
610} // namespace comms
611
612COMMS_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.
Provides common base class for the custom messages with default implementation.
Base class to all the field classes.
Definition Field.h:33
Protocol layer that uses size field as a prefix to all the subsequent data written by other (next) la...
Definition MsgSizeLayer.h:75
static void prepareFieldForWrite(std::size_t size, const TMsg *msg, Field &field)
Prepare field for writing.
Definition MsgSizeLayer.h:338
static std::size_t getRemainingSizeFromField(const Field &field)
Retrieve remaining size (length) from the field.
Definition MsgSizeLayer.h:309
comms::ErrorStatus doRead(Field &field, TMsg &msg, TIter &iter, std::size_t size, TNextLayerReader &&nextLayerReader, TExtraValues... extraValues)
Customized read functionality, invoked by read().
Definition MsgSizeLayer.h:171
~MsgSizeLayer() noexcept=default
Destructor.
comms::ErrorStatus doUpdate(const TMsg &msg, Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition MsgSizeLayer.h:295
ErrorStatus doWrite(Field &field, const TMsg &msg, TIter &iter, std::size_t size, TNextLayerWriter &&nextLayerWriter) const
Customized write functionality, invoked by write().
Definition MsgSizeLayer.h:249
constexpr bool isMsgSizeLayer()
Compile time check of whether the provided type is a variant of MsgSizeLayer.
Definition MsgSizeLayer.h:603
MsgSizeLayer(const MsgSizeLayer &)=default
Copy constructor.
MsgSizeLayer(MsgSizeLayer &&)=default
Move constructor.
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition MsgSizeLayer.h:270
static void beforeRead(const Field &field, TMsg *msg)
Extra operation before read is forwarded to the next layer.
Definition MsgSizeLayer.h:323
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition MsgSizeLayer.h:95
typename BaseImpl::Field Field
Type of the field object used to read/write remaining size value.
Definition MsgSizeLayer.h:90
MsgSizeLayer()=default
Default constructor.
Base class for all the middle (non MsgDataLayer) protocol transport layers.
Definition ProtocolLayerBase.h:61
typename details::ProtocolLayerMsgPtr< NextLayer >::Type MsgPtr
Type of pointer to the message.
Definition ProtocolLayerBase.h:96
TField Field
Type of the field used for this layer.
Definition ProtocolLayerBase.h:64
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:17
constexpr bool isMessageBase()
Compile time check of of whether the type is a message extending comms::MessageBase.
Definition MessageBase.h:899
Disallow usage of ProtocolLayerForceReadUntilDataSplit option in earlier (outer wrapping) layers.
Definition options.h:1122
Replacement to some types from standard type_traits.