COMMS
Template library intended to help with implementation of communication protocols.
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 
16 #include "comms/CompileControl.h"
17 #include "comms/field/IntValue.h"
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"
23 #include "comms/util/type_traits.h"
24 
25 COMMS_MSVC_WARNING_PUSH
26 COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
27 
28 namespace comms
29 {
30 
31 namespace protocol
32 {
33 
34 namespace details
35 {
36 template <bool TValidPtr>
37 struct MsgSizeLayerConstNullPtrCastHelper
38 {
39  template <typename TPtr>
40  using Type = const typename TPtr::element_type*;
41 };
42 
43 template <>
44 struct MsgSizeLayerConstNullPtrCastHelper<false>
45 {
46  template <typename TPtr>
47  using Type = const void*;
48 };
49 
50 } // namespace details
51 
64 template <typename TField, typename TNextLayer, typename... TOptions>
65 class 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...>;
88 public:
90  using Field = typename BaseImpl::Field;
91 
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 
305 protected:
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 
344 private:
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 
583 namespace details
584 {
585 template <typename T>
586 struct MsgSizeLayerCheckHelper
587 {
588  static const bool Value = false;
589 };
590 
591 template <typename TField, typename TNextLayer>
592 struct MsgSizeLayerCheckHelper<MsgSizeLayer<TField, TNextLayer> >
593 {
594  static const bool Value = true;
595 };
596 
597 } // namespace details
598 
602 template <typename T>
603 constexpr bool isMsgSizeLayer()
604 {
605  return details::MsgSizeLayerCheckHelper<T>::Value;
606 }
607 
608 } // namespace protocol
609 
610 } // namespace comms
611 
612 COMMS_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
comms::option::def::ExtendingClass< T > ExtendingClass
Same as comms::option::def::ExtendingClass.
Definition: options.h:1822
comms::option::def::MsgType< TMsg > MsgType
Same as comms::option::def::MsgType.
Definition: options.h:1459
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.