COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
SyncPrefixLayer.h
Go to the documentation of this file.
1//
2// Copyright 2015 - 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/frame/details/SyncPrefixLayerBase.h"
17#include "comms/frame/details/SyncPrefixLayerOptionsParser.h"
18
19#include <algorithm>
20#include <cstddef>
21#include <iterator>
22#include <type_traits>
23#include <utility>
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
56template <typename TField, typename TNextLayer, typename... TOptions>
57class SyncPrefixLayer : public comms::frame::details::SyncPrefixLayerBase<TField, TNextLayer, TOptions...>
58{
59 using BaseImpl = comms::frame::details::SyncPrefixLayerBase<TField, TNextLayer, TOptions...>;
60 using ParsedOptionsInternal = comms::frame::details::SyncPrefixLayerOptionsParser<TOptions...>;
61
62public:
64 using Field = typename BaseImpl::Field;
65
68 using EscField = typename ParsedOptionsInternal::EscField;
69
71 SyncPrefixLayer() = default;
72
75
78
80 ~SyncPrefixLayer() noexcept = default;
81
84 static constexpr bool hasExtendingClass()
85 {
86 return ParsedOptionsInternal::HasExtendingClass;
87 }
88
117 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
119 Field& field,
120 TMsg& msg,
121 TIter& iter,
122 std::size_t size,
123 TNextLayerReader&& nextLayerReader,
124 TExtraValues... extraValues)
125 {
126 using SeekTag =
127 typename comms::util::LazyShallowConditional<
128 ParsedOptionsInternal::HasSeekField
129 >::template Type<
130 SeekFieldTag,
131 InstantOpTag
132 >;
133
134 return
135 readInternal(
136 field,
137 msg,
138 iter,
139 size,
140 std::forward<TNextLayerReader>(nextLayerReader),
141 SeekTag(),
142 extraValues...);
143 }
144
162 template <typename TMsg, typename TIter, typename TNextLayerWriter>
164 Field& field,
165 const TMsg& msg,
166 TIter& iter,
167 std::size_t size,
168 TNextLayerWriter&& nextLayerWriter) const
169 {
170 auto& thisObj = BaseImpl::thisLayer();
171 thisObj.prepareFieldForWrite(field);
172 auto es = thisObj.doWriteField(&msg, field, iter, size);
173 if (es != ErrorStatus::Success) {
174 return es;
175 }
176
177 COMMS_ASSERT(field.length() <= size);
178 return nextLayerWriter.write(msg, iter, size - field.length());
179 }
180
181protected:
188 static bool verifyFieldValue(const Field& field)
189 {
190 return field == Field();
191 }
192
199 template <typename TEscField>
200 static bool verifyEscFieldValue(const TEscField& field)
201 {
202 return field == TEscField();
203 }
204
212 static void prepareFieldForWrite(Field& field)
213 {
214 static_cast<void>(field);
215 }
216
217private:
218 template <typename... TParams>
219 using SeekFieldTag = comms::details::tag::Tag1<>;
220
221 template <typename... TParams>
222 using InstantOpTag = comms::details::tag::Tag2<>;
223
224 template <typename... TParams>
225 using EscapeSupportedTag = comms::details::tag::Tag3<>;
226
227 template <typename... TParams>
228 using NoEscapeTag = comms::details::tag::Tag4<>;
229
230 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
231 ErrorStatus readInternal(
232 Field& field,
233 TMsg& msg,
234 TIter& iter,
235 std::size_t size,
236 TReader&& nextLayerReader,
237 SeekFieldTag<>,
238 TExtraValues... extraValues)
239 {
240 using IterType = typename std::decay<decltype(iter)>::type;
241 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
242 "The read operation is expected to use random access iterator");
243
244 auto& thisObj = BaseImpl::thisLayer();
245 auto* msgPtr = BaseImpl::toMsgPtr(msg);
246
247 auto fromIter = iter;
248 std::size_t consumed = 0U;
249 while (consumed < size) {
250 auto iterTmp = iter;
251 auto remSize = size - consumed;
252
253 auto fieldEs = thisObj.doReadField(msgPtr, field, iterTmp, remSize);
254 if (fieldEs == ErrorStatus::NotEnoughData) {
255 BaseImpl::updateMissingSize(field, remSize, extraValues...);
256 BaseImpl::resetMsg(msg);
257 return fieldEs;
258 }
259
260 if ((fieldEs == ErrorStatus::Success) &&
261 (!fieldEscapedInternal(fromIter, iter)) &&
262 (thisObj.verifyFieldValue(field))) {
263 // Set iter to point after field
264 iter = iterTmp;
265 break;
266 }
267
268 ++iter;
269 ++consumed;
270 }
271
272 if (size <= consumed) {
273 // Field hasn't been recognized
274 return ErrorStatus::NotEnoughData;
275 }
276
277 auto remSize = size - static_cast<std::size_t>(std::distance(fromIter, iter));
278 return nextLayerReader.read(msg, iter, remSize, extraValues...);
279 }
280
281 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
282 ErrorStatus readInternal(
283 Field& field,
284 TMsg& msg,
285 TIter& iter,
286 std::size_t size,
287 TReader&& nextLayerReader,
288 InstantOpTag<>,
289 TExtraValues... extraValues)
290 {
291 auto& thisObj = BaseImpl::thisLayer();
292 auto* msgPtr = BaseImpl::toMsgPtr(msg);
293 auto beforeReadIter = iter;
294
295 auto es = thisObj.doReadField(msgPtr, field, iter, size);
297 BaseImpl::updateMissingSize(field, size, extraValues...);
298 }
299
300 if (es != comms::ErrorStatus::Success) {
301 return es;
302 }
303
304 bool verified = thisObj.verifyFieldValue(field);
305 if (!verified) {
307 }
308
309 auto fieldLen = static_cast<std::size_t>(std::distance(beforeReadIter, iter));
310 return nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
311 }
312
313 template <typename TIter>
314 bool fieldEscapedInternal(TIter from, TIter to, NoEscapeTag<>)
315 {
316 static_cast<void>(from);
317 static_cast<void>(to);
318 return false;
319 }
320
321 template <typename TIter>
322 bool fieldEscapedInternal(TIter from, TIter to, EscapeSupportedTag<>)
323 {
324 unsigned escCount = 0U;
325 auto& thisObj = BaseImpl::thisLayer();
326
327 while (from != to) {
328 auto dist = static_cast<std::size_t>(std::distance(from, to));
329 dist = std::min(dist, EscField::maxLength());
330 if (dist < EscField::minLength()) {
331 break;
332 }
333
334 auto maxLenIter = to;
335 std::advance(maxLenIter, -static_cast<int>(dist));
336
337 auto iter = to;
338 std::advance(iter, -static_cast<int>(EscField::minLength()));
339 auto prevCount = escCount;
340 while (true) {
341 auto iterTmp = iter;
342 EscField escField;
343 auto len = static_cast<std::size_t>(std::distance(iterTmp, to));
344 auto es = escField.read(iterTmp, len);
345 if ((es == comms::ErrorStatus::Success) &&
346 (iterTmp == to) &&
347 (thisObj.verifyEscFieldValue(escField))) {
348 ++escCount;
349 break;
350 }
351
352 if (iter == maxLenIter) {
353 break;
354 }
355
356 std::advance(iter, -1);
357 }
358
359 if (prevCount == escCount) {
360 // Failed to detect escField
361 break;
362 }
363
364 // esc field was detected, trying again
365 to = iter;
366 }
367
368 // Escaped only if odd number of esc field is detected
369 return (escCount & 0x1) != 0U;
370 }
371
372 template <typename TIter>
373 bool fieldEscapedInternal(TIter from, TIter to)
374 {
375 using EscTag =
376 typename comms::util::LazyShallowConditional<
377 std::is_same<EscField, void>::value
378 >::template Type<
379 NoEscapeTag,
380 EscapeSupportedTag
381 >;
382
383 return fieldEscapedInternal(from, to, EscTag());
384 }
385};
386
387namespace details
388{
389template <typename T>
390struct SyncPrefixLayerCheckHelper
391{
392 static const bool Value = false;
393};
394
395template <typename TField, typename TNextLayer>
396struct SyncPrefixLayerCheckHelper<SyncPrefixLayer<TField, TNextLayer> >
397{
398 static const bool Value = true;
399};
400
401} // namespace details
402
406template <typename T>
407constexpr bool isSyncPrefixLayer()
408{
409 return details::SyncPrefixLayerCheckHelper<T>::Value;
410}
411
412} // namespace frame
413
414} // namespace comms
415
416COMMS_MSVC_WARNING_POP
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
Contains various compiler related definitions.
Base class to all the field classes.
Definition Field.h:36
Frame layer that uses "sync" field as a prefix to all the subsequent data written by other (next) lay...
Definition SyncPrefixLayer.h:58
static bool verifyFieldValue(const Field &field)
Verify the validity of the field.
Definition SyncPrefixLayer.h:188
static bool verifyEscFieldValue(const TEscField &field)
Verify the validity of the escapte field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncPrefixLayer.h:200
comms::ErrorStatus doRead(Field &field, TMsg &msg, TIter &iter, std::size_t size, TNextLayerReader &&nextLayerReader, TExtraValues... extraValues)
Customized read functionality, invoked by read().
Definition SyncPrefixLayer.h:118
SyncPrefixLayer(SyncPrefixLayer &&)=default
Move constructor.
static void prepareFieldForWrite(Field &field)
Prepare field for writing.
Definition SyncPrefixLayer.h:212
SyncPrefixLayer(const SyncPrefixLayer &)=default
Copy constructor.
comms::ErrorStatus doWrite(Field &field, const TMsg &msg, TIter &iter, std::size_t size, TNextLayerWriter &&nextLayerWriter) const
Customized write functionality, invoked by write().
Definition SyncPrefixLayer.h:163
~SyncPrefixLayer() noexcept=default
Destructor.
typename ParsedOptionsInternal::EscField EscField
Type of the escape field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncPrefixLayer.h:68
constexpr bool isSyncPrefixLayer()
Compile time check of whether the provided type is a variant of SyncPrefixLayer.
Definition SyncPrefixLayer.h:407
SyncPrefixLayer()=default
Default constructor.
typename BaseImpl::Field Field
Type of the field object used to read/write "sync" value.
Definition SyncPrefixLayer.h:64
comms::frame::SyncPrefixLayer< TField, TNextLayer, TOptions... > SyncPrefixLayer
Alias to the comms::frame::SyncPrefixLayer.
Definition SyncPrefixLayer.h:27
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:19
@ Success
Used to indicate successful outcome of the operation.