COMMS
Template library intended to help with implementation of communication protocols.
Loading...
Searching...
No Matches
RemLengthMemberField.h
1//
2// Copyright 2019 - 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
10#pragma once
11
12#include "comms/ErrorStatus.h"
13#include "comms/field/tag.h"
15#include "comms/details/tag.h"
16
17#include <algorithm>
18#include <cstddef>
19#include <iterator>
20#include <tuple>
21
22COMMS_MSVC_WARNING_PUSH
23COMMS_MSVC_WARNING_DISABLE(4127) // Disable warning about constant conditional expressions
24
25namespace comms
26{
27
28namespace field
29{
30
31namespace adapter
32{
33
34template <std::size_t TLenFieldIdx, typename TBase>
35class RemLengthMemberField : public TBase
36{
37 using BaseImpl = TBase;
38public:
39 using ValueType = typename BaseImpl::ValueType;
40
41 static_assert(TLenFieldIdx < std::tuple_size<ValueType>::value, "Bad index");
42 using LengthFieldType = typename std::tuple_element<TLenFieldIdx, ValueType>::type;
43 using VersionType = typename BaseImpl::VersionType;
44
46 {
47 refreshLengthInternal();
48 }
49
50 static constexpr std::size_t maxLength()
51 {
52 return MaxPossibleLen;
53 }
54
55 template <std::size_t TFromIdx>
56 static constexpr std::size_t maxLengthFrom()
57 {
58 using Tag =
59 typename comms::util::LazyShallowConditional<
60 TLenFieldIdx < TFromIdx
61 >::template Type<
62 BaseRedirectTag,
63 LocalTag
64 >;
65 return maxLengthFromInternal<TFromIdx>(Tag());
66 }
67
68 template <std::size_t TUntilIdx>
69 static constexpr std::size_t maxLengthUntil()
70 {
71 using Tag =
72 typename comms::util::LazyShallowConditional<
73 TUntilIdx <= TLenFieldIdx
74 >::template Type<
75 BaseRedirectTag,
76 LocalTag
77 >;
78
79 return maxLengthUntilInternal<TUntilIdx>(Tag());
80 }
81
82 template <std::size_t TFromIdx, std::size_t TUntilIdx>
83 static constexpr std::size_t maxLengthFromUntil()
84 {
85 using Tag =
86 typename comms::util::LazyShallowConditional<
87 (TUntilIdx <= TLenFieldIdx) || (TLenFieldIdx < TFromIdx)
88 >::template Type<
89 BaseRedirectTag,
90 LocalTag
91 >;
92
93 return maxLengthFromUntilInternal<TFromIdx, TUntilIdx>(Tag());
94 }
95
96 bool refresh()
97 {
98 bool updated = BaseImpl::refresh();
99 return refreshLengthInternal() || updated;
100 }
101
102 template <typename TIter>
103 ErrorStatus read(TIter& iter, std::size_t len)
104 {
105 return readFromUntilAndUpdateLen<0, std::tuple_size<ValueType>::value>(iter, len);
106 }
107
108 template <std::size_t TFromIdx, typename TIter>
109 ErrorStatus readFrom(TIter& iter, std::size_t len)
110 {
111 return readFromUntilAndUpdateLen<TFromIdx, std::tuple_size<ValueType>::value>(iter, len);
112 }
113
114 template <std::size_t TFromIdx, typename TIter>
115 ErrorStatus readFromAndUpdateLen(TIter& iter, std::size_t& len)
116 {
117 return readFromAndUpdateLen<TFromIdx, std::tuple_size<ValueType>::value>(iter, len);
118 }
119
120 template <std::size_t TUntilIdx, typename TIter>
121 ErrorStatus readUntil(TIter& iter, std::size_t len)
122 {
123 return readFromUntilAndUpdateLen<0U, TUntilIdx>(iter, len);
124 }
125
126 template <std::size_t TUntilIdx, typename TIter>
127 ErrorStatus readUntilAndUpdateLen(TIter& iter, std::size_t& len)
128 {
129 return readFromUntilAndUpdateLen<0, TUntilIdx>(iter, len);
130 }
131
132 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename TIter>
133 ErrorStatus readFromUntil(TIter& iter, std::size_t len)
134 {
135 return readFromUntilAndUpdateLen<TFromIdx, TUntilIdx>(iter, len);
136 }
137
138 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename TIter>
139 ErrorStatus readFromUntilAndUpdateLen(TIter& iter, std::size_t& len)
140 {
141 using Tag =
142 typename comms::util::LazyShallowConditional<
143 (TUntilIdx <= TLenFieldIdx)
144 >::template Type<
145 BaseRedirectTag,
146 LocalTag
147 >;
148 return readFromUntilInternal<TFromIdx, TUntilIdx>(iter, len, Tag());
149 }
150
151 template <typename TIter>
152 void readNoStatus(TIter& iter) = delete;
153
154 template <std::size_t TFromIdx, typename TIter>
155 void readFromNoStatus(TIter& iter) = delete;
156
157 template <std::size_t TUntilIdx, typename TIter>
158 void readUntilNoStatus(TIter& iter) = delete;
159
160 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename TIter>
161 void readFromUntilNoStatus(TIter& iter) = delete;
162
163 static constexpr bool hasNonDefaultRefresh()
164 {
165 return true;
166 }
167
168 bool setVersion(VersionType version)
169 {
170 bool updated = BaseImpl::setVersion(version);
171 return refreshLengthInternal() || updated;
172 }
173
174 bool canWrite() const
175 {
176 if (!BaseImpl::canWrite()) {
177 return false;
178 }
179
180 std::size_t expLen = BaseImpl::template lengthFrom<TLenFieldIdx + 1>();
181 if (static_cast<std::size_t>(LengthFieldType::maxValue()) < expLen) {
182 return false;
183 }
184
185 LengthFieldType lenField;
186 lenField.setValue(expLen);
187 return lenField.canWrite();
188 }
189
190 template <typename TIter>
191 comms::ErrorStatus write(TIter& iter, std::size_t len) const
192 {
193 if (!canWrite()) {
195 }
196
197 return BaseImpl::write(iter, len);
198 }
199
200 static constexpr bool hasWriteNoStatus()
201 {
202 return false;
203 }
204
205 template <typename TIter>
206 comms::ErrorStatus writeNoStatus(TIter& iter, std::size_t len) const = delete;
207
208 bool valid() const
209 {
210 return BaseImpl::valid() && canWrite();
211 }
212
213private:
214 template <typename... TParams>
215 using BaseRedirectTag = comms::details::tag::Tag1<>;
216
217 template <typename... TParams>
218 using LocalTag = comms::details::tag::Tag2<>;
219
220 template <typename... TParams>
221 using PerformOpTag = comms::details::tag::Tag3<>;
222
223 template <typename... TParams>
224 using SkipOpTag = comms::details::tag::Tag4<>;
225
226 template <std::size_t TFromIdx, typename... TParams>
227 static constexpr std::size_t maxLengthFromInternal(BaseRedirectTag<TParams...>)
228 {
229 return BaseImpl::template maxLengthFrom<TFromIdx>();
230 }
231
232 template <std::size_t TFromIdx, typename... TParams>
233 static constexpr std::size_t maxLengthFromInternal(LocalTag<TParams...>)
234 {
235 return MaxPossibleLen;
236 }
237
238 template <std::size_t TUntilIdx, typename... TParams>
239 static constexpr std::size_t maxLengthUntilInternal(BaseRedirectTag<TParams...>)
240 {
241 return BaseImpl::template maxLengthUntil<TUntilIdx>();
242 }
243
244 template <std::size_t TUntilIdx, typename... TParams>
245 static constexpr std::size_t maxLengthUntilInternal(LocalTag<TParams...>)
246 {
247 return MaxPossibleLen;
248 }
249
250 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename... TParams>
251 static constexpr std::size_t maxLengthFromUntilInternal(BaseRedirectTag<TParams...>)
252 {
253 return BaseImpl::template maxLengthFromUntil<TFromIdx, TUntilIdx>();
254 }
255
256 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename... TParams>
257 static constexpr std::size_t maxLengthFromUntilInternal(LocalTag<TParams...>)
258 {
259 return MaxPossibleLen;
260 }
261
262 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename TIter, typename... TParams>
263 ErrorStatus readFromUntilInternal(TIter& iter, std::size_t& len, BaseRedirectTag<TParams...>)
264 {
265 return BaseImpl::template readFromUntilAndUpdateLen<TFromIdx, TUntilIdx>(iter, len);
266 }
267
268 template <std::size_t TFromIdx, typename TIter, typename... TParams>
269 ErrorStatus readEarlierFieldsInternal(TIter& iter, std::size_t& len, PerformOpTag<TParams...>)
270 {
271 return BaseImpl::template readFromUntilAndUpdateLen<TFromIdx, TLenFieldIdx>(iter, len);
272 }
273
274 template <std::size_t TFromIdx, typename TIter, typename... TParams>
275 ErrorStatus readEarlierFieldsInternal(TIter& iter, std::size_t& len, SkipOpTag<TParams...>)
276 {
277 static_cast<void>(iter);
278 static_cast<void>(len);
279 return ErrorStatus::Success;
280 }
281
282 template <typename TIter, typename... TParams>
283 ErrorStatus readRemLengthFieldInternal(TIter& iter, std::size_t& len, std::size_t& remLen, PerformOpTag<TParams...>)
284 {
285 auto& mems = BaseImpl::value();
286 auto& lenField = std::get<TLenFieldIdx>(mems);
287
288 auto beforeLenReadIter = iter;
289 auto es = lenField.read(iter, len);
290 if (es != comms::ErrorStatus::Success) {
291 return es;
292 }
293
294 auto lenFieldLen = static_cast<std::size_t>(std::distance(beforeLenReadIter, iter));
295 COMMS_ASSERT(lenFieldLen <= len);
296 len -= lenFieldLen;
297
298 remLen = static_cast<std::size_t>(lenField.getValue());
299 return ErrorStatus::Success;
300 }
301
302 template <typename TIter, typename... TParams>
303 ErrorStatus readRemLengthFieldInternal(TIter& iter, std::size_t& len, std::size_t& remLen, SkipOpTag<TParams...>)
304 {
305 static_cast<void>(iter);
306 static_cast<void>(len);
307 auto& mems = BaseImpl::value();
308 auto& lenField = std::get<TLenFieldIdx>(mems);
309 remLen = lenField.value();
310 return ErrorStatus::Success;
311 }
312
313 template <std::size_t TUntilIdx, typename... TParams>
314 void skipUntilFieldInternal(std::size_t& reqLen, PerformOpTag<TParams...>)
315 {
316 static_assert(TLenFieldIdx < TUntilIdx, "Invalid assumption");
317 auto fieldsLen = BaseImpl::template lengthFromUntil<TLenFieldIdx + 1, TUntilIdx>();
318 COMMS_ASSERT(fieldsLen <= reqLen);
319 reqLen -= fieldsLen;
320 }
321
322 template <std::size_t TUntilIdx, typename... TParams>
323 void skipUntilFieldInternal(std::size_t& reqLen, SkipOpTag<TParams...>)
324 {
325 static_cast<void>(reqLen);
326 }
327
328 template <std::size_t TFromIdx, std::size_t TUntilIdx, typename TIter, typename... TParams>
329 ErrorStatus readFromUntilInternal(TIter& iter, std::size_t& len, LocalTag<TParams...>)
330 {
331 static_assert(TLenFieldIdx < TUntilIdx, "Invalid function invocation");
332 using EarlierFieldsTag =
333 typename comms::util::LazyShallowConditional<
334 (TFromIdx < TLenFieldIdx)
335 >::template Type<
336 PerformOpTag,
337 SkipOpTag
338 >;
339
340 auto es = readEarlierFieldsInternal<TFromIdx>(iter, len, EarlierFieldsTag());
341 if (es != comms::ErrorStatus::Success) {
342 return es;
343 }
344
345 using LenTag =
346 typename comms::util::LazyShallowConditional<
347 (TFromIdx <= TLenFieldIdx)
348 >::template Type<
349 PerformOpTag,
350 SkipOpTag
351 >;
352
353 std::size_t reqLen = 0U;
354 es = readRemLengthFieldInternal(iter, len, reqLen, LenTag());
355 if (es != comms::ErrorStatus::Success) {
356 return es;
357 }
358
359 using SkipTag =
360 typename comms::util::LazyShallowConditional<
361 (TLenFieldIdx < TFromIdx)
362 >::template Type<
363 PerformOpTag,
364 SkipOpTag
365 >;
366
367 skipUntilFieldInternal<TFromIdx>(reqLen, SkipTag());
368
369 static const std::size_t NextIdx = (TFromIdx <= TLenFieldIdx) ? TLenFieldIdx + 1 : TFromIdx;
370
371 if ((std::tuple_size<ValueType>::value <= TUntilIdx) &&
372 (len < reqLen)) {
374 }
375
376 auto remLen = std::min(len, reqLen);
377 es = BaseImpl::template readFromUntilAndUpdateLen<NextIdx, TUntilIdx>(iter, remLen);
378 auto consumed = reqLen - remLen;
379 len -= consumed;
380
381 if (es != comms::ErrorStatus::Success) {
382 return es;
383 }
384
385 if (std::tuple_size<ValueType>::value <= TUntilIdx) {
386 len -= remLen;
387 std::advance(iter, remLen);
388 }
389
390 return es;
391 }
392
393 bool refreshLengthInternal()
394 {
395 auto& mems = BaseImpl::value();
396 auto& lenField = std::get<TLenFieldIdx>(mems);
397 std::size_t expLen = BaseImpl::template lengthFrom<TLenFieldIdx + 1>();
398 std::size_t actLen = static_cast<std::size_t>(lenField.getValue());
399 if (expLen == actLen) {
400 return false;
401 }
402
403 lenField.setValue(expLen);
404 return true;
405 }
406
407 static const std::size_t MaxPossibleLen = 0xffff;
408};
409
410} // namespace adapter
411
412} // namespace field
413
414} // namespace comms
415
416COMMS_MSVC_WARNING_POP
417
#define COMMS_ASSERT(expr)
Generic assert macro.
Definition Assert.h:170
This file contain definition of error statuses used by comms module.
Contains definition of various tag classes.
comms::option::def::RemLengthMemberField< TIdx > RemLengthMemberField
Same as comms::option::def::RemLengthMemberField.
Definition options.h:1967
comms::option::def::VersionType< T > VersionType
Same as comms::option::def::VersionType.
Definition options.h:1930
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.
@ InvalidMsgData
Used to indicate that a message has invalid data.
Replacement to some types from standard type_traits.