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#include <utility>
25
26COMMS_MSVC_WARNING_PUSH
27COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
28
29namespace comms
30{
31
32namespace frame
33{
34
48template <typename TField, typename TNextLayer, typename... TOptions>
49class MsgSizeLayer : public comms::frame::details::MsgSizeLayerBase<TField, TNextLayer, TOptions...>
50{
51 using BaseImpl = comms::frame::details::MsgSizeLayerBase<TField, TNextLayer, TOptions...>;
52 using ParsedOptionsInternal = details::MsgSizeLayerOptionsParser<TOptions...>;
53
54public:
56 using Field = typename BaseImpl::Field;
57
59 explicit MsgSizeLayer() = default;
60
62 MsgSizeLayer(const MsgSizeLayer&) = default;
63
66
68 ~MsgSizeLayer() noexcept = default;
69
71 MsgSizeLayer& operator=(const MsgSizeLayer&) = default;
72
74 MsgSizeLayer& operator=(MsgSizeLayer&&) = default;
75
78 static constexpr bool hasExtendingClass()
79 {
80 return ParsedOptionsInternal::HasExtendingClass;
81 }
82
84 static constexpr std::size_t doFieldLength()
85 {
86 return BaseImpl::doFieldLength();
87 }
88
89 template <typename TMsg>
90 constexpr std::size_t doFieldLength(const TMsg& msg) const
91 {
92 return fieldLengthInternal(msg, LengthTag<>());
93 }
95
128 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
130 Field& field,
131 TMsg& msg,
132 TIter& iter,
133 std::size_t size,
134 TNextLayerReader&& nextLayerReader,
135 TExtraValues... extraValues)
136 {
137 using IterType = typename std::decay<decltype(iter)>::type;
138 using IterTag = typename std::iterator_traits<IterType>::iterator_category;
139 static_assert(
140 std::is_base_of<std::random_access_iterator_tag, IterTag>::value,
141 "Current implementation of MsgSizeLayer requires iterator used for reading to be random-access one.");
142
143 auto begIter = iter;
144 auto* msgPtr = BaseImpl::toMsgPtr(msg);
145 auto& thisObj = BaseImpl::thisLayer();
146 auto es = thisObj.doReadField(msgPtr, field, iter, size);
147 if (es == ErrorStatus::NotEnoughData) {
148 BaseImpl::updateMissingSize(field, size, extraValues...);
149 }
150
151 if (es != ErrorStatus::Success) {
152 return es;
153 }
154
155 auto fromIter = iter;
156 auto readFieldLength = static_cast<std::size_t>(std::distance(begIter, iter));
157 std::size_t actualRemainingSize = (size - readFieldLength);
158 std::size_t requiredRemainingSize = thisObj.getRemainingSizeFromField(field);
159
160 if (actualRemainingSize < requiredRemainingSize) {
161 BaseImpl::setMissingSize(requiredRemainingSize - actualRemainingSize, extraValues...);
162 return ErrorStatus::NotEnoughData;
163 }
164
165 thisObj.beforeRead(field, msgPtr);
166 es = nextLayerReader.read(msg, iter, requiredRemainingSize, extraValues...);
167 if (es == ErrorStatus::NotEnoughData) {
168 BaseImpl::resetMsg(msg);
169 return ErrorStatus::ProtocolError;
170 }
171
172 if (es != ErrorStatus::ProtocolError) {
173 iter = fromIter;
174 std::advance(iter, requiredRemainingSize);
175 }
176
177 auto consumed =
178 static_cast<std::size_t>(std::distance(fromIter, iter));
179 if (consumed < requiredRemainingSize) {
180 auto diff = requiredRemainingSize - consumed;
181 std::advance(iter, diff);
182 }
183 return es;
184 }
185
205 template <typename TMsg, typename TIter, typename TNextLayerWriter>
207 Field& field,
208 const TMsg& msg,
209 TIter& iter,
210 std::size_t size,
211 TNextLayerWriter&& nextLayerWriter) const
212 {
213 using MsgType = typename std::decay<decltype(msg)>::type;
214 return writeInternal(field, msg, iter, size, std::forward<TNextLayerWriter>(nextLayerWriter), MsgLengthTag<MsgType>());
215 }
216
226 template <typename TIter, typename TNextLayerUpdater>
228 Field& field,
229 TIter& iter,
230 std::size_t size,
231 TNextLayerUpdater&& nextLayerUpdater) const
232 {
233 using LocalMsgPtr = typename BaseImpl::MsgPtr;
234 using ConstNullptrType =
235 typename details::MsgSizeLayerConstNullPtrCastHelper<
236 !std::is_void<LocalMsgPtr>::value
237 >::template Type<LocalMsgPtr>;
238 auto noMsgPtr = static_cast<ConstNullptrType>(nullptr);
239 return doUpdateInternal(noMsgPtr, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), NoMsgTypeTag<>());
240 }
241
251 template <typename TMsg, typename TIter, typename TNextLayerUpdater>
253 const TMsg& msg,
254 Field& field,
255 TIter& iter,
256 std::size_t size,
257 TNextLayerUpdater&& nextLayerUpdater) const
258 {
259 return doUpdateInternal(&msg, field, iter, size, std::forward<TNextLayerUpdater>(nextLayerUpdater), ValidMsgTypeTag<>());
260 }
261
262protected:
266 static std::size_t getRemainingSizeFromField(const Field& field)
267 {
268 return static_cast<std::size_t>(field.getValue());
269 }
270
279 template <typename TMsg>
280 static void beforeRead(const Field& field, TMsg* msg)
281 {
282 static_cast<void>(field);
283 static_cast<void>(msg);
284 }
285
294 template <typename TMsg>
295 static void prepareFieldForWrite(std::size_t size, const TMsg* msg, Field& field)
296 {
297 static_cast<void>(msg);
298 field.setValue(size);
299 }
300
301private:
302 template <typename... TParams>
303 using FixedLengthTag = typename BaseImpl::template FixedLengthTag<TParams...>;
304
305 template <typename...TParams>
306 using VarLengthTag = typename BaseImpl::template VarLengthTag<TParams...>;
307
308 template <typename... TParams>
309 using LengthTag = typename BaseImpl::template LengthTag<TParams...>;
310
311 template <typename... TParams>
312 using MsgHasLengthTag = comms::details::tag::Tag3<>;
313
314 template <typename... TParams>
315 using MsgNoLengthTag = comms::details::tag::Tag4<>;
316
317 template <typename... TParams>
318 using ValidMsgTypeTag = comms::details::tag::Tag5<>;
319
320 template <typename... TParams>
321 using NoMsgTypeTag = comms::details::tag::Tag6<>;
322
323 template<typename TMsg>
324 using MsgLengthTag =
325 typename comms::util::LazyShallowConditional<
326 details::FrameLayerHasFieldsImpl<TMsg>::Value || TMsg::hasLength()
327 >::template Type<
328 MsgHasLengthTag,
329 MsgNoLengthTag
330 >;
331
332 template <typename TMsg, typename TIter, typename TWriter>
333 ErrorStatus writeInternalHasLength(
334 Field& field,
335 const TMsg& msg,
336 TIter& iter,
337 std::size_t size,
338 TWriter&& nextLayerWriter) const
339 {
340 std::size_t lenValue = BaseImpl::nextLayer().length(msg);
341 auto& thisObj = BaseImpl::thisLayer();
342
343 thisObj.prepareFieldForWrite(lenValue, &msg, field);
344 auto es = thisObj.doWriteField(&msg, field, iter, size);
345 if (es != ErrorStatus::Success) {
346 return es;
347 }
348
349 COMMS_ASSERT(field.length() <= size);
350 return nextLayerWriter.write(msg, iter, size - field.length());
351 }
352
353 template <typename TMsg, typename TIter, typename TWriter>
354 ErrorStatus writeInternalRandomAccess(
355 Field& field,
356 const TMsg& msg,
357 TIter& iter,
358 std::size_t size,
359 TWriter&& nextLayerWriter) const
360 {
361 auto valueIter = iter;
362 auto& thisObj = BaseImpl::thisLayer();
363 thisObj.prepareFieldForWrite(0U, &msg, field);
364 auto es = thisObj.doWriteField(&msg, field, iter, size);
365 if (es != ErrorStatus::Success) {
366 return es;
367 }
368
369 auto dataIter = iter;
370
371 auto sizeLen = field.length();
372 es = nextLayerWriter.write(msg, iter, size - sizeLen);
373 if (es != ErrorStatus::Success) {
374 return es;
375 }
376
377 auto dist = static_cast<std::size_t>(std::distance(dataIter, iter));
378 thisObj.prepareFieldForWrite(dist, &msg, field);
379 COMMS_ASSERT(field.length() == sizeLen);
380 return thisObj.doWriteField(&msg, field, valueIter, sizeLen);
381 }
382
383 template <typename TMsg, typename TIter, typename TWriter>
384 ErrorStatus writeInternalOutput(
385 Field& field,
386 const TMsg& msg,
387 TIter& iter,
388 std::size_t size,
389 TWriter&& nextLayerWriter) const
390 {
391 auto& thisObj = BaseImpl::thisLayer();
392 thisObj.prepareFieldForWrite(0U, &msg, field);
393 auto es = thisObj.doWriteField(&msg, field, iter, size);
394 if (es != ErrorStatus::Success) {
395 return es;
396 }
397
398 es = nextLayerWriter.write(msg, iter, size - field.length());
399 if ((es != ErrorStatus::Success) &&
400 (es != ErrorStatus::UpdateRequired)) {
401 return es;
402 }
403
404 return ErrorStatus::UpdateRequired;
405 }
406
407 template <typename TMsg, typename TIter, typename TWriter>
408 ErrorStatus writeInternalNoLengthTagged(
409 Field& field,
410 const TMsg& msg,
411 TIter& iter,
412 std::size_t size,
413 TWriter&& nextLayerWriter,
414 std::random_access_iterator_tag) const
415 {
416 return writeInternalRandomAccess(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
417 }
418
419 template <typename TMsg, typename TIter, typename TWriter>
420 ErrorStatus writeInternalNoLengthTagged(
421 Field& field,
422 const TMsg& msg,
423 TIter& iter,
424 std::size_t size,
425 TWriter&& nextLayerWriter,
426 std::output_iterator_tag) const
427 {
428 return writeInternalOutput(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
429 }
430
431 template <typename TMsg, typename TIter, typename TWriter>
432 ErrorStatus writeInternalNoLength(
433 Field& field,
434 const TMsg& msg,
435 TIter& iter,
436 std::size_t size,
437 TWriter&& nextLayerWriter) const
438 {
439 static_assert(
440 (BaseImpl::MinFieldLength == BaseImpl::MaxFieldLength) ||
441 (comms::isMessageBase<typename std::decay<decltype(msg)>::type>()),
442 "Unable to perform write with size field having variable length and "
443 "no polymorphic length calculation available.");
444 using IterType = typename std::decay<decltype(iter)>::type;
445 using Tag = typename std::iterator_traits<IterType>::iterator_category;
446 return writeInternalNoLengthTagged(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter), Tag());
447 }
448
449 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
450 ErrorStatus writeInternal(
451 Field& field,
452 const TMsg& msg,
453 TIter& iter,
454 std::size_t size,
455 TWriter&& nextLayerWriter,
456 MsgHasLengthTag<TParams...>) const
457 {
458 return writeInternalHasLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
459 }
460
461 template <typename TMsg, typename TIter, typename TWriter, typename... TParams>
462 ErrorStatus writeInternal(
463 Field& field,
464 const TMsg& msg,
465 TIter& iter,
466 std::size_t size,
467 TWriter&& nextLayerWriter,
468 MsgNoLengthTag<TParams...>) const
469 {
470 return writeInternalNoLength(field, msg, iter, size, std::forward<TWriter>(nextLayerWriter));
471 }
472
473 template <typename TMsg, typename... TParams>
474 constexpr std::size_t fieldLengthInternal(const TMsg& msg, FixedLengthTag<TParams...>) const
475 {
476 return BaseImpl::doFieldLength(msg);
477 }
478
479 template <typename TMsg, typename... TParams>
480 std::size_t fieldLengthInternal(const TMsg& msg, VarLengthTag<TParams...>) const
481 {
482 auto& thisObj = BaseImpl::thisLayer();
483 auto remSize = BaseImpl::nextLayer().length(msg);
484 Field fieldTmp;
485 thisObj.prepareFieldForWrite(remSize, &msg, fieldTmp);
486 return fieldTmp.length();
487 }
488
489 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename TForwardTag>
490 comms::ErrorStatus doUpdateInternal(
491 const TMsg* msg,
492 Field& field,
493 TIter& iter,
494 std::size_t size,
495 TNextLayerUpdater&& nextLayerUpdater,
496 TForwardTag&& tag) const
497 {
498 std::size_t lenValue = size - Field::maxLength();
499 auto& thisObj = BaseImpl::thisLayer();
500 thisObj.prepareFieldForWrite(lenValue, msg, field);
501
502 if (field.length() != Field::maxLength()) {
503 lenValue = size - field.length();
504 thisObj.prepareFieldForWrite(lenValue, msg, field);
505 }
506
507 auto es = thisObj.doWriteField(msg, field, iter, size);
508 if (es != ErrorStatus::Success) {
509 return es;
510 }
511
512 return doUpdateForward(msg, iter, size - field.length(), std::forward<TNextLayerUpdater>(nextLayerUpdater), tag);
513 }
514
515 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
516 comms::ErrorStatus doUpdateForward(
517 const TMsg* msg,
518 TIter& iter,
519 std::size_t size,
520 TNextLayerUpdater&& nextLayerUpdater,
521 NoMsgTypeTag<TParams...>) const
522 {
523 static_cast<void>(msg);
524 return nextLayerUpdater.update(iter, size);
525 }
526
527 template <typename TMsg, typename TIter, typename TNextLayerUpdater, typename... TParams>
528 comms::ErrorStatus doUpdateForward(
529 const TMsg* msg,
530 TIter& iter,
531 std::size_t size,
532 TNextLayerUpdater&& nextLayerUpdater,
533 ValidMsgTypeTag<TParams...>) const
534 {
535 COMMS_ASSERT(msg != nullptr);
536 return nextLayerUpdater.update(*msg, iter, size);
537 }
538};
539
540namespace details
541{
542template <typename T>
543struct MsgSizeLayerCheckHelper
544{
545 static const bool Value = false;
546};
547
548template <typename TField, typename TNextLayer>
549struct MsgSizeLayerCheckHelper<MsgSizeLayer<TField, TNextLayer> >
550{
551 static const bool Value = true;
552};
553
554} // namespace details
555
559template <typename T>
560constexpr bool isMsgSizeLayer()
561{
562 return details::MsgSizeLayerCheckHelper<T>::Value;
563}
564
565} // namespace frame
566
567} // namespace comms
568
569COMMS_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.
Provides common base class for the custom messages with default implementation.
Base class to all the field classes.
Definition Field.h:33
Frame layer that uses size field as a prefix to all the subsequent data written by other (next) layer...
Definition MsgSizeLayer.h:50
~MsgSizeLayer() noexcept=default
Destructor.
constexpr bool isMsgSizeLayer()
Compile time check of whether the provided type is a variant of MsgSizeLayer.
Definition MsgSizeLayer.h:560
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:206
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:252
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:56
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:129
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:280
static void prepareFieldForWrite(std::size_t size, const TMsg *msg, Field &field)
Prepare field for writing.
Definition MsgSizeLayer.h:295
static std::size_t getRemainingSizeFromField(const Field &field)
Retrieve remaining size (length) from the field.
Definition MsgSizeLayer.h:266
comms::ErrorStatus doUpdate(Field &field, TIter &iter, std::size_t size, TNextLayerUpdater &&nextLayerUpdater) const
Customized update functionality, invoked by update().
Definition MsgSizeLayer.h:227
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:905
Replacement to some types from standard type_traits.