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 - 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
14#include "comms/details/tag.h"
16#include "comms/frame/details/MsgSizeLayerBase.h"
17#include "comms/frame/details/MsgSizeLayerConstNullPtrCastHelper.h"
18#include "comms/frame/details/MsgSizeLayerOptionsParser.h"
19#include "comms/MessageBase.h"
21
22#include <iterator>
23#include <type_traits>
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{
33
47template <typename TField, typename TNextLayer, typename... TOptions>
48class MsgSizeLayer : public comms::frame::details::MsgSizeLayerBase<TField, TNextLayer, TOptions...>
49{
50 using BaseImpl = comms::frame::details::MsgSizeLayerBase<TField, TNextLayer, TOptions...>;
51 using ParsedOptionsInternal = details::MsgSizeLayerOptionsParser<TOptions...>;
52
53public:
55 using Field = typename BaseImpl::Field;
56
60 using ExtendingClass = typename ParsedOptionsInternal::ExtendingClass;
61
63 explicit MsgSizeLayer() = default;
64
66 MsgSizeLayer(const MsgSizeLayer&) = default;
67
70
72 ~MsgSizeLayer() noexcept = default;
73
75 MsgSizeLayer& operator=(const MsgSizeLayer&) = default;
76
78 MsgSizeLayer& operator=(MsgSizeLayer&&) = default;
79
84 static constexpr bool hasExtendingClass()
85 {
86 return ParsedOptionsInternal::HasExtendingClass;
87 }
88
90 static constexpr std::size_t doFieldLength()
91 {
92 return BaseImpl::doFieldLength();
93 }
94
95 template <typename TMsg>
96 constexpr std::size_t doFieldLength(const TMsg& msg) const
97 {
98 return fieldLengthInternal(msg, LengthTag<>());
99 }
101
134 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
136 Field& field,
137 TMsg& msg,
138 TIter& iter,
139 std::size_t size,
140 TNextLayerReader&& nextLayerReader,
141 TExtraValues... extraValues)
142 {
143 using IterType = typename std::decay<decltype(iter)>::type;
144 using IterTag = typename std::iterator_traits<IterType>::iterator_category;
145 static_assert(
146 std::is_base_of<std::random_access_iterator_tag, IterTag>::value,
147 "Current implementation of MsgSizeLayer requires iterator used for reading to be random-access one.");
148
149 auto begIter = iter;
150 auto* msgPtr = BaseImpl::toMsgPtr(msg);
151 auto& thisObj = BaseImpl::thisLayer();
152 auto es = thisObj.doReadField(msgPtr, field, iter, size);
153 if (es == ErrorStatus::NotEnoughData) {
154 BaseImpl::updateMissingSize(field, size, extraValues...);
155 }
156
157 if (es != ErrorStatus::Success) {
158 return es;
159 }
160
161 auto fromIter = iter;
162 auto readFieldLength = static_cast<std::size_t>(std::distance(begIter, iter));
163 std::size_t actualRemainingSize = (size - readFieldLength);
164 std::size_t requiredRemainingSize = thisObj.getRemainingSizeFromField(field);
165
166 if (actualRemainingSize < requiredRemainingSize) {
167 BaseImpl::setMissingSize(requiredRemainingSize - actualRemainingSize, extraValues...);
168 return ErrorStatus::NotEnoughData;
169 }
170
171 thisObj.beforeRead(field, msgPtr);
172 es = nextLayerReader.read(msg, iter, requiredRemainingSize, extraValues...);
173 if (es == ErrorStatus::NotEnoughData) {
174 BaseImpl::resetMsg(msg);
175 return ErrorStatus::ProtocolError;
176 }
177
178 if (es != ErrorStatus::ProtocolError) {
179 iter = fromIter;
180 std::advance(iter, requiredRemainingSize);
181 }
182
183 auto consumed =
184 static_cast<std::size_t>(std::distance(fromIter, iter));
185 if (consumed < requiredRemainingSize) {
186 auto diff = requiredRemainingSize - consumed;
187 std::advance(iter, diff);
188 }
189 return es;
190 }
191
192
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 MsgType = typename std::decay<decltype(msg)>::type;
221 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), MsgLengthTag<MsgType>());
222 }
223
233 template <typename TIter, typename TNextLayerUpdater>
235 Field& field,
236 TIter& iter,
237 std::size_t size,
238 TNextLayerUpdater&& nextLayerUpdater) const
239 {
240 using LocalMsgPtr = typename BaseImpl::MsgPtr;
241 using ConstNullptrType =
242 typename details::MsgSizeLayerConstNullPtrCastHelper<
243 !std::is_void<LocalMsgPtr>::value
244 >::template Type<LocalMsgPtr>;
245 auto noMsgPtr = static_cast<ConstNullptrType>(nullptr);
246 return doUpdateInternal(noMsgPtr, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), NoMsgTypeTag<>());
247 }
248
258 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
260 const TMsg& msg,
261 Field& field,
262 TIter& iter,
263 std::size_t size,
264 TNextLayerUpdater&& nextLayerUpdater) const
265 {
266 return doUpdateInternal(&msg, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), ValidMsgTypeTag<>());
267 }
268
269protected:
273 static std::size_t getRemainingSizeFromField(const Field& field)
274 {
275 return static_cast<std::size_t>(field.getValue());
276 }
277
286 template <typename TMsg>
287 static void beforeRead(const Field& field, TMsg* msg)
288 {
289 static_cast<void>(field);
290 static_cast<void>(msg);
291 }
292
301 template <typename TMsg>
302 static void prepareFieldForWrite(std::size_t size, const TMsg* msg, Field& field)
303 {
304 static_cast<void>(msg);
305 field.setValue(size);
306 }
307
308private:
309 template <typename... TParams>
310 using FixedLengthTag = typename BaseImpl::template FixedLengthTag<TParams...>;
311
312 template <typename...TParams>
313 using VarLengthTag = typename BaseImpl::template VarLengthTag<TParams...>;
314
315 template <typename... TParams>
316 using LengthTag = typename BaseImpl::template LengthTag<TParams...>;
317
318 template <typename... TParams>
319 using MsgHasLengthTag = comms::details::tag::Tag3<>;
320
321 template <typename... TParams>
322 using MsgNoLengthTag = comms::details::tag::Tag4<>;
323
324 template <typename... TParams>
325 using ValidMsgTypeTag = comms::details::tag::Tag5<>;
326
327 template <typename... TParams>
328 using NoMsgTypeTag = comms::details::tag::Tag6<>;
329
330 template<typename TMsg>
331 using MsgLengthTag =
332 typename comms::util::LazyShallowConditional<
333 details::FrameLayerHasFieldsImpl<TMsg>::Value || TMsg::hasLength()
334 >::template Type<
335 MsgHasLengthTag,
336 MsgNoLengthTag
337 >;
338
339 template <typename TMsg, typename TIter, typename TWriter>
340 ErrorStatus writeInternalHasLength(
341 Field& field,
342 const TMsg& msg,
343 TIter& iter,
344 std::size_t size,
345 TWriter&& nextLayerWriter) const
346 {
347 std::size_t lenValue = BaseImpl::nextLayer().length(msg);
348 auto& thisObj = BaseImpl::thisLayer();
349
350 thisObj.prepareFieldForWrite(lenValue, &msg, field);
351 auto es = thisObj.doWriteField(&msg, field, iter, size);
352 if (es != ErrorStatus::Success) {
353 return es;
354 }
355
356 COMMS_ASSERT(field.length() <= size);
357 return nextLayerWriter.write(msg, iter, size - field.length());
358 }
359
360 template <typename TMsg, typename TIter, typename TWriter>
361 ErrorStatus writeInternalRandomAccess(
362 Field& field,
363 const TMsg& msg,
364 TIter& iter,
365 std::size_t size,
366 TWriter&& nextLayerWriter) const
367 {
368 auto valueIter = iter;
369 auto& thisObj = BaseImpl::thisLayer();
370 thisObj.prepareFieldForWrite(0U, &msg, field);
371 auto es = thisObj.doWriteField(&msg, field, iter, size);
372 if (es != ErrorStatus::Success) {
373 return es;
374 }
375
376 auto dataIter = iter;
377
378 auto sizeLen = field.length();
379 es = nextLayerWriter.write(msg, iter, size - sizeLen);
380 if (es != ErrorStatus::Success) {
381 return es;
382 }
383
384 auto dist = static_cast<std::size_t>(std::distance(dataIter, iter));
385 thisObj.prepareFieldForWrite(dist, &msg, field);
386 COMMS_ASSERT(field.length() == sizeLen);
387 return thisObj.doWriteField(&msg, field, valueIter, sizeLen);
388 }
389
390 template <typename TMsg, typename TIter, typename TWriter>
391 ErrorStatus writeInternalOutput(
392 Field& field,
393 const TMsg& msg,
394 TIter& iter,
395 std::size_t size,
396 TWriter&& nextLayerWriter) const
397 {
398 auto& thisObj = BaseImpl::thisLayer();
399 thisObj.prepareFieldForWrite(0U, &msg, field);
400 auto es = thisObj.doWriteField(&msg, field, iter, size);
401 if (es != ErrorStatus::Success) {
402 return es;
403 }
404
405 es = nextLayerWriter.write(msg, iter, size - field.length());
406 if ((es != ErrorStatus::Success) &&
407 (es != ErrorStatus::UpdateRequired)) {
408 return es;
409 }
410
411 return ErrorStatus::UpdateRequired;
412 }
413
414 template <typename TMsg, typename TIter, typename TWriter>
415 ErrorStatus writeInternalNoLengthTagged(
416 Field& field,
417 const TMsg& msg,
418 TIter& iter,
419 std::size_t size,
420 TWriter&& nextLayerWriter,
421 std::random_access_iterator_tag) const
422 {
423 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
424 }
425
426 template <typename TMsg, typename TIter, typename TWriter>
427 ErrorStatus writeInternalNoLengthTagged(
428 Field& field,
429 const TMsg& msg,
430 TIter& iter,
431 std::size_t size,
432 TWriter&& nextLayerWriter,
433 std::output_iterator_tag) const
434 {
435 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
436 }
437
438 template <typename TMsg, typename TIter, typename TWriter>
439 ErrorStatus writeInternalNoLength(
440 Field& field,
441 const TMsg& msg,
442 TIter& iter,
443 std::size_t size,
444 TWriter&& nextLayerWriter) const
445 {
446 static_assert(
447 (BaseImpl::MinFieldLength == BaseImpl::MaxFieldLength) ||
448 (comms::isMessageBase<typename std::decay<decltype(msg)>::type>()),
449 "Unable to perform write with size field having variable length and "
450 "no polymorphic length calculation available.");
451 using IterType = typename std::decay<decltype(iter)>::type;
452 using Tag = typename std::iterator_traits<IterType>::iterator_category;
453 return writeInternalNoLengthTagged(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter), Tag());
454 }
455
456 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
457 ErrorStatus writeInternal(
458 Field& field,
459 const TMsg& msg,
460 TIter& iter,
461 std::size_t size,
462 TWriter&& nextLayerWriter,
463 MsgHasLengthTag<TParams...>) const
464 {
465 return writeInternalHasLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
466 }
467
468 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
469 ErrorStatus writeInternal(
470 Field& field,
471 const TMsg& msg,
472 TIter& iter,
473 std::size_t size,
474 TWriter&& nextLayerWriter,
475 MsgNoLengthTag<TParams...>) const
476 {
477 return writeInternalNoLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
478 }
479
480 template <typename TMsg, typename... TParams>
481 constexpr std::size_t fieldLengthInternal(const TMsg& msg, FixedLengthTag<TParams...>) const
482 {
483 return BaseImpl::doFieldLength(msg);
484 }
485
486 template <typename TMsg, typename... TParams>
487 std::size_t fieldLengthInternal(const TMsg& msg, VarLengthTag<TParams...>) const
488 {
489 auto& thisObj = BaseImpl::thisLayer();
490 auto remSize = BaseImpl::nextLayer().length(msg);
491 Field fieldTmp;
492 thisObj.prepareFieldForWrite(remSize, &msg, fieldTmp);
493 return fieldTmp.length();
494 }
495
496 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename TForwardTag>
497 comms::ErrorStatus doUpdateInternal(
498 const TMsg* msg,
499 Field& field,
500 TIter& iter,
501 std::size_t size,
502 TNextLayerUpdater&& nextLayerUpdater,
503 TForwardTag&& tag) const
504 {
505 std::size_t lenValue = size - Field::maxLength();
506 auto& thisObj = BaseImpl::thisLayer();
507 thisObj.prepareFieldForWrite(lenValue, msg, field);
508
509 if (field.length() != Field::maxLength()) {
510 lenValue = size - field.length();
511 thisObj.prepareFieldForWrite(lenValue, msg, field);
512 }
513
514 auto es = thisObj.doWriteField(msg, field, iter, size);
515 if (es != ErrorStatus::Success) {
516 return es;
517 }
518
519 return doUpdateForward(msg, iter, size - field.length(), std::forward<TNextLayerUpdater>(nextLayerUpdater), tag);
520 }
521
522 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
523 comms::ErrorStatus doUpdateForward(
524 const TMsg* msg,
525 TIter& iter,
526 std::size_t size,
527 TNextLayerUpdater&& nextLayerUpdater,
528 NoMsgTypeTag<TParams...>) const
529 {
530 static_cast<void>(msg);
531 return nextLayerUpdater.update(iter, size);
532 }
533
534 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
535 comms::ErrorStatus doUpdateForward(
536 const TMsg* msg,
537 TIter& iter,
538 std::size_t size,
539 TNextLayerUpdater&& nextLayerUpdater,
540 ValidMsgTypeTag<TParams...>) const
541 {
542 COMMS_ASSERT(msg != nullptr);
543 return nextLayerUpdater.update(*msg, iter, size);
544 }
545};
546
547namespace details
548{
549template <typename T>
550struct MsgSizeLayerCheckHelper
551{
552 static const bool Value = false;
553};
554
555template <typename TField, typename TNextLayer>
556struct MsgSizeLayerCheckHelper<MsgSizeLayer<TField, TNextLayer> >
557{
558 static const bool Value = true;
559};
560
561} // namespace details
562
566template <typename T>
567constexpr bool isMsgSizeLayer()
568{
569 return details::MsgSizeLayerCheckHelper<T>::Value;
570}
571
572} // namespace frame
573
574} // namespace comms
575
576COMMS_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:49
~MsgSizeLayer() noexcept=default
Destructor.
constexpr bool isMsgSizeLayer()
Compile time check of whether the provided type is a variant of MsgSizeLayer.
Definition MsgSizeLayer.h:567
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:213
MsgSizeLayer(MsgSizeLayer &&)=default
Move constructor.
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:259
typename ParsedOptionsInternal::ExtendingClass ExtendingClass
Type of real extending class.
Definition MsgSizeLayer.h:60
MsgSizeLayer(const MsgSizeLayer &)=default
Copy constructor.
typename BaseImpl::Field Field
Type of the field object used to read/write remaining size value.
Definition MsgSizeLayer.h:55
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:135
MsgSizeLayer()=default
Default constructor.
static void beforeRead(const Field &field, TMsg *msg)
Extra operation before read is forwarded to the next layer.
Definition MsgSizeLayer.h:287
static void prepareFieldForWrite(std::size_t size, const TMsg *msg, Field &field)
Prepare field for writing.
Definition MsgSizeLayer.h:302
static std::size_t getRemainingSizeFromField(const Field &field)
Retrieve remaining size (length) from the field.
Definition MsgSizeLayer.h:273
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition MsgSizeLayer.h:234
comms::frame::MsgSizeLayer< TField, TNextLayer, TOptions... > MsgSizeLayer
Alias to the comms::frame::MsgSizeLayer.
Definition MsgSizeLayer.h:25
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
Replacement to some types from standard type_traits.