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