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 - 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/frame/details/SyncPrefixLayerBase.h"
15#include "comms/frame/details/SyncPrefixLayerOptionsParser.h"
16
17#include <algorithm>
18#include <iterator>
19
20COMMS_MSVC_WARNING_PUSH
21COMMS_MSVC_WARNING_DISABLE(4189) // Disable erroneous initialized but not referenced variable warning
22
23namespace comms
24{
25
26namespace frame
27{
28
51template <typename TField, typename TNextLayer, typename... TOptions>
52class SyncPrefixLayer : public comms::frame::details::SyncPrefixLayerBase<TField, TNextLayer, TOptions...>
53{
54 using BaseImpl = comms::frame::details::SyncPrefixLayerBase<TField, TNextLayer, TOptions...>;
55 using ParsedOptionsInternal = comms::frame::details::SyncPrefixLayerOptionsParser<TOptions...>;
56
57public:
59 using Field = typename BaseImpl::Field;
60
63 using EscField = typename ParsedOptionsInternal::EscField;
64
66 SyncPrefixLayer() = default;
67
70
73
75 ~SyncPrefixLayer() noexcept = default;
76
79 static constexpr bool hasExtendingClass()
80 {
81 return ParsedOptionsInternal::HasExtendingClass;
82 }
83
112 template <typename TMsg, typename TIter, typename TNextLayerReader, typename... TExtraValues>
114 Field& field,
115 TMsg& msg,
116 TIter& iter,
117 std::size_t size,
118 TNextLayerReader&& nextLayerReader,
119 TExtraValues... extraValues)
120 {
121 using SeekTag =
122 typename comms::util::LazyShallowConditional<
123 ParsedOptionsInternal::HasSeekField
124 >::template Type<
125 SeekFieldTag,
126 InstantOpTag
127 >;
128
129 return
130 readInternal(
131 field,
132 msg,
133 iter,
134 size,
135 std::forward<TNextLayerReader>(nextLayerReader),
136 SeekTag(),
137 extraValues...);
138 }
139
157 template <typename TMsg, typename TIter, typename TNextLayerWriter>
159 Field& field,
160 const TMsg& msg,
161 TIter& iter,
162 std::size_t size,
163 TNextLayerWriter&& nextLayerWriter) const
164 {
165 auto& thisObj = BaseImpl::thisLayer();
166 thisObj.prepareFieldForWrite(field);
167 auto es = thisObj.doWriteField(&msg, field, iter, size);
168 if (es != ErrorStatus::Success) {
169 return es;
170 }
171
172 COMMS_ASSERT(field.length() <= size);
173 return nextLayerWriter.write(msg, iter, size - field.length());
174 }
175
176protected:
183 static bool verifyFieldValue(const Field& field)
184 {
185 return field == Field();
186 }
187
194 template <typename TEscField>
195 static bool verifyEscFieldValue(const TEscField& field)
196 {
197 return field == TEscField();
198 }
199
207 static void prepareFieldForWrite(Field& field)
208 {
209 static_cast<void>(field);
210 }
211
212private:
213 template <typename... TParams>
214 using SeekFieldTag = comms::details::tag::Tag1<>;
215
216 template <typename... TParams>
217 using InstantOpTag = comms::details::tag::Tag2<>;
218
219 template <typename... TParams>
220 using EscapeSupportedTag = comms::details::tag::Tag3<>;
221
222 template <typename... TParams>
223 using NoEscapeTag = comms::details::tag::Tag4<>;
224
225 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
226 ErrorStatus readInternal(
227 Field& field,
228 TMsg& msg,
229 TIter& iter,
230 std::size_t size,
231 TReader&& nextLayerReader,
232 SeekFieldTag<>,
233 TExtraValues... extraValues)
234 {
235 using IterType = typename std::decay<decltype(iter)>::type;
236 static_assert(std::is_same<typename std::iterator_traits<IterType>::iterator_category, std::random_access_iterator_tag>::value,
237 "The read operation is expected to use random access iterator");
238
239 auto& thisObj = BaseImpl::thisLayer();
240 auto* msgPtr = BaseImpl::toMsgPtr(msg);
241
242 auto fromIter = iter;
243 std::size_t consumed = 0U;
244 while (consumed < size) {
245 auto iterTmp = iter;
246 auto remSize = size - consumed;
247
248 auto fieldEs = thisObj.doReadField(msgPtr, field, iterTmp, remSize);
249 if (fieldEs == ErrorStatus::NotEnoughData) {
250 BaseImpl::updateMissingSize(field, remSize, extraValues...);
251 BaseImpl::resetMsg(msg);
252 return fieldEs;
253 }
254
255 if ((fieldEs == ErrorStatus::Success) &&
256 (!fieldEscapedInternal(fromIter, iter)) &&
257 (thisObj.verifyFieldValue(field))) {
258 // Set iter to point after field
259 iter = iterTmp;
260 break;
261 }
262
263 ++iter;
264 ++consumed;
265 }
266
267 if (size <= consumed) {
268 // Field hasn't been recognized
269 return ErrorStatus::NotEnoughData;
270 }
271
272 auto remSize = size - static_cast<std::size_t>(std::distance(fromIter, iter));
273 return nextLayerReader.read(msg, iter, remSize, extraValues...);
274 }
275
276 template <typename TMsg, typename TIter, typename TReader, typename... TExtraValues>
277 ErrorStatus readInternal(
278 Field& field,
279 TMsg& msg,
280 TIter& iter,
281 std::size_t size,
282 TReader&& nextLayerReader,
283 InstantOpTag<>,
284 TExtraValues... extraValues)
285 {
286 auto& thisObj = BaseImpl::thisLayer();
287 auto* msgPtr = BaseImpl::toMsgPtr(msg);
288 auto beforeReadIter = iter;
289
290 auto es = thisObj.doReadField(msgPtr, field, iter, size);
292 BaseImpl::updateMissingSize(field, size, extraValues...);
293 }
294
295 if (es != comms::ErrorStatus::Success) {
296 return es;
297 }
298
299 bool verified = thisObj.verifyFieldValue(field);
300 if (!verified) {
302 }
303
304 auto fieldLen = static_cast<std::size_t>(std::distance(beforeReadIter, iter));
305 return nextLayerReader.read(msg, iter, size - fieldLen, extraValues...);
306 }
307
308 template <typename TIter>
309 bool fieldEscapedInternal(TIter from, TIter to, NoEscapeTag<>)
310 {
311 static_cast<void>(from);
312 static_cast<void>(to);
313 return false;
314 }
315
316 template <typename TIter>
317 bool fieldEscapedInternal(TIter from, TIter to, EscapeSupportedTag<>)
318 {
319 unsigned escCount = 0U;
320 auto& thisObj = BaseImpl::thisLayer();
321
322 while (from != to) {
323 auto dist = static_cast<std::size_t>(std::distance(from, to));
324 dist = std::min(dist, EscField::maxLength());
325 if (dist < EscField::minLength()) {
326 break;
327 }
328
329 auto maxLenIter = to;
330 std::advance(maxLenIter, -static_cast<int>(dist));
331
332 auto iter = to;
333 std::advance(iter, -static_cast<int>(EscField::minLength()));
334 auto prevCount = escCount;
335 while (true) {
336 auto iterTmp = iter;
337 EscField escField;
338 auto len = static_cast<std::size_t>(std::distance(iterTmp, to));
339 auto es = escField.read(iterTmp, len);
340 if ((es == comms::ErrorStatus::Success) &&
341 (iterTmp == to) &&
342 (thisObj.verifyEscFieldValue(escField))) {
343 ++escCount;
344 break;
345 }
346
347 if (iter == maxLenIter) {
348 break;
349 }
350
351 std::advance(iter, -1);
352 }
353
354 if (prevCount == escCount) {
355 // Failed to detect escField
356 break;
357 }
358
359 // esc field was detected, trying again
360 to = iter;
361 }
362
363 // Escaped only if odd number of esc field is detected
364 return (escCount & 0x1) != 0U;
365 }
366
367 template <typename TIter>
368 bool fieldEscapedInternal(TIter from, TIter to)
369 {
370 using EscTag =
371 typename comms::util::LazyShallowConditional<
372 std::is_same<EscField, void>::value
373 >::template Type<
374 NoEscapeTag,
375 EscapeSupportedTag
376 >;
377
378 return fieldEscapedInternal(from, to, EscTag());
379 }
380};
381
382namespace details
383{
384template <typename T>
385struct SyncPrefixLayerCheckHelper
386{
387 static const bool Value = false;
388};
389
390template <typename TField, typename TNextLayer>
391struct SyncPrefixLayerCheckHelper<SyncPrefixLayer<TField, TNextLayer> >
392{
393 static const bool Value = true;
394};
395
396} // namespace details
397
401template <typename T>
402constexpr bool isSyncPrefixLayer()
403{
404 return details::SyncPrefixLayerCheckHelper<T>::Value;
405}
406
407} // namespace frame
408
409} // namespace comms
410
411COMMS_MSVC_WARNING_POP
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:168
Contains various compiler related definitions.
Base class to all the field classes.
Definition Field.h:33
Frame layer that uses "sync" field as a prefix to all the subsequent data written by other (next) lay...
Definition SyncPrefixLayer.h:53
static bool verifyFieldValue(const Field &field)
Verify the validity of the field.
Definition SyncPrefixLayer.h:183
static bool verifyEscFieldValue(const TEscField &field)
Verify the validity of the escapte field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncPrefixLayer.h:195
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:113
SyncPrefixLayer(SyncPrefixLayer &&)=default
Move constructor.
static void prepareFieldForWrite(Field &field)
Prepare field for writing.
Definition SyncPrefixLayer.h:207
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:158
~SyncPrefixLayer() noexcept=default
Destructor.
typename ParsedOptionsInternal::EscField EscField
Type of the escape field provided via comms::option::def::FrameLayerSeekField option.
Definition SyncPrefixLayer.h:63
constexpr bool isSyncPrefixLayer()
Compile time check of whether the provided type is a variant of SyncPrefixLayer.
Definition SyncPrefixLayer.h:402
SyncPrefixLayer()=default
Default constructor.
typename BaseImpl::Field Field
Type of the field object used to read/write "sync" value.
Definition SyncPrefixLayer.h:59
comms::frame::SyncPrefixLayer< TField, TNextLayer, TOptions... > SyncPrefixLayer
Alias to the comms::frame::SyncPrefixLayer.
Definition SyncPrefixLayer.h:25
Main namespace for all classes / functions of COMMS library.
ErrorStatus
Error statuses reported by the Communication module.
Definition ErrorStatus.h:17
@ Success
Used to indicate successful outcome of the operation.